From ac6ec349d3a2892c9aa54d17ee68ca3493c3c1e2 Mon Sep 17 00:00:00 2001 From: Jeremy Pritts Date: Sun, 16 Oct 2022 14:01:37 -0400 Subject: [PATCH 01/29] Add support for reading LProcRef and ProcRef --- .../Records/CodeViewSymbol.cs | 2 + .../Records/ProcedureReferenceSymbol.cs | 96 +++++++++++++++++++ .../SerializedProcedureReferenceSymbol.cs | 27 ++++++ 3 files changed, 125 insertions(+) create mode 100644 src/AsmResolver.Symbols.Pdb/Records/ProcedureReferenceSymbol.cs create mode 100644 src/AsmResolver.Symbols.Pdb/Records/Serialized/SerializedProcedureReferenceSymbol.cs diff --git a/src/AsmResolver.Symbols.Pdb/Records/CodeViewSymbol.cs b/src/AsmResolver.Symbols.Pdb/Records/CodeViewSymbol.cs index 5869c3acd..21d6191af 100644 --- a/src/AsmResolver.Symbols.Pdb/Records/CodeViewSymbol.cs +++ b/src/AsmResolver.Symbols.Pdb/Records/CodeViewSymbol.cs @@ -34,6 +34,8 @@ public static CodeViewSymbol FromReader(PdbReaderContext context, ref BinaryStre CodeViewSymbolType.Pub32 => new SerializedPublicSymbol(dataReader), CodeViewSymbolType.Udt => new SerializedUserDefinedTypeSymbol(context, dataReader), CodeViewSymbolType.Constant => new SerializedConstantSymbol(context, dataReader), + CodeViewSymbolType.ProcRef => new SerializedProcedureReferenceSymbol(dataReader, false), + CodeViewSymbolType.LProcRef => new SerializedProcedureReferenceSymbol(dataReader, true), _ => new UnknownSymbol(type, dataReader.ReadToEnd()) }; } diff --git a/src/AsmResolver.Symbols.Pdb/Records/ProcedureReferenceSymbol.cs b/src/AsmResolver.Symbols.Pdb/Records/ProcedureReferenceSymbol.cs new file mode 100644 index 000000000..6697275aa --- /dev/null +++ b/src/AsmResolver.Symbols.Pdb/Records/ProcedureReferenceSymbol.cs @@ -0,0 +1,96 @@ +namespace AsmResolver.Symbols.Pdb.Records; + +/// +/// Represents a procedure reference symbol stored in a PDB symbol stream. +/// +public class ProcedureReferenceSymbol : CodeViewSymbol +{ + private readonly LazyVariable _name; + private readonly bool _local; + + /// + /// Initializes a new empty symbol. + /// + /// If true, this represents a local procedure reference. + protected ProcedureReferenceSymbol(bool local) + { + _name = new LazyVariable(GetName); + _local = local; + } + + /// + /// Creates a new symbol. + /// + /// The checksum of the referenced symbol name. + /// The offset within the segment the symbol starts at. + /// Index of the module that contains this procedure record. + /// The name of the symbol. + /// If true, this represents a local procedure reference. + public ProcedureReferenceSymbol(uint checksum, uint offset, ushort module, Utf8String name, bool local) + { + Checksum = checksum; + Offset = offset; + Module = module; + _name = new LazyVariable(name); + _local = local; + } + + /// + public override CodeViewSymbolType CodeViewSymbolType + { + get + { + return _local ? CodeViewSymbolType.LProcRef : CodeViewSymbolType.ProcRef; + } + } + + /// + /// Gets the checksum of the referenced symbol name. The checksum used is the + /// one specified in the header of the global symbols stream or static symbols stream. + /// + public uint Checksum + { + get; + set; + } + + /// + /// Gets the offset of the procedure symbol record from the beginning of the + /// $$SYMBOL table for the module. + /// + public uint Offset + { + get; + set; + } + + /// + /// Index of the module that contains this procedure record. + /// + public ushort Module + { + get; + set; + } + + /// + /// Gets or sets the name of the symbol. + /// + public Utf8String Name + { + get => _name.Value; + set => _name.Value = value; + } + + /// + /// Obtains the name of the symbol. + /// + /// The name. + /// + /// This method is called upon initialization of the property. + /// + protected virtual Utf8String GetName() => Utf8String.Empty; + + /// + public override string ToString() => $"{CodeViewSymbolType}: [{Module:X4}:{Offset:X8}] {Name}"; +} diff --git a/src/AsmResolver.Symbols.Pdb/Records/Serialized/SerializedProcedureReferenceSymbol.cs b/src/AsmResolver.Symbols.Pdb/Records/Serialized/SerializedProcedureReferenceSymbol.cs new file mode 100644 index 000000000..e165013c0 --- /dev/null +++ b/src/AsmResolver.Symbols.Pdb/Records/Serialized/SerializedProcedureReferenceSymbol.cs @@ -0,0 +1,27 @@ +using AsmResolver.IO; + +namespace AsmResolver.Symbols.Pdb.Records.Serialized; + +/// +/// Represents a lazily initialized implementation of that is read from a PDB image. +/// +public class SerializedProcedureReferenceSymbol : ProcedureReferenceSymbol +{ + private readonly BinaryStreamReader _nameReader; + + /// + /// Reads a public symbol from the provided input stream. + /// + /// The input stream to read from. + /// If true, this represents a local procedure reference. + public SerializedProcedureReferenceSymbol(BinaryStreamReader reader, bool local) : base(local) + { + Checksum = reader.ReadUInt32(); + Offset = reader.ReadUInt32(); + Module = reader.ReadUInt16(); + _nameReader = reader; + } + + /// + protected override Utf8String GetName() => _nameReader.Fork().ReadUtf8String(); +} From 0db9b3eab8ace1b032c7a786ac2843f7166d1283 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Oct 2022 14:50:04 +0000 Subject: [PATCH 02/29] Bump coverlet.collector from 3.1.2 to 3.2.0 Bumps [coverlet.collector](https://github.com/coverlet-coverage/coverlet) from 3.1.2 to 3.2.0. - [Release notes](https://github.com/coverlet-coverage/coverlet/releases) - [Commits](https://github.com/coverlet-coverage/coverlet/commits/v3.2.0) --- updated-dependencies: - dependency-name: coverlet.collector dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .../AsmResolver.PE.Win32Resources.Tests.csproj | 2 +- .../AsmResolver.Symbols.Pdb.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/AsmResolver.PE.Win32Resources.Tests/AsmResolver.PE.Win32Resources.Tests.csproj b/test/AsmResolver.PE.Win32Resources.Tests/AsmResolver.PE.Win32Resources.Tests.csproj index cec83bfea..0b841d98d 100644 --- a/test/AsmResolver.PE.Win32Resources.Tests/AsmResolver.PE.Win32Resources.Tests.csproj +++ b/test/AsmResolver.PE.Win32Resources.Tests/AsmResolver.PE.Win32Resources.Tests.csproj @@ -14,7 +14,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/AsmResolver.Symbols.Pdb.Tests/AsmResolver.Symbols.Pdb.Tests.csproj b/test/AsmResolver.Symbols.Pdb.Tests/AsmResolver.Symbols.Pdb.Tests.csproj index 582e228ed..80740e743 100644 --- a/test/AsmResolver.Symbols.Pdb.Tests/AsmResolver.Symbols.Pdb.Tests.csproj +++ b/test/AsmResolver.Symbols.Pdb.Tests/AsmResolver.Symbols.Pdb.Tests.csproj @@ -14,7 +14,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 6d88d4849d3c69f112b2d713eea5b08d72584ee1 Mon Sep 17 00:00:00 2001 From: Jeremy Pritts Date: Fri, 4 Nov 2022 16:45:12 -0400 Subject: [PATCH 03/29] Add ProcedureReferenceSymbolTest --- .../Records/ProcedureReferenceSymbolTest.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 test/AsmResolver.Symbols.Pdb.Tests/Records/ProcedureReferenceSymbolTest.cs diff --git a/test/AsmResolver.Symbols.Pdb.Tests/Records/ProcedureReferenceSymbolTest.cs b/test/AsmResolver.Symbols.Pdb.Tests/Records/ProcedureReferenceSymbolTest.cs new file mode 100644 index 000000000..3ea9e1dbd --- /dev/null +++ b/test/AsmResolver.Symbols.Pdb.Tests/Records/ProcedureReferenceSymbolTest.cs @@ -0,0 +1,21 @@ +using System.Linq; +using AsmResolver.Symbols.Pdb.Records; +using Xunit; + +namespace AsmResolver.Symbols.Pdb.Tests.Records; + +public class ProcedureReferenceSymbolTest : IClassFixture +{ + private readonly MockPdbFixture _fixture; + + public ProcedureReferenceSymbolTest(MockPdbFixture fixture) + { + _fixture = fixture; + } + + [Fact] + public void Name() + { + Assert.Equal("DllMain", _fixture.SimplePdb.Symbols.OfType().First().Name); + } +} From 7b8bf60fc4281cec3cea22d8f724f40d6c1333af Mon Sep 17 00:00:00 2001 From: Jeremy Pritts Date: Fri, 4 Nov 2022 16:59:36 -0400 Subject: [PATCH 04/29] Local Procedure Reference test --- .../Records/ProcedureReferenceSymbol.cs | 5 +++++ .../Records/ProcedureReferenceSymbolTest.cs | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/AsmResolver.Symbols.Pdb/Records/ProcedureReferenceSymbol.cs b/src/AsmResolver.Symbols.Pdb/Records/ProcedureReferenceSymbol.cs index 6697275aa..0ee3f5782 100644 --- a/src/AsmResolver.Symbols.Pdb/Records/ProcedureReferenceSymbol.cs +++ b/src/AsmResolver.Symbols.Pdb/Records/ProcedureReferenceSymbol.cs @@ -44,6 +44,11 @@ public override CodeViewSymbolType CodeViewSymbolType } } + /// + /// Is the symbol a Local Procedure Reference? + /// + public bool IsLocal => _local; + /// /// Gets the checksum of the referenced symbol name. The checksum used is the /// one specified in the header of the global symbols stream or static symbols stream. diff --git a/test/AsmResolver.Symbols.Pdb.Tests/Records/ProcedureReferenceSymbolTest.cs b/test/AsmResolver.Symbols.Pdb.Tests/Records/ProcedureReferenceSymbolTest.cs index 3ea9e1dbd..ae534649b 100644 --- a/test/AsmResolver.Symbols.Pdb.Tests/Records/ProcedureReferenceSymbolTest.cs +++ b/test/AsmResolver.Symbols.Pdb.Tests/Records/ProcedureReferenceSymbolTest.cs @@ -16,6 +16,12 @@ public ProcedureReferenceSymbolTest(MockPdbFixture fixture) [Fact] public void Name() { - Assert.Equal("DllMain", _fixture.SimplePdb.Symbols.OfType().First().Name); + Assert.Equal("DllMain", _fixture.SimplePdb.Symbols.OfType().First(s => !s.IsLocal).Name); + } + + [Fact] + public void LocalName() + { + Assert.Equal("__get_entropy", _fixture.SimplePdb.Symbols.OfType().First(s => s.IsLocal).Name); } } From 6e630ee40190b75f4d12f7efd34c702dfc063831 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Nov 2022 14:05:56 +0000 Subject: [PATCH 05/29] Bump Microsoft.NET.Test.Sdk from 17.3.2 to 17.4.0 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.3.2 to 17.4.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v17.3.2...v17.4.0) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .../AsmResolver.DotNet.Dynamic.Tests.csproj | 2 +- test/AsmResolver.DotNet.Tests/AsmResolver.DotNet.Tests.csproj | 2 +- test/AsmResolver.PE.File.Tests/AsmResolver.PE.File.Tests.csproj | 2 +- test/AsmResolver.PE.Tests/AsmResolver.PE.Tests.csproj | 2 +- .../AsmResolver.PE.Win32Resources.Tests.csproj | 2 +- .../AsmResolver.Symbols.Pdb.Tests.csproj | 2 +- test/AsmResolver.Tests/AsmResolver.Tests.csproj | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/AsmResolver.DotNet.Dynamic.Tests/AsmResolver.DotNet.Dynamic.Tests.csproj b/test/AsmResolver.DotNet.Dynamic.Tests/AsmResolver.DotNet.Dynamic.Tests.csproj index cbb134178..9b6e3a775 100644 --- a/test/AsmResolver.DotNet.Dynamic.Tests/AsmResolver.DotNet.Dynamic.Tests.csproj +++ b/test/AsmResolver.DotNet.Dynamic.Tests/AsmResolver.DotNet.Dynamic.Tests.csproj @@ -8,7 +8,7 @@ - + diff --git a/test/AsmResolver.DotNet.Tests/AsmResolver.DotNet.Tests.csproj b/test/AsmResolver.DotNet.Tests/AsmResolver.DotNet.Tests.csproj index 830749a8d..12eab81f7 100644 --- a/test/AsmResolver.DotNet.Tests/AsmResolver.DotNet.Tests.csproj +++ b/test/AsmResolver.DotNet.Tests/AsmResolver.DotNet.Tests.csproj @@ -9,7 +9,7 @@ - + all diff --git a/test/AsmResolver.PE.File.Tests/AsmResolver.PE.File.Tests.csproj b/test/AsmResolver.PE.File.Tests/AsmResolver.PE.File.Tests.csproj index 6b532fdfc..44c298a73 100644 --- a/test/AsmResolver.PE.File.Tests/AsmResolver.PE.File.Tests.csproj +++ b/test/AsmResolver.PE.File.Tests/AsmResolver.PE.File.Tests.csproj @@ -9,7 +9,7 @@ - + all diff --git a/test/AsmResolver.PE.Tests/AsmResolver.PE.Tests.csproj b/test/AsmResolver.PE.Tests/AsmResolver.PE.Tests.csproj index 3e76c6ef0..3945a4696 100644 --- a/test/AsmResolver.PE.Tests/AsmResolver.PE.Tests.csproj +++ b/test/AsmResolver.PE.Tests/AsmResolver.PE.Tests.csproj @@ -16,7 +16,7 @@ - + diff --git a/test/AsmResolver.PE.Win32Resources.Tests/AsmResolver.PE.Win32Resources.Tests.csproj b/test/AsmResolver.PE.Win32Resources.Tests/AsmResolver.PE.Win32Resources.Tests.csproj index 0b841d98d..66003d51f 100644 --- a/test/AsmResolver.PE.Win32Resources.Tests/AsmResolver.PE.Win32Resources.Tests.csproj +++ b/test/AsmResolver.PE.Win32Resources.Tests/AsmResolver.PE.Win32Resources.Tests.csproj @@ -8,7 +8,7 @@ - + all diff --git a/test/AsmResolver.Symbols.Pdb.Tests/AsmResolver.Symbols.Pdb.Tests.csproj b/test/AsmResolver.Symbols.Pdb.Tests/AsmResolver.Symbols.Pdb.Tests.csproj index 80740e743..495bbf831 100644 --- a/test/AsmResolver.Symbols.Pdb.Tests/AsmResolver.Symbols.Pdb.Tests.csproj +++ b/test/AsmResolver.Symbols.Pdb.Tests/AsmResolver.Symbols.Pdb.Tests.csproj @@ -8,7 +8,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/AsmResolver.Tests/AsmResolver.Tests.csproj b/test/AsmResolver.Tests/AsmResolver.Tests.csproj index 6d9327ffb..2bac4b411 100644 --- a/test/AsmResolver.Tests/AsmResolver.Tests.csproj +++ b/test/AsmResolver.Tests/AsmResolver.Tests.csproj @@ -8,7 +8,7 @@ - + all From d9ef330187efdb1d645beb7b6193ddcd9983f9a0 Mon Sep 17 00:00:00 2001 From: Washi Date: Sun, 13 Nov 2022 13:41:10 +0100 Subject: [PATCH 06/29] BUGFIX: Make LPArrayMarshalDescriptor::ArrayElementType nullable. --- .../Marshal/LPArrayMarshalDescriptor.cs | 38 +++++---- .../Signatures/MarshalDescriptorTest.cs | 80 +++++++++++-------- 2 files changed, 71 insertions(+), 47 deletions(-) diff --git a/src/AsmResolver.DotNet/Signatures/Marshal/LPArrayMarshalDescriptor.cs b/src/AsmResolver.DotNet/Signatures/Marshal/LPArrayMarshalDescriptor.cs index 7346b3616..6584a972f 100644 --- a/src/AsmResolver.DotNet/Signatures/Marshal/LPArrayMarshalDescriptor.cs +++ b/src/AsmResolver.DotNet/Signatures/Marshal/LPArrayMarshalDescriptor.cs @@ -17,7 +17,11 @@ public class LPArrayMarshalDescriptor : MarshalDescriptor /// public static LPArrayMarshalDescriptor FromReader(ref BinaryStreamReader reader) { - var descriptor = new LPArrayMarshalDescriptor((NativeType) reader.ReadByte()); + var descriptor = new LPArrayMarshalDescriptor(); + + if (!reader.CanRead(sizeof(byte))) + return descriptor; + descriptor.ArrayElementType = (NativeType) reader.ReadByte(); if (!reader.TryReadCompressedUInt32(out uint value)) return descriptor; @@ -34,11 +38,15 @@ public static LPArrayMarshalDescriptor FromReader(ref BinaryStreamReader reader) return descriptor; } + public LPArrayMarshalDescriptor() + { + } + /// /// Creates a new instance of the class. /// /// The type of elements stored in the array. - public LPArrayMarshalDescriptor(NativeType arrayElementType) + public LPArrayMarshalDescriptor(NativeType? arrayElementType) { ArrayElementType = arrayElementType; } @@ -49,7 +57,7 @@ public LPArrayMarshalDescriptor(NativeType arrayElementType) /// /// Gets the type of elements stored in the array. /// - public NativeType ArrayElementType + public NativeType? ArrayElementType { get; set; @@ -86,20 +94,22 @@ public LPArrayFlags? Flags protected override void WriteContents(in BlobSerializationContext context) { var writer = context.Writer; - writer.WriteByte((byte) NativeType); + + if (!ArrayElementType.HasValue) + return; writer.WriteByte((byte) ArrayElementType); - if (ParameterIndex.HasValue) - { - writer.WriteCompressedUInt32((uint) ParameterIndex.Value); - if (NumberOfElements.HasValue) - { - writer.WriteCompressedUInt32((uint) NumberOfElements.Value); - if (Flags.HasValue) - writer.WriteCompressedUInt32((uint) Flags.Value); - } - } + if (!ParameterIndex.HasValue) + return; + writer.WriteCompressedUInt32((uint) ParameterIndex.Value); + + if (!NumberOfElements.HasValue) + return; + writer.WriteCompressedUInt32((uint) NumberOfElements.Value); + + if (Flags.HasValue) + writer.WriteCompressedUInt32((uint) Flags.Value); } } } diff --git a/test/AsmResolver.DotNet.Tests/Signatures/MarshalDescriptorTest.cs b/test/AsmResolver.DotNet.Tests/Signatures/MarshalDescriptorTest.cs index 90e8c7925..493aa2099 100644 --- a/test/AsmResolver.DotNet.Tests/Signatures/MarshalDescriptorTest.cs +++ b/test/AsmResolver.DotNet.Tests/Signatures/MarshalDescriptorTest.cs @@ -65,10 +65,24 @@ public void PersistentSimpleMarshaller() { var method = LookupMethod(nameof(PlatformInvoke.SimpleMarshaller)); var newMethod = RebuildAndLookup(method); - Assert.Equal(method.Parameters[0].Definition.MarshalDescriptor.NativeType, + Assert.Equal(method.Parameters[0].Definition.MarshalDescriptor.NativeType, newMethod.Parameters[0].Definition.MarshalDescriptor.NativeType); } - + + [Fact] + public void PersistentLPArrayWithoutElementType() + { + var method = LookupMethod(nameof(PlatformInvoke.LPArrayFixedSizeMarshaller)); + var originalMarshaller = (LPArrayMarshalDescriptor) method.Parameters[0].Definition!.MarshalDescriptor!; + originalMarshaller.ArrayElementType = null; + + var newMethod = RebuildAndLookup(method); + var newArrayMarshaller = Assert.IsAssignableFrom( + newMethod.Parameters[0].Definition!.MarshalDescriptor); + + Assert.Null(newArrayMarshaller.ArrayElementType); + } + [Fact] public void ReadLPArrayMarshallerWithFixedSize() { @@ -85,7 +99,7 @@ public void PersistentLPArrayMarshallerWithFixedSize() { var method = LookupMethod(nameof(PlatformInvoke.LPArrayFixedSizeMarshaller)); var originalMarshaller = (LPArrayMarshalDescriptor) method.Parameters[0].Definition.MarshalDescriptor; - + var newMethod = RebuildAndLookup(method); var newMarshaller = newMethod.Parameters[0].Definition.MarshalDescriptor; Assert.IsAssignableFrom(newMarshaller); @@ -110,7 +124,7 @@ public void PersistentLPArrayMarshallerWithVariableSize() { var method = LookupMethod(nameof(PlatformInvoke.LPArrayVariableSizeMarshaller)); var originalMarshaller = (LPArrayMarshalDescriptor) method.Parameters[0].Definition.MarshalDescriptor; - + var newMethod = RebuildAndLookup(method); var marshaller = newMethod.Parameters[0].Definition.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); @@ -135,7 +149,7 @@ public void PersistentSafeArrayMarshaller() { var method = LookupMethod(nameof(PlatformInvoke.SafeArrayMarshaller)); var originalMarshaller = (SafeArrayMarshalDescriptor) method.Parameters[0].Definition.MarshalDescriptor; - + var newMethod = RebuildAndLookup(method); var marshaller = newMethod.Parameters[0].Definition.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); @@ -160,7 +174,7 @@ public void PersistentSafeArrayMarshallerWithSubType() { var method = LookupMethod(nameof(PlatformInvoke.SafeArrayMarshallerWithSubType)); var originalMarshaller = (SafeArrayMarshalDescriptor) method.Parameters[0].Definition.MarshalDescriptor; - + var newMethod = RebuildAndLookup(method); var marshaller = newMethod.Parameters[0].Definition.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); @@ -186,7 +200,7 @@ public void PersistentSafeArrayMarshallerWithUserDefinedSubType() { var method = LookupMethod(nameof(PlatformInvoke.SafeArrayMarshallerWithUserSubType)); var originalMarshaller = (SafeArrayMarshalDescriptor) method.Parameters[0].Definition.MarshalDescriptor; - + var newMethod = RebuildAndLookup(method); var marshaller = newMethod.Parameters[0].Definition.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); @@ -195,7 +209,7 @@ public void PersistentSafeArrayMarshallerWithUserDefinedSubType() Assert.NotNull(arrayMarshaller.UserDefinedSubType); Assert.Equal(originalMarshaller.UserDefinedSubType.Name, arrayMarshaller.UserDefinedSubType.Name); } - + [Fact] public void ReadFixedArrayMarshaller() { @@ -203,7 +217,7 @@ public void ReadFixedArrayMarshaller() var marshaller = field.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); } - + [Fact] public void PersistentFixedArrayMarshaller() { @@ -212,18 +226,18 @@ public void PersistentFixedArrayMarshaller() var marshaller = newField.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); } - + [Fact] public void ReadFixedArrayMarshallerWithFixedSizeSpecifier() { var field = LookupField(nameof(Marshalling.FixedArrayMarshallerWithFixedSize)); var marshaller = field.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); - + var arrayMarshaller = (FixedArrayMarshalDescriptor) marshaller; Assert.Equal(10, arrayMarshaller.Size); } - + [Fact] public void PersistentFixedArrayMarshallerWithFixedSizeSpecifier() { @@ -233,33 +247,33 @@ public void PersistentFixedArrayMarshallerWithFixedSizeSpecifier() var newField = RebuildAndLookup(field); var marshaller = newField.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); - + var arrayMarshaller = (FixedArrayMarshalDescriptor) marshaller; Assert.Equal(originalMarshaller.Size, arrayMarshaller.Size); } - + [Fact] public void ReadFixedArrayMarshallerWithFixedSizeSpecifierAndArrayType() { var field = LookupField(nameof(Marshalling.FixedArrayMarshallerWithFixedSizeAndArrayType)); var marshaller = field.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); - + var arrayMarshaller = (FixedArrayMarshalDescriptor) marshaller; Assert.Equal(10, arrayMarshaller.Size); Assert.Equal(NativeType.U1, arrayMarshaller.ArrayElementType); } - + [Fact] public void PersistentFixedArrayMarshallerWithFixedSizeSpecifierAndArrayType() { var field = LookupField(nameof(Marshalling.FixedArrayMarshallerWithFixedSizeAndArrayType)); var originalMarshaller = (FixedArrayMarshalDescriptor) field.MarshalDescriptor; - + var newField = RebuildAndLookup(field); var marshaller = newField.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); - + var arrayMarshaller = (FixedArrayMarshalDescriptor) marshaller; Assert.Equal(originalMarshaller.Size, arrayMarshaller.Size); Assert.Equal(originalMarshaller.ArrayElementType, arrayMarshaller.ArrayElementType); @@ -271,7 +285,7 @@ public void ReadCustomMarshallerType() var field = LookupField(nameof(Marshalling.CustomMarshallerWithCustomType)); var marshaller = field.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); - + var customMarshaller = (CustomMarshalDescriptor) marshaller; Assert.Equal(nameof(Marshalling), customMarshaller.MarshalType.Name); } @@ -281,11 +295,11 @@ public void PersistentCustomMarshallerType() { var field = LookupField(nameof(Marshalling.CustomMarshallerWithCustomType)); var originalMarshaller = (CustomMarshalDescriptor) field.MarshalDescriptor; - + var newField = RebuildAndLookup(field); var marshaller = newField.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); - + var customMarshaller = (CustomMarshalDescriptor) marshaller; Assert.Equal(originalMarshaller.MarshalType.Name, customMarshaller.MarshalType.Name); } @@ -296,7 +310,7 @@ public void ReadCustomMarshallerTypeWithCookie() var field = LookupField(nameof(Marshalling.CustomMarshallerWithCustomTypeAndCookie)); var marshaller = field.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); - + var customMarshaller = (CustomMarshalDescriptor) marshaller; Assert.Equal(nameof(Marshalling), customMarshaller.MarshalType.Name); Assert.Equal("abc", customMarshaller.Cookie); @@ -307,11 +321,11 @@ public void PersistentCustomMarshallerTypeWithCookie() { var field = LookupField(nameof(Marshalling.CustomMarshallerWithCustomTypeAndCookie)); var originalMarshaller = (CustomMarshalDescriptor) field.MarshalDescriptor; - + var newField = RebuildAndLookup(field); var marshaller = newField.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); - + var customMarshaller = (CustomMarshalDescriptor) marshaller; Assert.Equal(originalMarshaller.MarshalType.Name, customMarshaller.MarshalType.Name); Assert.Equal(originalMarshaller.Cookie, customMarshaller.Cookie); @@ -323,7 +337,7 @@ public void ReadFixedSysString() var field = LookupField(nameof(Marshalling.FixedSysString)); var marshaller = field.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); - + var stringMarshaller = (FixedSysStringMarshalDescriptor) marshaller; Assert.Equal(123, stringMarshaller.Size); } @@ -333,11 +347,11 @@ public void PersistentFixedSysString() { var field = LookupField(nameof(Marshalling.FixedSysString)); var originalMarshaller = (FixedSysStringMarshalDescriptor) field.MarshalDescriptor; - + var newField = RebuildAndLookup(field); var marshaller = newField.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); - + var stringMarshaller = (FixedSysStringMarshalDescriptor) marshaller; Assert.Equal(originalMarshaller.Size, stringMarshaller.Size); } @@ -348,7 +362,7 @@ public void ReadComInterface() var method = LookupMethod(nameof(PlatformInvoke.ComInterface)); var marshaller = method.Parameters[0].Definition.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); - + var comMarshaller = (ComInterfaceMarshalDescriptor) marshaller; Assert.Null(comMarshaller.IidParameterIndex); } @@ -361,7 +375,7 @@ public void PersistentComInterface() var newMethod = RebuildAndLookup(method); var marshaller = newMethod.Parameters[0].Definition.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); - + var comMarshaller = (ComInterfaceMarshalDescriptor) marshaller; Assert.Null(comMarshaller.IidParameterIndex); } @@ -372,7 +386,7 @@ public void ReadComInterfaceWithParameterIndex() var method = LookupMethod(nameof(PlatformInvoke.ComInterfaceWithIidParameter)); var marshaller = method.Parameters[0].Definition.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); - + var comMarshaller = (ComInterfaceMarshalDescriptor) marshaller; Assert.Equal(1, comMarshaller.IidParameterIndex); } @@ -382,13 +396,13 @@ public void PersistentComInterfaceWithParameterIndex() { var method = LookupMethod(nameof(PlatformInvoke.ComInterfaceWithIidParameter)); var originalMarshaller = (ComInterfaceMarshalDescriptor) method.Parameters[0].Definition.MarshalDescriptor; - + var newMethod = RebuildAndLookup(method); var marshaller = newMethod.Parameters[0].Definition.MarshalDescriptor; Assert.IsAssignableFrom(marshaller); - + var comMarshaller = (ComInterfaceMarshalDescriptor) marshaller; Assert.Equal(originalMarshaller.IidParameterIndex, comMarshaller.IidParameterIndex); } } -} \ No newline at end of file +} From 1cf73702338d3de56a231c3caa4dc5446ca0ffd9 Mon Sep 17 00:00:00 2001 From: Washi Date: Sun, 13 Nov 2022 22:13:55 +0100 Subject: [PATCH 07/29] Add missing xmldoc to default constructor. --- .../Signatures/Marshal/LPArrayMarshalDescriptor.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/AsmResolver.DotNet/Signatures/Marshal/LPArrayMarshalDescriptor.cs b/src/AsmResolver.DotNet/Signatures/Marshal/LPArrayMarshalDescriptor.cs index 6584a972f..b57d2ed18 100644 --- a/src/AsmResolver.DotNet/Signatures/Marshal/LPArrayMarshalDescriptor.cs +++ b/src/AsmResolver.DotNet/Signatures/Marshal/LPArrayMarshalDescriptor.cs @@ -38,6 +38,9 @@ public static LPArrayMarshalDescriptor FromReader(ref BinaryStreamReader reader) return descriptor; } + /// + /// Creates a new empty instance of the class. + /// public LPArrayMarshalDescriptor() { } From 9bb5d4dca409cdc1e6b2ba4cba816cc94b89311f Mon Sep 17 00:00:00 2001 From: Washi Date: Sun, 13 Nov 2022 22:14:30 +0100 Subject: [PATCH 08/29] Upgrade AsmResolver.DotNet.Tests to net6.0 --- test/AsmResolver.DotNet.Tests/AsmResolver.DotNet.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/AsmResolver.DotNet.Tests/AsmResolver.DotNet.Tests.csproj b/test/AsmResolver.DotNet.Tests/AsmResolver.DotNet.Tests.csproj index 830749a8d..6ad665d50 100644 --- a/test/AsmResolver.DotNet.Tests/AsmResolver.DotNet.Tests.csproj +++ b/test/AsmResolver.DotNet.Tests/AsmResolver.DotNet.Tests.csproj @@ -1,7 +1,7 @@ - net5.0 + net6.0 false From 1522dcbe9a8e30cd208bcecae9fa8752a39c5d05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Nov 2022 14:10:21 +0000 Subject: [PATCH 09/29] Bump System.Text.Json from 6.0.6 to 6.0.7 Bumps [System.Text.Json](https://github.com/dotnet/runtime) from 6.0.6 to 6.0.7. - [Release notes](https://github.com/dotnet/runtime/releases) - [Commits](https://github.com/dotnet/runtime/compare/v6.0.6...v6.0.7) --- updated-dependencies: - dependency-name: System.Text.Json dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- src/AsmResolver.DotNet/AsmResolver.DotNet.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AsmResolver.DotNet/AsmResolver.DotNet.csproj b/src/AsmResolver.DotNet/AsmResolver.DotNet.csproj index edf05784b..344e5b44a 100644 --- a/src/AsmResolver.DotNet/AsmResolver.DotNet.csproj +++ b/src/AsmResolver.DotNet/AsmResolver.DotNet.csproj @@ -28,7 +28,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 6bc9518acd0bb68a3aeac5c93839a7e5a4501c86 Mon Sep 17 00:00:00 2001 From: Washi Date: Sat, 26 Nov 2022 12:29:05 +0100 Subject: [PATCH 10/29] Add failing assemblyref with explicit public key test. --- .../AssemblyResolverTest.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/test/AsmResolver.DotNet.Tests/AssemblyResolverTest.cs b/test/AsmResolver.DotNet.Tests/AssemblyResolverTest.cs index 8673fb4df..aaf5d0427 100644 --- a/test/AsmResolver.DotNet.Tests/AssemblyResolverTest.cs +++ b/test/AsmResolver.DotNet.Tests/AssemblyResolverTest.cs @@ -202,5 +202,38 @@ public void PreferResolveFromGac64If64BitAssembly(bool legacy) Assert.NotNull(resolved); Assert.Contains("GAC_64", resolved.ManifestModule!.FilePath!); } + + [Fact] + public void ResolveReferenceWithExplicitPublicKey() + { + // https://github.com/Washi1337/AsmResolver/issues/381 + + var reference = new AssemblyReference( + "System.Collections", + new Version(6, 0, 0, 0), + true, + new byte[] + { + 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x52, 0x53, 0x41, 0x31, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x07, 0xD1, + 0xFA, 0x57, 0xC4, 0xAE, 0xD9, 0xF0, 0xA3, 0x2E, 0x84, 0xAA, 0x0F, 0xAE, 0xFD, 0x0D, 0xE9, 0xE8, 0xFD, + 0x6A, 0xEC, 0x8F, 0x87, 0xFB, 0x03, 0x76, 0x6C, 0x83, 0x4C, 0x99, 0x92, 0x1E, 0xB2, 0x3B, 0xE7, 0x9A, + 0xD9, 0xD5, 0xDC, 0xC1, 0xDD, 0x9A, 0xD2, 0x36, 0x13, 0x21, 0x02, 0x90, 0x0B, 0x72, 0x3C, 0xF9, 0x80, + 0x95, 0x7F, 0xC4, 0xE1, 0x77, 0x10, 0x8F, 0xC6, 0x07, 0x77, 0x4F, 0x29, 0xE8, 0x32, 0x0E, 0x92, 0xEA, + 0x05, 0xEC, 0xE4, 0xE8, 0x21, 0xC0, 0xA5, 0xEF, 0xE8, 0xF1, 0x64, 0x5C, 0x4C, 0x0C, 0x93, 0xC1, 0xAB, + 0x99, 0x28, 0x5D, 0x62, 0x2C, 0xAA, 0x65, 0x2C, 0x1D, 0xFA, 0xD6, 0x3D, 0x74, 0x5D, 0x6F, 0x2D, 0xE5, + 0xF1, 0x7E, 0x5E, 0xAF, 0x0F, 0xC4, 0x96, 0x3D, 0x26, 0x1C, 0x8A, 0x12, 0x43, 0x65, 0x18, 0x20, 0x6D, + 0xC0, 0x93, 0x34, 0x4D, 0x5A, 0xD2, 0x93 + }); + + var module = new ModuleDefinition("Dummy", KnownCorLibs.SystemRuntime_v6_0_0_0); + module.AssemblyReferences.Add(reference); + + var definition = reference.Resolve(); + + Assert.NotNull(definition); + Assert.Equal(reference.Name, definition.Name); + Assert.NotNull(definition.ManifestModule!.FilePath); + } } } From bf4ed239cb6dfd1cf0deb707ddee0a204abfca13 Mon Sep 17 00:00:00 2001 From: Washi Date: Sat, 26 Nov 2022 12:33:08 +0100 Subject: [PATCH 11/29] BUGFIX: Do not resolve assembly refs while computing public key tokens. --- src/AsmResolver.DotNet/AssemblyReference.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/AsmResolver.DotNet/AssemblyReference.cs b/src/AsmResolver.DotNet/AssemblyReference.cs index f699d00fc..c255427de 100644 --- a/src/AsmResolver.DotNet/AssemblyReference.cs +++ b/src/AsmResolver.DotNet/AssemblyReference.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using AsmResolver.Collections; using AsmResolver.PE.DotNet.Metadata.Tables; using AsmResolver.PE.DotNet.Metadata.Tables.Rows; @@ -128,9 +129,14 @@ public byte[]? HashValue if (!HasPublicKey) return PublicKeyOrToken; - _publicKeyToken ??= PublicKeyOrToken != null - ? ComputePublicKeyToken(PublicKeyOrToken, Resolve()?.HashAlgorithm ?? AssemblyHashAlgorithm.Sha1) - : null; + if (_publicKeyToken is null && PublicKeyOrToken is not null) + { + lock (_publicKeyOrToken) + { + if (_publicKeyToken is null && PublicKeyOrToken is not null) + _publicKeyToken = ComputePublicKeyToken(PublicKeyOrToken, AssemblyHashAlgorithm.Sha1); + } + } return _publicKeyToken; } From eb41d58f81a77f94a7cf8a8c2e49863ba64ae9c4 Mon Sep 17 00:00:00 2001 From: Washi Date: Sun, 27 Nov 2022 12:24:15 +0100 Subject: [PATCH 12/29] BUGFIX: Let EndFinally clear stack. --- src/AsmResolver.PE/DotNet/Cil/CilOpCodes.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/AsmResolver.PE/DotNet/Cil/CilOpCodes.cs b/src/AsmResolver.PE/DotNet/Cil/CilOpCodes.cs index 2c5f41b5a..6d7012b0f 100644 --- a/src/AsmResolver.PE/DotNet/Cil/CilOpCodes.cs +++ b/src/AsmResolver.PE/DotNet/Cil/CilOpCodes.cs @@ -18,7 +18,7 @@ public static class CilOpCodes /// Gets a sorted list of all single-byte operation codes. /// public static readonly CilOpCode[] SingleByteOpCodes = new CilOpCode[256]; - + /// /// Gets a sorted list of all multi-byte operation codes. /// @@ -2824,7 +2824,7 @@ public static class CilOpCodes (((ushort) CilCode.Endfinally & 0xFF) << ValueOffset) | (((ushort) CilCode.Endfinally >> 15) << TwoBytesOffset) | ((ushort) Push0 << StackBehaviourPushOffset) - | ((ushort) Pop0 << StackBehaviourPopOffset) + | ((ushort) PopAll << StackBehaviourPopOffset) | ((byte) Primitive << OpCodeTypeOffset) | ((byte) InlineNone << OperandTypeOffset) | ((byte) Return << FlowControlOffset)); @@ -2890,7 +2890,7 @@ public static class CilOpCodes | ((byte) Next << FlowControlOffset)); /// - /// This prefix opcode is reserved and currently not implemented in the runtime + /// This prefix opcode is reserved and currently not implemented in the runtime /// /// /// See also: @@ -2905,7 +2905,7 @@ public static class CilOpCodes | ((byte) Meta << FlowControlOffset)); /// - /// This prefix opcode is reserved and currently not implemented in the runtime + /// This prefix opcode is reserved and currently not implemented in the runtime /// /// /// See also: @@ -2920,7 +2920,7 @@ public static class CilOpCodes | ((byte) Meta << FlowControlOffset)); /// - /// This prefix opcode is reserved and currently not implemented in the runtime + /// This prefix opcode is reserved and currently not implemented in the runtime /// /// /// See also: @@ -2935,7 +2935,7 @@ public static class CilOpCodes | ((byte) Meta << FlowControlOffset)); /// - /// This prefix opcode is reserved and currently not implemented in the runtime + /// This prefix opcode is reserved and currently not implemented in the runtime /// /// /// See also: @@ -2950,7 +2950,7 @@ public static class CilOpCodes | ((byte) Meta << FlowControlOffset)); /// - /// This prefix opcode is reserved and currently not implemented in the runtime + /// This prefix opcode is reserved and currently not implemented in the runtime /// /// /// See also: @@ -2965,7 +2965,7 @@ public static class CilOpCodes | ((byte) Meta << FlowControlOffset)); /// - /// This prefix opcode is reserved and currently not implemented in the runtime + /// This prefix opcode is reserved and currently not implemented in the runtime /// /// /// See also: @@ -2980,7 +2980,7 @@ public static class CilOpCodes | ((byte) Meta << FlowControlOffset)); /// - /// This prefix opcode is reserved and currently not implemented in the runtime + /// This prefix opcode is reserved and currently not implemented in the runtime /// /// /// See also: @@ -2995,7 +2995,7 @@ public static class CilOpCodes | ((byte) Meta << FlowControlOffset)); /// - /// This prefix opcode is reserved and currently not implemented in the runtime + /// This prefix opcode is reserved and currently not implemented in the runtime /// /// /// See also: From bebe5d63d69699132fbdb7f0baf7565a07f3c985 Mon Sep 17 00:00:00 2001 From: Washi Date: Sun, 27 Nov 2022 12:45:53 +0100 Subject: [PATCH 13/29] Remove redundant ReaderWriterLockSlim in LazyList. --- src/AsmResolver/Collections/LazyList.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/AsmResolver/Collections/LazyList.cs b/src/AsmResolver/Collections/LazyList.cs index 6a88b6193..2bfdab185 100644 --- a/src/AsmResolver/Collections/LazyList.cs +++ b/src/AsmResolver/Collections/LazyList.cs @@ -13,7 +13,6 @@ namespace AsmResolver.Collections [DebuggerDisplay("Count = {" + nameof(Count) + "}")] public abstract class LazyList : IList { - private readonly ReaderWriterLockSlim _lock = new(LockRecursionPolicy.NoRecursion); private readonly List _items; /// From c0283c721957ddbb149a1edbfa77b16b1555ae2d Mon Sep 17 00:00:00 2001 From: Washi Date: Sun, 27 Nov 2022 13:28:27 +0100 Subject: [PATCH 14/29] Avoid allocation of traversed tokens when possible. --- .../DynamicTypeSignatureReader.cs | 2 +- .../Serialized/SerializedFieldDefinition.cs | 3 +- .../Serialized/SerializedMemberReference.cs | 3 +- .../Serialized/SerializedMethodDefinition.cs | 3 +- .../SerializedMethodSpecification.cs | 3 +- .../SerializedPropertyDefinition.cs | 3 +- .../SerializedStandAloneSignature.cs | 3 +- .../Serialized/SerializedTypeSpecification.cs | 4 +-- .../Signatures/BlobReadContext.cs | 25 ++++++++----- .../Signatures/CallingConventionSignature.cs | 16 ++++----- .../Signatures/FieldSignature.cs | 4 +-- .../GenericInstanceMethodSignature.cs | 4 +-- .../Signatures/LocalVariablesSignature.cs | 4 +-- .../Signatures/MethodSignature.cs | 4 +-- .../Signatures/MethodSignatureBase.cs | 6 ++-- .../Signatures/PropertySignature.cs | 4 +-- .../Signatures/Types/ArrayTypeSignature.cs | 4 +-- .../Types/GenericInstanceTypeSignature.cs | 6 ++-- .../Types/ITypeSignatureResolver.cs | 4 +-- .../Types/PhysicalTypeSignatureResolver.cs | 6 ++-- .../Signatures/Types/TypeSignature.cs | 36 +++++++++---------- .../Signatures/MethodSignatureTest.cs | 7 ++++ 22 files changed, 87 insertions(+), 67 deletions(-) diff --git a/src/AsmResolver.DotNet.Dynamic/DynamicTypeSignatureReader.cs b/src/AsmResolver.DotNet.Dynamic/DynamicTypeSignatureReader.cs index 969ef3f0a..9ebb00448 100644 --- a/src/AsmResolver.DotNet.Dynamic/DynamicTypeSignatureReader.cs +++ b/src/AsmResolver.DotNet.Dynamic/DynamicTypeSignatureReader.cs @@ -41,7 +41,7 @@ static DynamicTypeSignatureResolver() public static bool IsSupported => GetTypeFromHandleUnsafeMethod is not null; /// - public override TypeSignature ResolveRuntimeType(in BlobReadContext context, nint address) + public override TypeSignature ResolveRuntimeType(ref BlobReadContext context, nint address) { if (!IsSupported) throw new PlatformNotSupportedException("The current platform does not support the translation of raw type handles to System.Type instances."); diff --git a/src/AsmResolver.DotNet/Serialized/SerializedFieldDefinition.cs b/src/AsmResolver.DotNet/Serialized/SerializedFieldDefinition.cs index 083dd51e9..9af8ed262 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedFieldDefinition.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedFieldDefinition.cs @@ -47,7 +47,8 @@ public SerializedFieldDefinition(ModuleReaderContext context, MetadataToken toke $"Invalid signature blob index in field {MetadataToken.ToString()}."); } - return FieldSignature.FromReader(new BlobReadContext(_context), ref reader); + var context = new BlobReadContext(_context); + return FieldSignature.FromReader(ref context, ref reader); } /// diff --git a/src/AsmResolver.DotNet/Serialized/SerializedMemberReference.cs b/src/AsmResolver.DotNet/Serialized/SerializedMemberReference.cs index 1a867ef11..0064bfc53 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedMemberReference.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedMemberReference.cs @@ -60,7 +60,8 @@ public SerializedMemberReference( $"Invalid signature blob index in member reference {MetadataToken.ToString()}."); } - return CallingConventionSignature.FromReader(new BlobReadContext(_context), ref reader, true); + var context = new BlobReadContext(_context); + return CallingConventionSignature.FromReader(ref context, ref reader, true); } /// diff --git a/src/AsmResolver.DotNet/Serialized/SerializedMethodDefinition.cs b/src/AsmResolver.DotNet/Serialized/SerializedMethodDefinition.cs index 0d09b8e0a..4c55e60c1 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedMethodDefinition.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedMethodDefinition.cs @@ -52,7 +52,8 @@ public SerializedMethodDefinition(ModuleReaderContext context, MetadataToken tok $"Invalid signature blob index in method {MetadataToken.ToString()}."); } - return MethodSignature.FromReader(new BlobReadContext(_context), ref reader); + var context = new BlobReadContext(_context); + return MethodSignature.FromReader(ref context, ref reader); } /// diff --git a/src/AsmResolver.DotNet/Serialized/SerializedMethodSpecification.cs b/src/AsmResolver.DotNet/Serialized/SerializedMethodSpecification.cs index 929366651..0f8b7e3b6 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedMethodSpecification.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedMethodSpecification.cs @@ -52,7 +52,8 @@ public SerializedMethodSpecification(ModuleReaderContext context, MetadataToken $"Invalid instantiation blob index in method specification {MetadataToken.ToString()}."); } - return GenericInstanceMethodSignature.FromReader(new BlobReadContext(_context), ref reader); + var context = new BlobReadContext(_context); + return GenericInstanceMethodSignature.FromReader(ref context, ref reader); } /// diff --git a/src/AsmResolver.DotNet/Serialized/SerializedPropertyDefinition.cs b/src/AsmResolver.DotNet/Serialized/SerializedPropertyDefinition.cs index d2dd3ae74..5f051e158 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedPropertyDefinition.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedPropertyDefinition.cs @@ -48,7 +48,8 @@ public SerializedPropertyDefinition(ModuleReaderContext context, MetadataToken t $"Invalid signature blob index in property {MetadataToken.ToString()}."); } - return PropertySignature.FromReader(new BlobReadContext(_context), ref reader); + var context = new BlobReadContext(_context); + return PropertySignature.FromReader(ref context, ref reader); } /// diff --git a/src/AsmResolver.DotNet/Serialized/SerializedStandAloneSignature.cs b/src/AsmResolver.DotNet/Serialized/SerializedStandAloneSignature.cs index 49ab50915..e65329632 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedStandAloneSignature.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedStandAloneSignature.cs @@ -43,7 +43,8 @@ public SerializedStandAloneSignature( $"Invalid signature blob index in stand-alone signature {MetadataToken.ToString()}."); } - return CallingConventionSignature.FromReader(new BlobReadContext(_context), ref reader); + var context = new BlobReadContext(_context); + return CallingConventionSignature.FromReader(ref context, ref reader); } /// diff --git a/src/AsmResolver.DotNet/Serialized/SerializedTypeSpecification.cs b/src/AsmResolver.DotNet/Serialized/SerializedTypeSpecification.cs index 47f29d5ac..50107bbc0 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedTypeSpecification.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedTypeSpecification.cs @@ -42,8 +42,8 @@ public SerializedTypeSpecification(ModuleReaderContext context, MetadataToken to } var context = new BlobReadContext(_context); - context.TraversedTokens.Add(MetadataToken); - return TypeSignature.FromReader(context, ref reader); + context.StepInToken(MetadataToken); + return TypeSignature.FromReader(ref context, ref reader); } /// diff --git a/src/AsmResolver.DotNet/Signatures/BlobReadContext.cs b/src/AsmResolver.DotNet/Signatures/BlobReadContext.cs index 1af93d92d..f95f1f94f 100644 --- a/src/AsmResolver.DotNet/Signatures/BlobReadContext.cs +++ b/src/AsmResolver.DotNet/Signatures/BlobReadContext.cs @@ -10,14 +10,16 @@ namespace AsmResolver.DotNet.Signatures /// Provides a context in which a metadata blob parser exists in. This includes the original module reader context /// as well as a mechanism to protect against infinite recursion. /// - public readonly struct BlobReadContext + public struct BlobReadContext { + private Stack? _traversedTokens; + /// /// Creates a new instance of the structure. /// /// The original read context. public BlobReadContext(ModuleReaderContext readerContext) - : this(readerContext, PhysicalTypeSignatureResolver.Instance, Enumerable.Empty()) + : this(readerContext, PhysicalTypeSignatureResolver.Instance) { } @@ -27,8 +29,10 @@ public BlobReadContext(ModuleReaderContext readerContext) /// The original read context. /// The object responsible for resolving raw type metadata tokens and addresses. public BlobReadContext(ModuleReaderContext readerContext, ITypeSignatureResolver resolver) - : this(readerContext, resolver, Enumerable.Empty()) { + ReaderContext = readerContext; + TypeSignatureResolver = resolver; + _traversedTokens = null; } /// @@ -41,7 +45,7 @@ public BlobReadContext(ModuleReaderContext readerContext, ITypeSignatureResolver { ReaderContext = readerContext; TypeSignatureResolver = resolver; - TraversedTokens = new HashSet(traversedTokens); + _traversedTokens = new Stack(traversedTokens); } /// @@ -60,12 +64,15 @@ public ITypeSignatureResolver TypeSignatureResolver get; } - /// - /// Gets a collection of metadata tokens that were traversed during the parsing of metadata. - /// - public ISet TraversedTokens + public bool StepInToken(MetadataToken token) { - get; + _traversedTokens ??= new Stack(); + + if (_traversedTokens.Contains(token)) + return false; + + _traversedTokens.Push(token); + return true; } } } diff --git a/src/AsmResolver.DotNet/Signatures/CallingConventionSignature.cs b/src/AsmResolver.DotNet/Signatures/CallingConventionSignature.cs index 12d04b0e2..563ab5194 100644 --- a/src/AsmResolver.DotNet/Signatures/CallingConventionSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/CallingConventionSignature.cs @@ -20,11 +20,11 @@ public abstract class CallingConventionSignature : ExtendableBlobSignature, IImp /// put into the property. /// The read signature. public static CallingConventionSignature? FromReader( - in BlobReadContext context, + ref BlobReadContext context, ref BinaryStreamReader reader, bool readToEnd = true) { - var signature = ReadSignature(context, ref reader); + var signature = ReadSignature(ref context, ref reader); if (readToEnd) { byte[] extraData = reader.ReadToEnd(); @@ -35,7 +35,7 @@ public abstract class CallingConventionSignature : ExtendableBlobSignature, IImp return signature; } - private static CallingConventionSignature? ReadSignature(in BlobReadContext context, ref BinaryStreamReader reader) + private static CallingConventionSignature? ReadSignature(ref BlobReadContext context, ref BinaryStreamReader reader) { byte flag = reader.ReadByte(); reader.Offset--; @@ -50,19 +50,19 @@ public abstract class CallingConventionSignature : ExtendableBlobSignature, IImp case CallingConventionAttributes.ThisCall: case CallingConventionAttributes.VarArg: case CallingConventionAttributes.Unmanaged: - return MethodSignature.FromReader(context, ref reader); + return MethodSignature.FromReader(ref context, ref reader); case CallingConventionAttributes.Property: - return PropertySignature.FromReader(context, ref reader); + return PropertySignature.FromReader(ref context, ref reader); case CallingConventionAttributes.Local: - return LocalVariablesSignature.FromReader(context, ref reader); + return LocalVariablesSignature.FromReader(ref context, ref reader); case CallingConventionAttributes.GenericInstance: - return GenericInstanceMethodSignature.FromReader(context, ref reader); + return GenericInstanceMethodSignature.FromReader(ref context, ref reader); case CallingConventionAttributes.Field: - return FieldSignature.FromReader(context, ref reader); + return FieldSignature.FromReader(ref context, ref reader); } throw new NotSupportedException($"Invalid or unsupported calling convention signature header {flag:X2}."); diff --git a/src/AsmResolver.DotNet/Signatures/FieldSignature.cs b/src/AsmResolver.DotNet/Signatures/FieldSignature.cs index f1d69e005..581d58553 100644 --- a/src/AsmResolver.DotNet/Signatures/FieldSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/FieldSignature.cs @@ -37,11 +37,11 @@ public static FieldSignature CreateInstance(TypeSignature fieldType) /// The blob reader context. /// The blob input stream. /// The field signature. - public static FieldSignature FromReader(in BlobReadContext context, ref BinaryStreamReader reader) + public static FieldSignature FromReader(ref BlobReadContext context, ref BinaryStreamReader reader) { return new( (CallingConventionAttributes) reader.ReadByte(), - TypeSignature.FromReader(context, ref reader)); + TypeSignature.FromReader(ref context, ref reader)); } /// diff --git a/src/AsmResolver.DotNet/Signatures/GenericInstanceMethodSignature.cs b/src/AsmResolver.DotNet/Signatures/GenericInstanceMethodSignature.cs index 33848a34d..23f9ec947 100644 --- a/src/AsmResolver.DotNet/Signatures/GenericInstanceMethodSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/GenericInstanceMethodSignature.cs @@ -11,7 +11,7 @@ namespace AsmResolver.DotNet.Signatures public class GenericInstanceMethodSignature : CallingConventionSignature, IGenericArgumentsProvider { internal static GenericInstanceMethodSignature? FromReader( - in BlobReadContext context, + ref BlobReadContext context, ref BinaryStreamReader reader) { if (!reader.CanRead(sizeof(byte))) @@ -30,7 +30,7 @@ public class GenericInstanceMethodSignature : CallingConventionSignature, IGener var result = new GenericInstanceMethodSignature(attributes, (int) count); for (int i = 0; i < count; i++) - result.TypeArguments.Add(TypeSignature.FromReader(context, ref reader)); + result.TypeArguments.Add(TypeSignature.FromReader(ref context, ref reader)); return result; } diff --git a/src/AsmResolver.DotNet/Signatures/LocalVariablesSignature.cs b/src/AsmResolver.DotNet/Signatures/LocalVariablesSignature.cs index 068dfc4a9..fe4044d0f 100644 --- a/src/AsmResolver.DotNet/Signatures/LocalVariablesSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/LocalVariablesSignature.cs @@ -16,7 +16,7 @@ public class LocalVariablesSignature : CallingConventionSignature /// The blob reader context. /// The input stream. /// The signature. - public static LocalVariablesSignature FromReader(in BlobReadContext context, ref BinaryStreamReader reader) + public static LocalVariablesSignature FromReader(ref BlobReadContext context, ref BinaryStreamReader reader) { var result = new LocalVariablesSignature(); result.Attributes = (CallingConventionAttributes) reader.ReadByte(); @@ -28,7 +28,7 @@ public static LocalVariablesSignature FromReader(in BlobReadContext context, ref } for (int i = 0; i < count; i++) - result.VariableTypes.Add(TypeSignature.FromReader(context, ref reader)); + result.VariableTypes.Add(TypeSignature.FromReader(ref context, ref reader)); return result; } diff --git a/src/AsmResolver.DotNet/Signatures/MethodSignature.cs b/src/AsmResolver.DotNet/Signatures/MethodSignature.cs index 3600c6e32..2ec85f515 100644 --- a/src/AsmResolver.DotNet/Signatures/MethodSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/MethodSignature.cs @@ -18,7 +18,7 @@ public class MethodSignature : MethodSignatureBase /// The blob reader context. /// The blob input stream. /// The method signature. - public static MethodSignature FromReader(in BlobReadContext context, ref BinaryStreamReader reader) + public static MethodSignature FromReader(ref BlobReadContext context, ref BinaryStreamReader reader) { var result = new MethodSignature( (CallingConventionAttributes) reader.ReadByte(), @@ -37,7 +37,7 @@ public static MethodSignature FromReader(in BlobReadContext context, ref BinaryS result.GenericParameterCount = (int) genericParameterCount; } - result.ReadParametersAndReturnType(context, ref reader); + result.ReadParametersAndReturnType(ref context, ref reader); return result; } diff --git a/src/AsmResolver.DotNet/Signatures/MethodSignatureBase.cs b/src/AsmResolver.DotNet/Signatures/MethodSignatureBase.cs index 191bc2710..f3ec25f94 100644 --- a/src/AsmResolver.DotNet/Signatures/MethodSignatureBase.cs +++ b/src/AsmResolver.DotNet/Signatures/MethodSignatureBase.cs @@ -106,7 +106,7 @@ public override bool IsImportedInModule(ModuleDefinition module) /// /// The blob reader context. /// The input stream. - protected void ReadParametersAndReturnType(in BlobReadContext context, ref BinaryStreamReader reader) + protected void ReadParametersAndReturnType(ref BlobReadContext context, ref BinaryStreamReader reader) { // Parameter count. if (!reader.TryReadCompressedUInt32(out uint parameterCount)) @@ -116,14 +116,14 @@ protected void ReadParametersAndReturnType(in BlobReadContext context, ref Binar } // Return type. - ReturnType = TypeSignature.FromReader(context, ref reader); + ReturnType = TypeSignature.FromReader(ref context, ref reader); // Parameter types. _parameterTypes.Capacity = (int) parameterCount; bool sentinel = false; for (int i = 0; i < parameterCount; i++) { - var parameterType = TypeSignature.FromReader(context, ref reader); + var parameterType = TypeSignature.FromReader(ref context, ref reader); if (parameterType.ElementType == ElementType.Sentinel) { diff --git a/src/AsmResolver.DotNet/Signatures/PropertySignature.cs b/src/AsmResolver.DotNet/Signatures/PropertySignature.cs index 15b01e0d1..ebbf719fd 100644 --- a/src/AsmResolver.DotNet/Signatures/PropertySignature.cs +++ b/src/AsmResolver.DotNet/Signatures/PropertySignature.cs @@ -17,7 +17,7 @@ public class PropertySignature : MethodSignatureBase /// The blob reader context. /// The blob input stream. /// The property signature. - public static PropertySignature? FromReader(in BlobReadContext context, ref BinaryStreamReader reader) + public static PropertySignature? FromReader(ref BlobReadContext context, ref BinaryStreamReader reader) { var attributes = (CallingConventionAttributes) reader.ReadByte(); if ((attributes & CallingConventionAttributes.Property) == 0) @@ -31,7 +31,7 @@ public class PropertySignature : MethodSignatureBase context.ReaderContext.ParentModule.CorLibTypeFactory.Object, Enumerable.Empty()); - result.ReadParametersAndReturnType(context, ref reader); + result.ReadParametersAndReturnType(ref context, ref reader); return result; } diff --git a/src/AsmResolver.DotNet/Signatures/Types/ArrayTypeSignature.cs b/src/AsmResolver.DotNet/Signatures/Types/ArrayTypeSignature.cs index 081ec88e2..3edc3e299 100644 --- a/src/AsmResolver.DotNet/Signatures/Types/ArrayTypeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/Types/ArrayTypeSignature.cs @@ -69,9 +69,9 @@ public IList Dimensions get; } - internal new static ArrayTypeSignature FromReader(in BlobReadContext context, ref BinaryStreamReader reader) + internal new static ArrayTypeSignature FromReader(ref BlobReadContext context, ref BinaryStreamReader reader) { - var signature = new ArrayTypeSignature(TypeSignature.FromReader(context, ref reader)); + var signature = new ArrayTypeSignature(TypeSignature.FromReader(ref context, ref reader)); // Rank if (!reader.TryReadCompressedUInt32(out uint rank)) diff --git a/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs b/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs index 33d588a03..1db5d1093 100644 --- a/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs @@ -14,9 +14,9 @@ public class GenericInstanceTypeSignature : TypeSignature, IGenericArgumentsProv private ITypeDefOrRef _genericType; private bool _isValueType; - internal new static GenericInstanceTypeSignature FromReader(in BlobReadContext context, ref BinaryStreamReader reader) + internal new static GenericInstanceTypeSignature FromReader(ref BlobReadContext context, ref BinaryStreamReader reader) { - var genericType = TypeSignature.FromReader(context, ref reader); + var genericType = TypeSignature.FromReader(ref context, ref reader); var signature = new GenericInstanceTypeSignature(genericType.ToTypeDefOrRef(), genericType.ElementType == ElementType.ValueType); if (!reader.TryReadCompressedUInt32(out uint count)) @@ -27,7 +27,7 @@ public class GenericInstanceTypeSignature : TypeSignature, IGenericArgumentsProv signature._typeArguments.Capacity = (int) count; for (int i = 0; i < count; i++) - signature._typeArguments.Add(TypeSignature.FromReader(context, ref reader)); + signature._typeArguments.Add(TypeSignature.FromReader(ref context, ref reader)); return signature; } diff --git a/src/AsmResolver.DotNet/Signatures/Types/ITypeSignatureResolver.cs b/src/AsmResolver.DotNet/Signatures/Types/ITypeSignatureResolver.cs index 9b7faec67..4630add6d 100644 --- a/src/AsmResolver.DotNet/Signatures/Types/ITypeSignatureResolver.cs +++ b/src/AsmResolver.DotNet/Signatures/Types/ITypeSignatureResolver.cs @@ -13,7 +13,7 @@ public interface ITypeSignatureResolver /// The blob reading context the type is situated in. /// The token to resolve. /// The type. - ITypeDefOrRef ResolveToken(in BlobReadContext context, MetadataToken token); + ITypeDefOrRef ResolveToken(ref BlobReadContext context, MetadataToken token); /// /// Resolves an address to a runtime method table to a type signature. @@ -21,7 +21,7 @@ public interface ITypeSignatureResolver /// The blob reading context the type is situated in. /// The address to resolve. /// The type. - TypeSignature ResolveRuntimeType(in BlobReadContext context, nint address); + TypeSignature ResolveRuntimeType(ref BlobReadContext context, nint address); } } diff --git a/src/AsmResolver.DotNet/Signatures/Types/PhysicalTypeSignatureResolver.cs b/src/AsmResolver.DotNet/Signatures/Types/PhysicalTypeSignatureResolver.cs index b85f6b6aa..f819b3fb1 100644 --- a/src/AsmResolver.DotNet/Signatures/Types/PhysicalTypeSignatureResolver.cs +++ b/src/AsmResolver.DotNet/Signatures/Types/PhysicalTypeSignatureResolver.cs @@ -18,12 +18,12 @@ public static PhysicalTypeSignatureResolver Instance } = new(); /// - public virtual ITypeDefOrRef ResolveToken(in BlobReadContext context, MetadataToken token) + public virtual ITypeDefOrRef ResolveToken(ref BlobReadContext context, MetadataToken token) { switch (token.Table) { // Check for infinite recursion. - case TableIndex.TypeSpec when !context.TraversedTokens.Add(token): + case TableIndex.TypeSpec when !context.StepInToken(token): context.ReaderContext.BadImage("Infinite metadata loop was detected."); return InvalidTypeDefOrRef.Get(InvalidTypeSignatureError.MetadataLoop); @@ -47,7 +47,7 @@ public virtual ITypeDefOrRef ResolveToken(in BlobReadContext context, MetadataTo } /// - public virtual TypeSignature ResolveRuntimeType(in BlobReadContext context, nint address) + public virtual TypeSignature ResolveRuntimeType(ref BlobReadContext context, nint address) { throw new NotSupportedException( "Encountered an COR_ELEMENT_TYPE_INTERNAL type signature which is not supported by this " diff --git a/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs b/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs index 78ce8e2b9..e975197cd 100644 --- a/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs @@ -63,7 +63,7 @@ public abstract ElementType ElementType /// The type signature. /// Occurs when the blob reader points to an element type that is /// invalid or unsupported. - public static TypeSignature FromReader(in BlobReadContext context, ref BinaryStreamReader reader) + public static TypeSignature FromReader(ref BlobReadContext context, ref BinaryStreamReader reader) { var elementType = (ElementType) reader.ReadByte(); switch (elementType) @@ -89,16 +89,16 @@ public static TypeSignature FromReader(in BlobReadContext context, ref BinaryStr return context.ReaderContext.ParentModule.CorLibTypeFactory.FromElementType(elementType)!; case ElementType.ValueType: - return new TypeDefOrRefSignature(ReadTypeDefOrRef(context, ref reader, false), true); + return new TypeDefOrRefSignature(ReadTypeDefOrRef(ref context, ref reader, false), true); case ElementType.Class: - return new TypeDefOrRefSignature(ReadTypeDefOrRef(context, ref reader, false), false); + return new TypeDefOrRefSignature(ReadTypeDefOrRef(ref context, ref reader, false), false); case ElementType.Ptr: - return new PointerTypeSignature(FromReader(context, ref reader)); + return new PointerTypeSignature(FromReader(ref context, ref reader)); case ElementType.ByRef: - return new ByReferenceTypeSignature(FromReader(context, ref reader)); + return new ByReferenceTypeSignature(FromReader(ref context, ref reader)); case ElementType.Var: return new GenericParameterSignature(context.ReaderContext.ParentModule, @@ -111,40 +111,40 @@ public static TypeSignature FromReader(in BlobReadContext context, ref BinaryStr (int) reader.ReadCompressedUInt32()); case ElementType.Array: - return ArrayTypeSignature.FromReader(context, ref reader); + return ArrayTypeSignature.FromReader(ref context, ref reader); case ElementType.GenericInst: - return GenericInstanceTypeSignature.FromReader(context, ref reader); + return GenericInstanceTypeSignature.FromReader(ref context, ref reader); case ElementType.FnPtr: - return new FunctionPointerTypeSignature(MethodSignature.FromReader(context, ref reader)); + return new FunctionPointerTypeSignature(MethodSignature.FromReader(ref context, ref reader)); case ElementType.SzArray: - return new SzArrayTypeSignature(FromReader(context, ref reader)); + return new SzArrayTypeSignature(FromReader(ref context, ref reader)); case ElementType.CModReqD: return new CustomModifierTypeSignature( - ReadTypeDefOrRef(context, ref reader, true), + ReadTypeDefOrRef(ref context, ref reader, true), true, - FromReader(context, ref reader)); + FromReader(ref context, ref reader)); case ElementType.CModOpt: return new CustomModifierTypeSignature( - ReadTypeDefOrRef(context, ref reader, true), + ReadTypeDefOrRef(ref context, ref reader, true), false, - FromReader(context, ref reader)); + FromReader(ref context, ref reader)); case ElementType.Sentinel: return new SentinelTypeSignature(); case ElementType.Pinned: - return new PinnedTypeSignature(FromReader(context, ref reader)); + return new PinnedTypeSignature(FromReader(ref context, ref reader)); case ElementType.Boxed: - return new BoxedTypeSignature(FromReader(context, ref reader)); + return new BoxedTypeSignature(FromReader(ref context, ref reader)); case ElementType.Internal: - return context.TypeSignatureResolver.ResolveRuntimeType(context, IntPtr.Size switch + return context.TypeSignatureResolver.ResolveRuntimeType(ref context, IntPtr.Size switch { 4 => new IntPtr(reader.ReadInt32()), _ => new IntPtr(reader.ReadInt64()) @@ -163,7 +163,7 @@ public static TypeSignature FromReader(in BlobReadContext context, ref BinaryStr /// Indicates the coded index to the type is allowed to be decoded to a member in /// the type specification table. /// The decoded and resolved type definition or reference. - protected static ITypeDefOrRef ReadTypeDefOrRef(in BlobReadContext context, ref BinaryStreamReader reader, bool allowTypeSpec) + protected static ITypeDefOrRef ReadTypeDefOrRef(ref BlobReadContext context, ref BinaryStreamReader reader, bool allowTypeSpec) { if (!reader.TryReadCompressedUInt32(out uint codedIndex)) return InvalidTypeDefOrRef.Get(InvalidTypeSignatureError.BlobTooShort); @@ -179,7 +179,7 @@ protected static ITypeDefOrRef ReadTypeDefOrRef(in BlobReadContext context, ref return InvalidTypeDefOrRef.Get(InvalidTypeSignatureError.IllegalTypeSpec); } - return context.TypeSignatureResolver.ResolveToken(context, token); + return context.TypeSignatureResolver.ResolveToken(ref context, token); } /// diff --git a/test/AsmResolver.DotNet.Tests/Signatures/MethodSignatureTest.cs b/test/AsmResolver.DotNet.Tests/Signatures/MethodSignatureTest.cs index 0b271d859..ed2b51265 100644 --- a/test/AsmResolver.DotNet.Tests/Signatures/MethodSignatureTest.cs +++ b/test/AsmResolver.DotNet.Tests/Signatures/MethodSignatureTest.cs @@ -1,4 +1,11 @@ +using System; +using System.Linq; +using AsmResolver.DotNet.Code.Cil; using AsmResolver.DotNet.Signatures; +using AsmResolver.DotNet.Signatures.Types; +using AsmResolver.PE.DotNet.Cil; +using AsmResolver.PE.DotNet.Metadata.Tables.Rows; +using AsmResolver.PE.File.Headers; using Xunit; namespace AsmResolver.DotNet.Tests.Signatures From 983873b46108c5290c6e9282ba7b386faf287665 Mon Sep 17 00:00:00 2001 From: Washi Date: Sun, 27 Nov 2022 13:46:04 +0100 Subject: [PATCH 15/29] Rename BlobReadContext -> BlobReaderContext. --- .../DynamicCilOperandResolver.cs | 3 +- .../DynamicMethodHelper.cs | 4 +- .../DynamicTypeSignatureReader.cs | 2 +- .../Serialized/SerializedCustomAttribute.cs | 2 +- .../Serialized/SerializedFieldDefinition.cs | 2 +- .../Serialized/SerializedMemberReference.cs | 2 +- .../Serialized/SerializedMethodDefinition.cs | 2 +- .../SerializedMethodSpecification.cs | 2 +- .../SerializedPropertyDefinition.cs | 2 +- .../SerializedSecurityDeclaration.cs | 2 +- .../SerializedStandAloneSignature.cs | 2 +- .../Serialized/SerializedTypeSpecification.cs | 2 +- ...lobReadContext.cs => BlobReaderContext.cs} | 52 +++++++++++-------- .../Signatures/CallingConventionSignature.cs | 4 +- .../Signatures/CustomAttributeArgument.cs | 2 +- .../CustomAttributeArgumentReader.cs | 2 +- .../CustomAttributeNamedArgument.cs | 2 +- .../Signatures/CustomAttributeSignature.cs | 2 +- .../Signatures/FieldSignature.cs | 2 +- .../GenericInstanceMethodSignature.cs | 2 +- .../Signatures/LocalVariablesSignature.cs | 2 +- .../Signatures/MethodSignature.cs | 2 +- .../Signatures/MethodSignatureBase.cs | 2 +- .../Signatures/PropertySignature.cs | 2 +- .../Security/PermissionSetSignature.cs | 2 +- .../Signatures/Security/SecurityAttribute.cs | 2 +- .../SerializedCustomAttributeSignature.cs | 4 +- .../Signatures/Types/ArrayTypeSignature.cs | 2 +- .../Types/GenericInstanceTypeSignature.cs | 2 +- .../Types/ITypeSignatureResolver.cs | 4 +- .../Types/PhysicalTypeSignatureResolver.cs | 7 ++- .../Signatures/Types/TypeSignature.cs | 6 +-- 32 files changed, 72 insertions(+), 60 deletions(-) rename src/AsmResolver.DotNet/Signatures/{BlobReadContext.cs => BlobReaderContext.cs} (55%) diff --git a/src/AsmResolver.DotNet.Dynamic/DynamicCilOperandResolver.cs b/src/AsmResolver.DotNet.Dynamic/DynamicCilOperandResolver.cs index 3efbe83d1..8f4b202f4 100644 --- a/src/AsmResolver.DotNet.Dynamic/DynamicCilOperandResolver.cs +++ b/src/AsmResolver.DotNet.Dynamic/DynamicCilOperandResolver.cs @@ -96,7 +96,8 @@ public DynamicCilOperandResolver(SerializedModuleDefinition contextModule, CilMe case TableIndex.StandAloneSig: var reader = new BinaryStreamReader((byte[])_tokens[(int)token.Rid]!); - return CallingConventionSignature.FromReader(new BlobReadContext(_readerContext), ref reader); + var blobReadContext = new BlobReaderContext(_readerContext); + return CallingConventionSignature.FromReader(ref blobReadContext, ref reader); } return token; diff --git a/src/AsmResolver.DotNet.Dynamic/DynamicMethodHelper.cs b/src/AsmResolver.DotNet.Dynamic/DynamicMethodHelper.cs index 3563096b6..27e3d27c3 100644 --- a/src/AsmResolver.DotNet.Dynamic/DynamicMethodHelper.cs +++ b/src/AsmResolver.DotNet.Dynamic/DynamicMethodHelper.cs @@ -19,8 +19,8 @@ public static void ReadLocalVariables(CilMethodBody methodBody, MethodDefinition throw new ArgumentException("Method body should reference a serialized module."); var reader = new BinaryStreamReader(localSig); - var context = new BlobReadContext(module.ReaderContext, DynamicTypeSignatureResolver.Instance); - if (CallingConventionSignature.FromReader(context, ref reader) + var context = new BlobReaderContext(module.ReaderContext, DynamicTypeSignatureResolver.Instance); + if (CallingConventionSignature.FromReader(ref context, ref reader) is not LocalVariablesSignature localsSignature) { throw new ArgumentException("Invalid local variables signature."); diff --git a/src/AsmResolver.DotNet.Dynamic/DynamicTypeSignatureReader.cs b/src/AsmResolver.DotNet.Dynamic/DynamicTypeSignatureReader.cs index 9ebb00448..876ae9f4c 100644 --- a/src/AsmResolver.DotNet.Dynamic/DynamicTypeSignatureReader.cs +++ b/src/AsmResolver.DotNet.Dynamic/DynamicTypeSignatureReader.cs @@ -41,7 +41,7 @@ static DynamicTypeSignatureResolver() public static bool IsSupported => GetTypeFromHandleUnsafeMethod is not null; /// - public override TypeSignature ResolveRuntimeType(ref BlobReadContext context, nint address) + public override TypeSignature ResolveRuntimeType(ref BlobReaderContext context, nint address) { if (!IsSupported) throw new PlatformNotSupportedException("The current platform does not support the translation of raw type handles to System.Type instances."); diff --git a/src/AsmResolver.DotNet/Serialized/SerializedCustomAttribute.cs b/src/AsmResolver.DotNet/Serialized/SerializedCustomAttribute.cs index 9809a0516..6b334c1b4 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedCustomAttribute.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedCustomAttribute.cs @@ -64,7 +64,7 @@ public SerializedCustomAttribute(ModuleReaderContext context, MetadataToken toke $"Invalid signature blob index in custom attribute {MetadataToken}."); } - return CustomAttributeSignature.FromReader(new BlobReadContext(_context), Constructor, reader); + return CustomAttributeSignature.FromReader(new BlobReaderContext(_context), Constructor, reader); } } } diff --git a/src/AsmResolver.DotNet/Serialized/SerializedFieldDefinition.cs b/src/AsmResolver.DotNet/Serialized/SerializedFieldDefinition.cs index 9af8ed262..768f687fd 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedFieldDefinition.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedFieldDefinition.cs @@ -47,7 +47,7 @@ public SerializedFieldDefinition(ModuleReaderContext context, MetadataToken toke $"Invalid signature blob index in field {MetadataToken.ToString()}."); } - var context = new BlobReadContext(_context); + var context = new BlobReaderContext(_context); return FieldSignature.FromReader(ref context, ref reader); } diff --git a/src/AsmResolver.DotNet/Serialized/SerializedMemberReference.cs b/src/AsmResolver.DotNet/Serialized/SerializedMemberReference.cs index 0064bfc53..e1250020c 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedMemberReference.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedMemberReference.cs @@ -60,7 +60,7 @@ public SerializedMemberReference( $"Invalid signature blob index in member reference {MetadataToken.ToString()}."); } - var context = new BlobReadContext(_context); + var context = new BlobReaderContext(_context); return CallingConventionSignature.FromReader(ref context, ref reader, true); } diff --git a/src/AsmResolver.DotNet/Serialized/SerializedMethodDefinition.cs b/src/AsmResolver.DotNet/Serialized/SerializedMethodDefinition.cs index 4c55e60c1..42675f8ab 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedMethodDefinition.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedMethodDefinition.cs @@ -52,7 +52,7 @@ public SerializedMethodDefinition(ModuleReaderContext context, MetadataToken tok $"Invalid signature blob index in method {MetadataToken.ToString()}."); } - var context = new BlobReadContext(_context); + var context = new BlobReaderContext(_context); return MethodSignature.FromReader(ref context, ref reader); } diff --git a/src/AsmResolver.DotNet/Serialized/SerializedMethodSpecification.cs b/src/AsmResolver.DotNet/Serialized/SerializedMethodSpecification.cs index 0f8b7e3b6..7878db964 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedMethodSpecification.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedMethodSpecification.cs @@ -52,7 +52,7 @@ public SerializedMethodSpecification(ModuleReaderContext context, MetadataToken $"Invalid instantiation blob index in method specification {MetadataToken.ToString()}."); } - var context = new BlobReadContext(_context); + var context = new BlobReaderContext(_context); return GenericInstanceMethodSignature.FromReader(ref context, ref reader); } diff --git a/src/AsmResolver.DotNet/Serialized/SerializedPropertyDefinition.cs b/src/AsmResolver.DotNet/Serialized/SerializedPropertyDefinition.cs index 5f051e158..1d235e66a 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedPropertyDefinition.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedPropertyDefinition.cs @@ -48,7 +48,7 @@ public SerializedPropertyDefinition(ModuleReaderContext context, MetadataToken t $"Invalid signature blob index in property {MetadataToken.ToString()}."); } - var context = new BlobReadContext(_context); + var context = new BlobReaderContext(_context); return PropertySignature.FromReader(ref context, ref reader); } diff --git a/src/AsmResolver.DotNet/Serialized/SerializedSecurityDeclaration.cs b/src/AsmResolver.DotNet/Serialized/SerializedSecurityDeclaration.cs index c39273a75..a6dd50fd9 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedSecurityDeclaration.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedSecurityDeclaration.cs @@ -56,7 +56,7 @@ public SerializedSecurityDeclaration( $"Invalid permission set blob index in security declaration {MetadataToken.ToString()}."); } - return PermissionSetSignature.FromReader(new BlobReadContext(_context), ref reader); + return PermissionSetSignature.FromReader(new BlobReaderContext(_context), ref reader); } } } diff --git a/src/AsmResolver.DotNet/Serialized/SerializedStandAloneSignature.cs b/src/AsmResolver.DotNet/Serialized/SerializedStandAloneSignature.cs index e65329632..65e82da76 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedStandAloneSignature.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedStandAloneSignature.cs @@ -43,7 +43,7 @@ public SerializedStandAloneSignature( $"Invalid signature blob index in stand-alone signature {MetadataToken.ToString()}."); } - var context = new BlobReadContext(_context); + var context = new BlobReaderContext(_context); return CallingConventionSignature.FromReader(ref context, ref reader); } diff --git a/src/AsmResolver.DotNet/Serialized/SerializedTypeSpecification.cs b/src/AsmResolver.DotNet/Serialized/SerializedTypeSpecification.cs index 50107bbc0..4f05ed1ba 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedTypeSpecification.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedTypeSpecification.cs @@ -41,7 +41,7 @@ public SerializedTypeSpecification(ModuleReaderContext context, MetadataToken to $"Invalid blob signature for type specification {MetadataToken.ToString()}"); } - var context = new BlobReadContext(_context); + var context = new BlobReaderContext(_context); context.StepInToken(MetadataToken); return TypeSignature.FromReader(ref context, ref reader); } diff --git a/src/AsmResolver.DotNet/Signatures/BlobReadContext.cs b/src/AsmResolver.DotNet/Signatures/BlobReaderContext.cs similarity index 55% rename from src/AsmResolver.DotNet/Signatures/BlobReadContext.cs rename to src/AsmResolver.DotNet/Signatures/BlobReaderContext.cs index f95f1f94f..b9395871a 100644 --- a/src/AsmResolver.DotNet/Signatures/BlobReadContext.cs +++ b/src/AsmResolver.DotNet/Signatures/BlobReaderContext.cs @@ -1,5 +1,5 @@ +using System; using System.Collections.Generic; -using System.Linq; using AsmResolver.DotNet.Serialized; using AsmResolver.DotNet.Signatures.Types; using AsmResolver.PE.DotNet.Metadata.Tables; @@ -10,44 +10,31 @@ namespace AsmResolver.DotNet.Signatures /// Provides a context in which a metadata blob parser exists in. This includes the original module reader context /// as well as a mechanism to protect against infinite recursion. /// - public struct BlobReadContext + public struct BlobReaderContext { private Stack? _traversedTokens; /// - /// Creates a new instance of the structure. + /// Creates a new instance of the structure. /// /// The original read context. - public BlobReadContext(ModuleReaderContext readerContext) + public BlobReaderContext(ModuleReaderContext readerContext) : this(readerContext, PhysicalTypeSignatureResolver.Instance) { } /// - /// Creates a new instance of the structure. + /// Creates a new instance of the structure. /// /// The original read context. /// The object responsible for resolving raw type metadata tokens and addresses. - public BlobReadContext(ModuleReaderContext readerContext, ITypeSignatureResolver resolver) + public BlobReaderContext(ModuleReaderContext readerContext, ITypeSignatureResolver resolver) { ReaderContext = readerContext; TypeSignatureResolver = resolver; _traversedTokens = null; } - /// - /// Creates a new instance of the structure. - /// - /// The original read context. - /// The object responsible for resolving raw type metadata tokens and addresses. - /// A collection of traversed metadata tokens. - public BlobReadContext(ModuleReaderContext readerContext, ITypeSignatureResolver resolver, IEnumerable traversedTokens) - { - ReaderContext = readerContext; - TypeSignatureResolver = resolver; - _traversedTokens = new Stack(traversedTokens); - } - /// /// Gets the module reader context. /// @@ -64,15 +51,36 @@ public ITypeSignatureResolver TypeSignatureResolver get; } + /// + /// Records a step in the blob reading process where a metadata token into the tables stream is about to + /// be traversed. + /// + /// The token to traverse + /// + /// true if this token was recorded, false if the token was already traversed before. + /// public bool StepInToken(MetadataToken token) { - _traversedTokens ??= new Stack(); - - if (_traversedTokens.Contains(token)) + if (_traversedTokens is null) + _traversedTokens = new Stack(); + else if (_traversedTokens.Contains(token)) return false; _traversedTokens.Push(token); return true; } + + /// + /// Records a step in the blob reading process where the last recorded metadata token into the tables stream + /// was traversed and processed completely. + /// + /// Occurs when there was no token traversed. + public void StepOutToken() + { + if (_traversedTokens is null) + throw new InvalidOperationException(); + + _traversedTokens.Pop(); + } } } diff --git a/src/AsmResolver.DotNet/Signatures/CallingConventionSignature.cs b/src/AsmResolver.DotNet/Signatures/CallingConventionSignature.cs index 563ab5194..e58903d30 100644 --- a/src/AsmResolver.DotNet/Signatures/CallingConventionSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/CallingConventionSignature.cs @@ -20,7 +20,7 @@ public abstract class CallingConventionSignature : ExtendableBlobSignature, IImp /// put into the property. /// The read signature. public static CallingConventionSignature? FromReader( - ref BlobReadContext context, + ref BlobReaderContext context, ref BinaryStreamReader reader, bool readToEnd = true) { @@ -35,7 +35,7 @@ public abstract class CallingConventionSignature : ExtendableBlobSignature, IImp return signature; } - private static CallingConventionSignature? ReadSignature(ref BlobReadContext context, ref BinaryStreamReader reader) + private static CallingConventionSignature? ReadSignature(ref BlobReaderContext context, ref BinaryStreamReader reader) { byte flag = reader.ReadByte(); reader.Offset--; diff --git a/src/AsmResolver.DotNet/Signatures/CustomAttributeArgument.cs b/src/AsmResolver.DotNet/Signatures/CustomAttributeArgument.cs index 58823dd1b..8683f8925 100644 --- a/src/AsmResolver.DotNet/Signatures/CustomAttributeArgument.cs +++ b/src/AsmResolver.DotNet/Signatures/CustomAttributeArgument.cs @@ -19,7 +19,7 @@ public class CustomAttributeArgument /// The type of the argument to read. /// The input stream. /// The argument. - public static CustomAttributeArgument FromReader(in BlobReadContext context, TypeSignature argumentType, + public static CustomAttributeArgument FromReader(in BlobReaderContext context, TypeSignature argumentType, ref BinaryStreamReader reader) { var elementReader = CustomAttributeArgumentReader.Create(); diff --git a/src/AsmResolver.DotNet/Signatures/CustomAttributeArgumentReader.cs b/src/AsmResolver.DotNet/Signatures/CustomAttributeArgumentReader.cs index 246f8a27f..4ace31c1d 100644 --- a/src/AsmResolver.DotNet/Signatures/CustomAttributeArgumentReader.cs +++ b/src/AsmResolver.DotNet/Signatures/CustomAttributeArgumentReader.cs @@ -30,7 +30,7 @@ public bool IsNullArray private set; } - public void ReadValue(in BlobReadContext context, ref BinaryStreamReader reader, TypeSignature valueType) + public void ReadValue(in BlobReaderContext context, ref BinaryStreamReader reader, TypeSignature valueType) { var module = context.ReaderContext.ParentModule; diff --git a/src/AsmResolver.DotNet/Signatures/CustomAttributeNamedArgument.cs b/src/AsmResolver.DotNet/Signatures/CustomAttributeNamedArgument.cs index 770af083e..6d4e08f62 100644 --- a/src/AsmResolver.DotNet/Signatures/CustomAttributeNamedArgument.cs +++ b/src/AsmResolver.DotNet/Signatures/CustomAttributeNamedArgument.cs @@ -69,7 +69,7 @@ public CustomAttributeArgument Argument /// The blob reader context. /// The input stream. /// The argument. - public static CustomAttributeNamedArgument FromReader(in BlobReadContext context, ref BinaryStreamReader reader) + public static CustomAttributeNamedArgument FromReader(in BlobReaderContext context, ref BinaryStreamReader reader) { var memberType = (CustomAttributeArgumentMemberType) reader.ReadByte(); var argumentType = TypeSignature.ReadFieldOrPropType(context, ref reader); diff --git a/src/AsmResolver.DotNet/Signatures/CustomAttributeSignature.cs b/src/AsmResolver.DotNet/Signatures/CustomAttributeSignature.cs index 8e645704a..1a399f616 100644 --- a/src/AsmResolver.DotNet/Signatures/CustomAttributeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/CustomAttributeSignature.cs @@ -86,7 +86,7 @@ public IList NamedArguments /// The signature. /// Occurs when the input stream does not point to a valid signature. public static CustomAttributeSignature FromReader( - in BlobReadContext context, + in BlobReaderContext context, ICustomAttributeType ctor, in BinaryStreamReader reader) { diff --git a/src/AsmResolver.DotNet/Signatures/FieldSignature.cs b/src/AsmResolver.DotNet/Signatures/FieldSignature.cs index 581d58553..971839ab8 100644 --- a/src/AsmResolver.DotNet/Signatures/FieldSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/FieldSignature.cs @@ -37,7 +37,7 @@ public static FieldSignature CreateInstance(TypeSignature fieldType) /// The blob reader context. /// The blob input stream. /// The field signature. - public static FieldSignature FromReader(ref BlobReadContext context, ref BinaryStreamReader reader) + public static FieldSignature FromReader(ref BlobReaderContext context, ref BinaryStreamReader reader) { return new( (CallingConventionAttributes) reader.ReadByte(), diff --git a/src/AsmResolver.DotNet/Signatures/GenericInstanceMethodSignature.cs b/src/AsmResolver.DotNet/Signatures/GenericInstanceMethodSignature.cs index 23f9ec947..9e12333c8 100644 --- a/src/AsmResolver.DotNet/Signatures/GenericInstanceMethodSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/GenericInstanceMethodSignature.cs @@ -11,7 +11,7 @@ namespace AsmResolver.DotNet.Signatures public class GenericInstanceMethodSignature : CallingConventionSignature, IGenericArgumentsProvider { internal static GenericInstanceMethodSignature? FromReader( - ref BlobReadContext context, + ref BlobReaderContext context, ref BinaryStreamReader reader) { if (!reader.CanRead(sizeof(byte))) diff --git a/src/AsmResolver.DotNet/Signatures/LocalVariablesSignature.cs b/src/AsmResolver.DotNet/Signatures/LocalVariablesSignature.cs index fe4044d0f..5f0e56597 100644 --- a/src/AsmResolver.DotNet/Signatures/LocalVariablesSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/LocalVariablesSignature.cs @@ -16,7 +16,7 @@ public class LocalVariablesSignature : CallingConventionSignature /// The blob reader context. /// The input stream. /// The signature. - public static LocalVariablesSignature FromReader(ref BlobReadContext context, ref BinaryStreamReader reader) + public static LocalVariablesSignature FromReader(ref BlobReaderContext context, ref BinaryStreamReader reader) { var result = new LocalVariablesSignature(); result.Attributes = (CallingConventionAttributes) reader.ReadByte(); diff --git a/src/AsmResolver.DotNet/Signatures/MethodSignature.cs b/src/AsmResolver.DotNet/Signatures/MethodSignature.cs index 2ec85f515..0651a80fb 100644 --- a/src/AsmResolver.DotNet/Signatures/MethodSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/MethodSignature.cs @@ -18,7 +18,7 @@ public class MethodSignature : MethodSignatureBase /// The blob reader context. /// The blob input stream. /// The method signature. - public static MethodSignature FromReader(ref BlobReadContext context, ref BinaryStreamReader reader) + public static MethodSignature FromReader(ref BlobReaderContext context, ref BinaryStreamReader reader) { var result = new MethodSignature( (CallingConventionAttributes) reader.ReadByte(), diff --git a/src/AsmResolver.DotNet/Signatures/MethodSignatureBase.cs b/src/AsmResolver.DotNet/Signatures/MethodSignatureBase.cs index f3ec25f94..4da04a96b 100644 --- a/src/AsmResolver.DotNet/Signatures/MethodSignatureBase.cs +++ b/src/AsmResolver.DotNet/Signatures/MethodSignatureBase.cs @@ -106,7 +106,7 @@ public override bool IsImportedInModule(ModuleDefinition module) /// /// The blob reader context. /// The input stream. - protected void ReadParametersAndReturnType(ref BlobReadContext context, ref BinaryStreamReader reader) + protected void ReadParametersAndReturnType(ref BlobReaderContext context, ref BinaryStreamReader reader) { // Parameter count. if (!reader.TryReadCompressedUInt32(out uint parameterCount)) diff --git a/src/AsmResolver.DotNet/Signatures/PropertySignature.cs b/src/AsmResolver.DotNet/Signatures/PropertySignature.cs index ebbf719fd..7da4f39c3 100644 --- a/src/AsmResolver.DotNet/Signatures/PropertySignature.cs +++ b/src/AsmResolver.DotNet/Signatures/PropertySignature.cs @@ -17,7 +17,7 @@ public class PropertySignature : MethodSignatureBase /// The blob reader context. /// The blob input stream. /// The property signature. - public static PropertySignature? FromReader(ref BlobReadContext context, ref BinaryStreamReader reader) + public static PropertySignature? FromReader(ref BlobReaderContext context, ref BinaryStreamReader reader) { var attributes = (CallingConventionAttributes) reader.ReadByte(); if ((attributes & CallingConventionAttributes.Property) == 0) diff --git a/src/AsmResolver.DotNet/Signatures/Security/PermissionSetSignature.cs b/src/AsmResolver.DotNet/Signatures/Security/PermissionSetSignature.cs index 347365357..9ccbcfb75 100644 --- a/src/AsmResolver.DotNet/Signatures/Security/PermissionSetSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/Security/PermissionSetSignature.cs @@ -14,7 +14,7 @@ public class PermissionSetSignature : ExtendableBlobSignature /// The blob reader context. /// The input blob stream. /// The permission set. - public static PermissionSetSignature FromReader(in BlobReadContext context, ref BinaryStreamReader reader) + public static PermissionSetSignature FromReader(in BlobReaderContext context, ref BinaryStreamReader reader) { var result = new PermissionSetSignature(); if (reader.ReadByte() != '.') diff --git a/src/AsmResolver.DotNet/Signatures/Security/SecurityAttribute.cs b/src/AsmResolver.DotNet/Signatures/Security/SecurityAttribute.cs index 92ea1cef5..abb55b287 100644 --- a/src/AsmResolver.DotNet/Signatures/Security/SecurityAttribute.cs +++ b/src/AsmResolver.DotNet/Signatures/Security/SecurityAttribute.cs @@ -17,7 +17,7 @@ public class SecurityAttribute /// The blob reader context. /// The input blob stream. /// The security attribute. - public static SecurityAttribute FromReader(in BlobReadContext context, ref BinaryStreamReader reader) + public static SecurityAttribute FromReader(in BlobReaderContext context, ref BinaryStreamReader reader) { string? typeName = reader.ReadSerString(); var type = string.IsNullOrEmpty(typeName) diff --git a/src/AsmResolver.DotNet/Signatures/SerializedCustomAttributeSignature.cs b/src/AsmResolver.DotNet/Signatures/SerializedCustomAttributeSignature.cs index 3a841170a..a7e6e6028 100644 --- a/src/AsmResolver.DotNet/Signatures/SerializedCustomAttributeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/SerializedCustomAttributeSignature.cs @@ -10,7 +10,7 @@ namespace AsmResolver.DotNet.Signatures /// public class SerializedCustomAttributeSignature : CustomAttributeSignature { - private readonly BlobReadContext _context; + private readonly BlobReaderContext _context; private readonly TypeSignature[] _fixedArgTypes; private readonly BinaryStreamReader _reader; @@ -21,7 +21,7 @@ public class SerializedCustomAttributeSignature : CustomAttributeSignature /// The types of all fixed arguments. /// The input blob reader. public SerializedCustomAttributeSignature( - in BlobReadContext context, + in BlobReaderContext context, IEnumerable fixedArgTypes, in BinaryStreamReader reader) { diff --git a/src/AsmResolver.DotNet/Signatures/Types/ArrayTypeSignature.cs b/src/AsmResolver.DotNet/Signatures/Types/ArrayTypeSignature.cs index 3edc3e299..682ec1f4e 100644 --- a/src/AsmResolver.DotNet/Signatures/Types/ArrayTypeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/Types/ArrayTypeSignature.cs @@ -69,7 +69,7 @@ public IList Dimensions get; } - internal new static ArrayTypeSignature FromReader(ref BlobReadContext context, ref BinaryStreamReader reader) + internal new static ArrayTypeSignature FromReader(ref BlobReaderContext context, ref BinaryStreamReader reader) { var signature = new ArrayTypeSignature(TypeSignature.FromReader(ref context, ref reader)); diff --git a/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs b/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs index 1db5d1093..67045f1da 100644 --- a/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs @@ -14,7 +14,7 @@ public class GenericInstanceTypeSignature : TypeSignature, IGenericArgumentsProv private ITypeDefOrRef _genericType; private bool _isValueType; - internal new static GenericInstanceTypeSignature FromReader(ref BlobReadContext context, ref BinaryStreamReader reader) + internal new static GenericInstanceTypeSignature FromReader(ref BlobReaderContext context, ref BinaryStreamReader reader) { var genericType = TypeSignature.FromReader(ref context, ref reader); var signature = new GenericInstanceTypeSignature(genericType.ToTypeDefOrRef(), genericType.ElementType == ElementType.ValueType); diff --git a/src/AsmResolver.DotNet/Signatures/Types/ITypeSignatureResolver.cs b/src/AsmResolver.DotNet/Signatures/Types/ITypeSignatureResolver.cs index 4630add6d..5e67b8f34 100644 --- a/src/AsmResolver.DotNet/Signatures/Types/ITypeSignatureResolver.cs +++ b/src/AsmResolver.DotNet/Signatures/Types/ITypeSignatureResolver.cs @@ -13,7 +13,7 @@ public interface ITypeSignatureResolver /// The blob reading context the type is situated in. /// The token to resolve. /// The type. - ITypeDefOrRef ResolveToken(ref BlobReadContext context, MetadataToken token); + ITypeDefOrRef ResolveToken(ref BlobReaderContext context, MetadataToken token); /// /// Resolves an address to a runtime method table to a type signature. @@ -21,7 +21,7 @@ public interface ITypeSignatureResolver /// The blob reading context the type is situated in. /// The address to resolve. /// The type. - TypeSignature ResolveRuntimeType(ref BlobReadContext context, nint address); + TypeSignature ResolveRuntimeType(ref BlobReaderContext context, nint address); } } diff --git a/src/AsmResolver.DotNet/Signatures/Types/PhysicalTypeSignatureResolver.cs b/src/AsmResolver.DotNet/Signatures/Types/PhysicalTypeSignatureResolver.cs index f819b3fb1..a934931e8 100644 --- a/src/AsmResolver.DotNet/Signatures/Types/PhysicalTypeSignatureResolver.cs +++ b/src/AsmResolver.DotNet/Signatures/Types/PhysicalTypeSignatureResolver.cs @@ -18,7 +18,7 @@ public static PhysicalTypeSignatureResolver Instance } = new(); /// - public virtual ITypeDefOrRef ResolveToken(ref BlobReadContext context, MetadataToken token) + public virtual ITypeDefOrRef ResolveToken(ref BlobReaderContext context, MetadataToken token) { switch (token.Table) { @@ -34,6 +34,9 @@ public virtual ITypeDefOrRef ResolveToken(ref BlobReadContext context, MetadataT if (context.ReaderContext.ParentModule.TryLookupMember(token, out var member) && member is ITypeDefOrRef typeDefOrRef) { + if (token.Table == TableIndex.TypeSpec) + context.StepOutToken(); + return typeDefOrRef; } @@ -47,7 +50,7 @@ public virtual ITypeDefOrRef ResolveToken(ref BlobReadContext context, MetadataT } /// - public virtual TypeSignature ResolveRuntimeType(ref BlobReadContext context, nint address) + public virtual TypeSignature ResolveRuntimeType(ref BlobReaderContext context, nint address) { throw new NotSupportedException( "Encountered an COR_ELEMENT_TYPE_INTERNAL type signature which is not supported by this " diff --git a/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs b/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs index e975197cd..161c5978c 100644 --- a/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs @@ -63,7 +63,7 @@ public abstract ElementType ElementType /// The type signature. /// Occurs when the blob reader points to an element type that is /// invalid or unsupported. - public static TypeSignature FromReader(ref BlobReadContext context, ref BinaryStreamReader reader) + public static TypeSignature FromReader(ref BlobReaderContext context, ref BinaryStreamReader reader) { var elementType = (ElementType) reader.ReadByte(); switch (elementType) @@ -163,7 +163,7 @@ public static TypeSignature FromReader(ref BlobReadContext context, ref BinarySt /// Indicates the coded index to the type is allowed to be decoded to a member in /// the type specification table. /// The decoded and resolved type definition or reference. - protected static ITypeDefOrRef ReadTypeDefOrRef(ref BlobReadContext context, ref BinaryStreamReader reader, bool allowTypeSpec) + protected static ITypeDefOrRef ReadTypeDefOrRef(ref BlobReaderContext context, ref BinaryStreamReader reader, bool allowTypeSpec) { if (!reader.TryReadCompressedUInt32(out uint codedIndex)) return InvalidTypeDefOrRef.Get(InvalidTypeSignatureError.BlobTooShort); @@ -206,7 +206,7 @@ protected void WriteTypeDefOrRef(BlobSerializationContext context, ITypeDefOrRef context.Writer.WriteCompressedUInt32(index); } - internal static TypeSignature ReadFieldOrPropType(in BlobReadContext context, ref BinaryStreamReader reader) + internal static TypeSignature ReadFieldOrPropType(in BlobReaderContext context, ref BinaryStreamReader reader) { var module = context.ReaderContext.ParentModule; From baf25ac0e3881e31698f0367a8ea03ce4fc0e652 Mon Sep 17 00:00:00 2001 From: Washi Date: Sun, 27 Nov 2022 14:23:03 +0100 Subject: [PATCH 16/29] Add IsTypeOfUtf8 as well as Utf8String versions of Name properties to avoid unnecessary string allocations during member resolution. --- .../DefaultMetadataResolver.cs | 30 +++++++++++-------- src/AsmResolver.DotNet/IFieldDescriptor.cs | 8 +++++ src/AsmResolver.DotNet/IMethodDescriptor.cs | 8 +++++ .../TypeDescriptorExtensions.cs | 22 ++++++++++++++ 4 files changed, 55 insertions(+), 13 deletions(-) diff --git a/src/AsmResolver.DotNet/DefaultMetadataResolver.cs b/src/AsmResolver.DotNet/DefaultMetadataResolver.cs index fcfc69b8b..bd3030090 100644 --- a/src/AsmResolver.DotNet/DefaultMetadataResolver.cs +++ b/src/AsmResolver.DotNet/DefaultMetadataResolver.cs @@ -81,12 +81,12 @@ public IAssemblyResolver AssemblyResolver return null; var resolvedType = LookupInCache(exportedType); - if (resolvedType != null) + if (resolvedType is not null) return resolvedType; var resolution = new TypeResolution(AssemblyResolver); resolvedType = resolution.ResolveExportedType(exportedType); - if (resolvedType != null) + if (resolvedType is not null) _typeCache[exportedType] = resolvedType; return resolvedType; @@ -137,6 +137,10 @@ public IAssemblyResolver AssemblyResolver if (declaringType is null) return null; + var name = field is MemberReference member + ? member.Name + : (Utf8String?) field.Name; + for (int i = 0; i < declaringType.Fields.Count; i++) { var candidate = declaringType.Fields[i]; @@ -171,7 +175,7 @@ public TypeResolution(IAssemblyResolver resolver) { case TableIndex.AssemblyRef: var assemblyDefScope = _assemblyResolver.Resolve((AssemblyReference) scope); - return assemblyDefScope != null + return assemblyDefScope is not null ? FindTypeInAssembly(assemblyDefScope, reference.Namespace, reference.Name) : null; @@ -180,7 +184,7 @@ public TypeResolution(IAssemblyResolver resolver) case TableIndex.TypeRef: var typeDefScope = ResolveTypeReference((TypeReference) scope); - return typeDefScope != null + return typeDefScope is not null ? FindTypeInType(typeDefScope, reference.Name) : null; @@ -200,20 +204,20 @@ public TypeResolution(IAssemblyResolver resolver) { case TableIndex.AssemblyRef: var assembly = _assemblyResolver.Resolve((AssemblyReference) implementation); - return assembly is {} + return assembly is not null ? FindTypeInAssembly(assembly, exportedType.Namespace, exportedType.Name) : null; case TableIndex.File when !string.IsNullOrEmpty(implementation.Name): var module = FindModuleInAssembly(exportedType.Module!.Assembly!, implementation.Name!); - return module is {} + return module is not null ? FindTypeInModule(module, exportedType.Namespace, exportedType.Name) : null; case TableIndex.ExportedType: var exportedDeclaringType = (ExportedType) implementation; var declaringType = ResolveExportedType(exportedDeclaringType); - return declaringType is {} + return declaringType is not null ? FindTypeInType(declaringType, exportedType.Name) : null; @@ -228,33 +232,33 @@ public TypeResolution(IAssemblyResolver resolver) { var module = assembly.Modules[i]; var type = FindTypeInModule(module, ns, name); - if (type != null) + if (type is not null) return type; } return null; } - private TypeDefinition? FindTypeInModule(ModuleDefinition module, string? ns, string name) + private TypeDefinition? FindTypeInModule(ModuleDefinition module, Utf8String? ns, Utf8String name) { for (int i = 0; i < module.ExportedTypes.Count; i++) { var exportedType = module.ExportedTypes[i]; - if (exportedType.IsTypeOf(ns, name)) + if (exportedType.IsTypeOfUtf8(ns, name)) return ResolveExportedType(exportedType); } for (int i = 0; i < module.TopLevelTypes.Count; i++) { var type = module.TopLevelTypes[i]; - if (type.IsTypeOf(ns, name)) + if (type.IsTypeOfUtf8(ns, name)) return type; } return null; } - private static TypeDefinition? FindTypeInType(TypeDefinition enclosingType, string name) + private static TypeDefinition? FindTypeInType(TypeDefinition enclosingType, Utf8String name) { for (int i = 0; i < enclosingType.NestedTypes.Count; i++) { @@ -266,7 +270,7 @@ public TypeResolution(IAssemblyResolver resolver) return null; } - private static ModuleDefinition? FindModuleInAssembly(AssemblyDefinition assembly, string name) + private static ModuleDefinition? FindModuleInAssembly(AssemblyDefinition assembly, Utf8String name) { for (int i = 0; i < assembly.Modules.Count; i++) { diff --git a/src/AsmResolver.DotNet/IFieldDescriptor.cs b/src/AsmResolver.DotNet/IFieldDescriptor.cs index 18143ac19..8ed9a5ebf 100644 --- a/src/AsmResolver.DotNet/IFieldDescriptor.cs +++ b/src/AsmResolver.DotNet/IFieldDescriptor.cs @@ -7,6 +7,14 @@ namespace AsmResolver.DotNet /// public interface IFieldDescriptor : IMemberDescriptor, IMetadataMember { + /// + /// Gets the name of the field. + /// + new Utf8String Name + { + get; + } + /// /// Gets the signature of the field. /// diff --git a/src/AsmResolver.DotNet/IMethodDescriptor.cs b/src/AsmResolver.DotNet/IMethodDescriptor.cs index 4f695c8f9..d9cf2085c 100644 --- a/src/AsmResolver.DotNet/IMethodDescriptor.cs +++ b/src/AsmResolver.DotNet/IMethodDescriptor.cs @@ -7,6 +7,14 @@ namespace AsmResolver.DotNet /// public interface IMethodDescriptor : IMemberDescriptor, IMetadataMember { + /// + /// Gets the name of the method. + /// + new Utf8String Name + { + get; + } + /// /// Gets the signature of the method. /// diff --git a/src/AsmResolver.DotNet/TypeDescriptorExtensions.cs b/src/AsmResolver.DotNet/TypeDescriptorExtensions.cs index 360ad9f54..5b586f18d 100644 --- a/src/AsmResolver.DotNet/TypeDescriptorExtensions.cs +++ b/src/AsmResolver.DotNet/TypeDescriptorExtensions.cs @@ -20,6 +20,28 @@ public static class TypeDescriptorExtensions public static bool IsTypeOf(this ITypeDescriptor type, string? ns, string? name) => type.Name == name && type.Namespace == ns; + /// + /// Determines whether a type matches a namespace and name pair. + /// + /// The type. + /// The namespace. + /// The name. + /// true if the name and the namespace of the type matches the provided values, + /// false otherwise. + public static bool IsTypeOfUtf8(this ITypeDefOrRef type, Utf8String? ns, Utf8String? name) => + type.Name == name && type.Namespace == ns; + + /// + /// Determines whether a type matches a namespace and name pair. + /// + /// The type. + /// The namespace. + /// The name. + /// true if the name and the namespace of the type matches the provided values, + /// false otherwise. + public static bool IsTypeOfUtf8(this ExportedType type, Utf8String? ns, Utf8String? name) => + type.Name == name && type.Namespace == ns; + /// /// Constructs a new single-dimension, zero based array signature with the provided type descriptor /// as element type. From 533a9117b99e53ca521e30289e798ad2d67adff1 Mon Sep 17 00:00:00 2001 From: Washi Date: Sun, 27 Nov 2022 14:30:27 +0100 Subject: [PATCH 17/29] Remove redundant GetBytesUnsafe call. --- .../Builder/Metadata/Strings/StringsStreamBlob.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AsmResolver.DotNet/Builder/Metadata/Strings/StringsStreamBlob.cs b/src/AsmResolver.DotNet/Builder/Metadata/Strings/StringsStreamBlob.cs index f048ba4c8..9dcc1f167 100644 --- a/src/AsmResolver.DotNet/Builder/Metadata/Strings/StringsStreamBlob.cs +++ b/src/AsmResolver.DotNet/Builder/Metadata/Strings/StringsStreamBlob.cs @@ -6,7 +6,7 @@ namespace AsmResolver.DotNet.Builder.Metadata.Strings { public StringsStreamBlob(Utf8String blob, bool isFixed) { - Blob = blob.GetBytesUnsafe(); + Blob = blob; Flags = isFixed ? StringsStreamBlobFlags.ZeroTerminated | StringsStreamBlobFlags.Fixed : StringsStreamBlobFlags.ZeroTerminated; From 5d8cb885873d58c9af1a9f62a42e45068e4ce704 Mon Sep 17 00:00:00 2001 From: Washi Date: Sun, 27 Nov 2022 14:41:43 +0100 Subject: [PATCH 18/29] Replace string parameters in constructors with Utf8String parameters. --- src/AsmResolver.DotNet/AssemblyDefinition.cs | 10 ++++------ src/AsmResolver.DotNet/AssemblyReference.cs | 4 ++-- src/AsmResolver.DotNet/DefaultMetadataResolver.cs | 2 +- src/AsmResolver.DotNet/EventDefinition.cs | 2 +- src/AsmResolver.DotNet/FieldDefinition.cs | 4 ++-- src/AsmResolver.DotNet/FileReference.cs | 2 +- src/AsmResolver.DotNet/GenericParameter.cs | 4 ++-- src/AsmResolver.DotNet/IFieldDescriptor.cs | 2 +- src/AsmResolver.DotNet/IMethodDescriptor.cs | 2 +- src/AsmResolver.DotNet/ImplementationMap.cs | 2 +- src/AsmResolver.DotNet/ManifestResource.cs | 4 ++-- src/AsmResolver.DotNet/MemberReference.cs | 2 +- src/AsmResolver.DotNet/MethodDefinition.cs | 2 +- src/AsmResolver.DotNet/ModuleDefinition.cs | 2 +- src/AsmResolver.DotNet/ModuleReference.cs | 2 +- src/AsmResolver.DotNet/ParameterDefinition.cs | 4 ++-- src/AsmResolver.DotNet/PropertyDefinition.cs | 2 +- src/AsmResolver.DotNet/TypeDefinition.cs | 4 ++-- src/AsmResolver.DotNet/TypeReference.cs | 4 ++-- 19 files changed, 29 insertions(+), 31 deletions(-) diff --git a/src/AsmResolver.DotNet/AssemblyDefinition.cs b/src/AsmResolver.DotNet/AssemblyDefinition.cs index fabe500a6..e379cf590 100644 --- a/src/AsmResolver.DotNet/AssemblyDefinition.cs +++ b/src/AsmResolver.DotNet/AssemblyDefinition.cs @@ -106,7 +106,7 @@ protected AssemblyDefinition(MetadataToken token) /// /// The name of the assembly. /// The version of the assembly. - public AssemblyDefinition(string? name, Version version) + public AssemblyDefinition(Utf8String? name, Version version) : this(new MetadataToken(TableIndex.Assembly, 0)) { Name = name; @@ -290,11 +290,9 @@ public void Write(string filePath, IPEImageBuilder imageBuilder, IPEFileBuilder for (int i = 0; i < Modules.Count; i++) { var module = Modules[i]; - string modulePath; - if (module == ManifestModule) - modulePath = filePath; - else - modulePath = Path.Combine(directory, module.Name ?? $"module{i}.bin"); + string modulePath = module == ManifestModule + ? filePath + : Path.Combine(directory, module.Name?.Value ?? $"module{i}.bin"); module.Write(modulePath, imageBuilder, fileBuilder); } diff --git a/src/AsmResolver.DotNet/AssemblyReference.cs b/src/AsmResolver.DotNet/AssemblyReference.cs index c255427de..d4432896c 100644 --- a/src/AsmResolver.DotNet/AssemblyReference.cs +++ b/src/AsmResolver.DotNet/AssemblyReference.cs @@ -35,7 +35,7 @@ protected AssemblyReference(MetadataToken token) /// /// The name of the assembly. /// The version of the assembly. - public AssemblyReference(string? name, Version version) + public AssemblyReference(Utf8String? name, Version version) : this(new MetadataToken(TableIndex.AssemblyRef, 0)) { Name = name; @@ -51,7 +51,7 @@ public AssemblyReference(string? name, Version version) /// unhashed public key used to verify the authenticity of the assembly. /// Indicates the public key or token (depending on ), /// used to verify the authenticity of the assembly. - public AssemblyReference(string? name, Version version, bool publicKey, byte[]? publicKeyOrToken) + public AssemblyReference(Utf8String? name, Version version, bool publicKey, byte[]? publicKeyOrToken) : this(new MetadataToken(TableIndex.AssemblyRef, 0)) { Name = name; diff --git a/src/AsmResolver.DotNet/DefaultMetadataResolver.cs b/src/AsmResolver.DotNet/DefaultMetadataResolver.cs index bd3030090..0d625ed02 100644 --- a/src/AsmResolver.DotNet/DefaultMetadataResolver.cs +++ b/src/AsmResolver.DotNet/DefaultMetadataResolver.cs @@ -226,7 +226,7 @@ public TypeResolution(IAssemblyResolver resolver) } } - private TypeDefinition? FindTypeInAssembly(AssemblyDefinition assembly, string? ns, string name) + private TypeDefinition? FindTypeInAssembly(AssemblyDefinition assembly, Utf8String? ns, Utf8String name) { for (int i = 0; i < assembly.Modules.Count; i++) { diff --git a/src/AsmResolver.DotNet/EventDefinition.cs b/src/AsmResolver.DotNet/EventDefinition.cs index d21d1036b..02b84bc36 100644 --- a/src/AsmResolver.DotNet/EventDefinition.cs +++ b/src/AsmResolver.DotNet/EventDefinition.cs @@ -42,7 +42,7 @@ protected EventDefinition(MetadataToken token) /// The name of the property. /// The attributes. /// The delegate type of the event. - public EventDefinition(string? name, EventAttributes attributes, ITypeDefOrRef? eventType) + public EventDefinition(Utf8String? name, EventAttributes attributes, ITypeDefOrRef? eventType) : this(new MetadataToken(TableIndex.Event,0)) { Name = name; diff --git a/src/AsmResolver.DotNet/FieldDefinition.cs b/src/AsmResolver.DotNet/FieldDefinition.cs index 39b7e5c3e..5f1f8d345 100644 --- a/src/AsmResolver.DotNet/FieldDefinition.cs +++ b/src/AsmResolver.DotNet/FieldDefinition.cs @@ -57,7 +57,7 @@ protected FieldDefinition(MetadataToken token) /// The name of the field. /// The attributes. /// The signature of the field. - public FieldDefinition(string? name, FieldAttributes attributes, FieldSignature? signature) + public FieldDefinition(Utf8String? name, FieldAttributes attributes, FieldSignature? signature) : this(new MetadataToken(TableIndex.Field, 0)) { Name = name; @@ -71,7 +71,7 @@ public FieldDefinition(string? name, FieldAttributes attributes, FieldSignature? /// The name of the field. /// The attributes. /// The type of values the field contains. - public FieldDefinition(string? name, FieldAttributes attributes, TypeSignature? fieldType) + public FieldDefinition(Utf8String name, FieldAttributes attributes, TypeSignature? fieldType) : this(new MetadataToken(TableIndex.Field, 0)) { Name = name; diff --git a/src/AsmResolver.DotNet/FileReference.cs b/src/AsmResolver.DotNet/FileReference.cs index 8f6078e0e..48401e611 100644 --- a/src/AsmResolver.DotNet/FileReference.cs +++ b/src/AsmResolver.DotNet/FileReference.cs @@ -35,7 +35,7 @@ protected FileReference(MetadataToken token) /// /// The name of the file. /// The attributes associated to the reference. - public FileReference(string? name, FileAttributes attributes) + public FileReference(Utf8String? name, FileAttributes attributes) : this(new MetadataToken(TableIndex.File, 0)) { Name = name; diff --git a/src/AsmResolver.DotNet/GenericParameter.cs b/src/AsmResolver.DotNet/GenericParameter.cs index cb4d1390a..a4021ca53 100644 --- a/src/AsmResolver.DotNet/GenericParameter.cs +++ b/src/AsmResolver.DotNet/GenericParameter.cs @@ -36,7 +36,7 @@ protected GenericParameter(MetadataToken token) /// Creates a new generic parameter. /// /// The name of the parameter. - public GenericParameter(string? name) + public GenericParameter(Utf8String? name) : this(new MetadataToken(TableIndex.GenericParam, 0)) { Name = name; @@ -47,7 +47,7 @@ public GenericParameter(string? name) /// /// The name of the parameter. /// Additional attributes to assign to the parameter. - public GenericParameter(string? name, GenericParameterAttributes attributes) + public GenericParameter(Utf8String? name, GenericParameterAttributes attributes) : this(new MetadataToken(TableIndex.GenericParam, 0)) { Name = name; diff --git a/src/AsmResolver.DotNet/IFieldDescriptor.cs b/src/AsmResolver.DotNet/IFieldDescriptor.cs index 8ed9a5ebf..233b45d02 100644 --- a/src/AsmResolver.DotNet/IFieldDescriptor.cs +++ b/src/AsmResolver.DotNet/IFieldDescriptor.cs @@ -10,7 +10,7 @@ public interface IFieldDescriptor : IMemberDescriptor, IMetadataMember /// /// Gets the name of the field. /// - new Utf8String Name + new Utf8String? Name { get; } diff --git a/src/AsmResolver.DotNet/IMethodDescriptor.cs b/src/AsmResolver.DotNet/IMethodDescriptor.cs index d9cf2085c..80b4485d0 100644 --- a/src/AsmResolver.DotNet/IMethodDescriptor.cs +++ b/src/AsmResolver.DotNet/IMethodDescriptor.cs @@ -10,7 +10,7 @@ public interface IMethodDescriptor : IMemberDescriptor, IMetadataMember /// /// Gets the name of the method. /// - new Utf8String Name + new Utf8String? Name { get; } diff --git a/src/AsmResolver.DotNet/ImplementationMap.cs b/src/AsmResolver.DotNet/ImplementationMap.cs index 2c9a9877f..b846d8f42 100644 --- a/src/AsmResolver.DotNet/ImplementationMap.cs +++ b/src/AsmResolver.DotNet/ImplementationMap.cs @@ -31,7 +31,7 @@ protected ImplementationMap(MetadataToken token) /// The scope that declares the imported member. /// The name of the imported member. /// The attributes associated to the implementation mapping. - public ImplementationMap(ModuleReference? scope, string? name, ImplementationMapAttributes attributes) + public ImplementationMap(ModuleReference? scope, Utf8String? name, ImplementationMapAttributes attributes) : this(new MetadataToken(TableIndex.ImplMap, 0)) { Scope = scope; diff --git a/src/AsmResolver.DotNet/ManifestResource.cs b/src/AsmResolver.DotNet/ManifestResource.cs index a0b07fbde..4034e9142 100644 --- a/src/AsmResolver.DotNet/ManifestResource.cs +++ b/src/AsmResolver.DotNet/ManifestResource.cs @@ -42,7 +42,7 @@ protected ManifestResource(MetadataToken token) /// The attributes of the resource. /// The location of the resource data. /// The offset within the file referenced by where the data starts. - public ManifestResource(string? name, ManifestResourceAttributes attributes, IImplementation? implementation, uint offset) + public ManifestResource(Utf8String? name, ManifestResourceAttributes attributes, IImplementation? implementation, uint offset) : this(new MetadataToken(TableIndex.ManifestResource, 0)) { Name = name; @@ -57,7 +57,7 @@ public ManifestResource(string? name, ManifestResourceAttributes attributes, IIm /// The name of the repository. /// The attributes of the resource. /// The embedded resource data. - public ManifestResource(string? name, ManifestResourceAttributes attributes, ISegment? data) + public ManifestResource(Utf8String? name, ManifestResourceAttributes attributes, ISegment? data) : this(new MetadataToken(TableIndex.ManifestResource, 0)) { Name = name; diff --git a/src/AsmResolver.DotNet/MemberReference.cs b/src/AsmResolver.DotNet/MemberReference.cs index 69a274884..bbc335a99 100644 --- a/src/AsmResolver.DotNet/MemberReference.cs +++ b/src/AsmResolver.DotNet/MemberReference.cs @@ -37,7 +37,7 @@ protected MemberReference(MetadataToken token) /// The name of the referenced member. /// The signature of the referenced member. This dictates whether the /// referenced member is a field or a method. - public MemberReference(IMemberRefParent? parent, string? name, MemberSignature? signature) + public MemberReference(IMemberRefParent? parent, Utf8String? name, MemberSignature? signature) : this(new MetadataToken(TableIndex.MemberRef, 0)) { Parent = parent; diff --git a/src/AsmResolver.DotNet/MethodDefinition.cs b/src/AsmResolver.DotNet/MethodDefinition.cs index a65658523..7d81b58af 100644 --- a/src/AsmResolver.DotNet/MethodDefinition.cs +++ b/src/AsmResolver.DotNet/MethodDefinition.cs @@ -68,7 +68,7 @@ protected MethodDefinition(MetadataToken token) /// is set, the bit should be unset in /// and vice versa. /// - public MethodDefinition(string? name, MethodAttributes attributes, MethodSignature? signature) + public MethodDefinition(Utf8String? name, MethodAttributes attributes, MethodSignature? signature) : this(new MetadataToken(TableIndex.Method, 0)) { Name = name; diff --git a/src/AsmResolver.DotNet/ModuleDefinition.cs b/src/AsmResolver.DotNet/ModuleDefinition.cs index c463b7e2a..ecf5895ce 100644 --- a/src/AsmResolver.DotNet/ModuleDefinition.cs +++ b/src/AsmResolver.DotNet/ModuleDefinition.cs @@ -283,7 +283,7 @@ protected ModuleDefinition(MetadataToken token) /// Defines a new .NET module that references mscorlib version 4.0.0.0. /// /// The name of the module. - public ModuleDefinition(string? name) + public ModuleDefinition(Utf8String? name) : this(new MetadataToken(TableIndex.Module, 0)) { Name = name; diff --git a/src/AsmResolver.DotNet/ModuleReference.cs b/src/AsmResolver.DotNet/ModuleReference.cs index 1c4cef6d4..87f8a52e2 100644 --- a/src/AsmResolver.DotNet/ModuleReference.cs +++ b/src/AsmResolver.DotNet/ModuleReference.cs @@ -32,7 +32,7 @@ protected ModuleReference(MetadataToken token) /// Creates a new reference to an external module. /// /// The file name of the module. - public ModuleReference(string? name) + public ModuleReference(Utf8String? name) : this(new MetadataToken(TableIndex.ModuleRef, 0)) { Name = name; diff --git a/src/AsmResolver.DotNet/ParameterDefinition.cs b/src/AsmResolver.DotNet/ParameterDefinition.cs index 33331e50a..81e815ea9 100644 --- a/src/AsmResolver.DotNet/ParameterDefinition.cs +++ b/src/AsmResolver.DotNet/ParameterDefinition.cs @@ -45,7 +45,7 @@ protected ParameterDefinition(MetadataToken token) /// Creates a new parameter definition using the provided name. /// /// The name of the new parameter. - public ParameterDefinition(string? name) + public ParameterDefinition(Utf8String? name) : this(new MetadataToken(TableIndex.Param, 0)) { Name = name; @@ -57,7 +57,7 @@ public ParameterDefinition(string? name) /// The sequence number of the new parameter. /// The name of the new parameter. /// The attributes to assign to the parameter. - public ParameterDefinition(ushort sequence, string? name, ParameterAttributes attributes) + public ParameterDefinition(ushort sequence, Utf8String? name, ParameterAttributes attributes) : this(new MetadataToken(TableIndex.Param, 0)) { Sequence = sequence; diff --git a/src/AsmResolver.DotNet/PropertyDefinition.cs b/src/AsmResolver.DotNet/PropertyDefinition.cs index 325bacd11..8a795d0e3 100644 --- a/src/AsmResolver.DotNet/PropertyDefinition.cs +++ b/src/AsmResolver.DotNet/PropertyDefinition.cs @@ -46,7 +46,7 @@ protected PropertyDefinition(MetadataToken token) /// The name of the property. /// The attributes. /// The signature of the property. - public PropertyDefinition(string? name, PropertyAttributes attributes, PropertySignature? signature) + public PropertyDefinition(Utf8String? name, PropertyAttributes attributes, PropertySignature? signature) : this(new MetadataToken(TableIndex.Property,0)) { Name = name; diff --git a/src/AsmResolver.DotNet/TypeDefinition.cs b/src/AsmResolver.DotNet/TypeDefinition.cs index b9f658544..c1e8fd6aa 100644 --- a/src/AsmResolver.DotNet/TypeDefinition.cs +++ b/src/AsmResolver.DotNet/TypeDefinition.cs @@ -63,7 +63,7 @@ protected TypeDefinition(MetadataToken token) /// The namespace the type resides in. /// The name of the type. /// The attributes associated to the type. - public TypeDefinition(string? ns, string? name, TypeAttributes attributes) + public TypeDefinition(Utf8String? ns, Utf8String? name, TypeAttributes attributes) : this(ns, name, attributes, null) { } @@ -75,7 +75,7 @@ public TypeDefinition(string? ns, string? name, TypeAttributes attributes) /// The name of the type. /// The attributes associated to the type. /// The super class that this type extends. - public TypeDefinition(string? ns, string? name, TypeAttributes attributes, ITypeDefOrRef? baseType) + public TypeDefinition(Utf8String? ns, Utf8String? name, TypeAttributes attributes, ITypeDefOrRef? baseType) : this(new MetadataToken(TableIndex.TypeDef, 0)) { Namespace = ns; diff --git a/src/AsmResolver.DotNet/TypeReference.cs b/src/AsmResolver.DotNet/TypeReference.cs index fef21335e..637de2a95 100644 --- a/src/AsmResolver.DotNet/TypeReference.cs +++ b/src/AsmResolver.DotNet/TypeReference.cs @@ -37,7 +37,7 @@ protected TypeReference(MetadataToken token) /// The scope that defines the type. /// The namespace the type resides in. /// The name of the type. - public TypeReference(IResolutionScope? scope, string? ns, string? name) + public TypeReference(IResolutionScope? scope, Utf8String? ns, Utf8String? name) : this(new MetadataToken(TableIndex.TypeRef, 0)) { _scope.Value = scope; @@ -52,7 +52,7 @@ public TypeReference(IResolutionScope? scope, string? ns, string? name) /// The scope that defines the type. /// The namespace the type resides in. /// The name of the type. - public TypeReference(ModuleDefinition? module, IResolutionScope? scope, string? ns, string? name) + public TypeReference(ModuleDefinition? module, IResolutionScope? scope, Utf8String? ns, Utf8String? name) : this(new MetadataToken(TableIndex.TypeRef, 0)) { _scope.Value = scope; From ce0a94f8ee9d7762433412e2bd5c10caeaa38828 Mon Sep 17 00:00:00 2001 From: Washi Date: Sun, 27 Nov 2022 15:00:26 +0100 Subject: [PATCH 19/29] Avoid allocation of many empty OneToManyRelation+ValueSets --- .../Collections/OneToManyRelation.cs | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/AsmResolver/Collections/OneToManyRelation.cs b/src/AsmResolver/Collections/OneToManyRelation.cs index ce3e55a4a..80a2bbbe9 100644 --- a/src/AsmResolver/Collections/OneToManyRelation.cs +++ b/src/AsmResolver/Collections/OneToManyRelation.cs @@ -46,7 +46,13 @@ public bool Add(TKey key, TValue value) { if (!_memberOwners.ContainsKey(value)) { - GetValues(key).Items.Add(value); + if (!_memberLists.TryGetValue(key, out var valueSet)) + { + valueSet = new ValueSet(); + _memberLists.Add(key, valueSet); + } + + valueSet.Items.Add(value); _memberOwners.Add(value, key); return true; } @@ -59,34 +65,29 @@ public bool Add(TKey key, TValue value) /// /// The key. /// The values. - public ValueSet GetValues(TKey key) - { - if (!_memberLists.TryGetValue(key, out var items)) - { - items = new ValueSet(); - _memberLists.Add(key, items); - } - - return items; - } + public ValueSet GetValues(TKey key) => _memberLists.TryGetValue(key, out var valueSet) + ? valueSet + : ValueSet.Empty; /// /// Gets the key that maps to the provided value. /// /// The value. /// The key. - public TKey? GetKey(TValue value) - { - return _memberOwners.TryGetValue(value, out var key) - ? key - : default; - } + public TKey? GetKey(TValue value) => _memberOwners.TryGetValue(value, out var key) + ? key + : default; /// /// Represents a collection of values assigned to a single key in a one-to-many relation. /// public class ValueSet : ICollection { + /// + /// Represents the empty value set. + /// + public static readonly ValueSet Empty = new(); + internal List Items { get; From a9eb7b4581d40ab3f96f55da2d0faa719a8f31a0 Mon Sep 17 00:00:00 2001 From: Washi Date: Tue, 29 Nov 2022 21:51:44 +0100 Subject: [PATCH 20/29] Add member full name tests. --- ...ameGenerator.cs => MemberNameGenerator.cs} | 118 ++++++++++++++++-- .../EventDefinitionTest.cs | 12 +- .../FieldDefinitionTest.cs | 10 ++ .../MethodDefinitionTest.cs | 48 +++++++ .../MethodSpecificationTest.cs | 19 +++ .../PropertyDefinitionTest.cs | 22 +++- .../Signatures/TypeSignatureTest.cs | 104 +++++++++++++++ .../TypeDefinitionTest.cs | 24 ++++ 8 files changed, 348 insertions(+), 9 deletions(-) rename src/AsmResolver.DotNet/{FullNameGenerator.cs => MemberNameGenerator.cs} (59%) create mode 100644 test/AsmResolver.DotNet.Tests/Signatures/TypeSignatureTest.cs diff --git a/src/AsmResolver.DotNet/FullNameGenerator.cs b/src/AsmResolver.DotNet/MemberNameGenerator.cs similarity index 59% rename from src/AsmResolver.DotNet/FullNameGenerator.cs rename to src/AsmResolver.DotNet/MemberNameGenerator.cs index fe2409c38..949c83b2e 100644 --- a/src/AsmResolver.DotNet/FullNameGenerator.cs +++ b/src/AsmResolver.DotNet/MemberNameGenerator.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Text; using AsmResolver.DotNet.Signatures; using AsmResolver.DotNet.Signatures.Types; @@ -8,8 +9,13 @@ namespace AsmResolver.DotNet /// /// Provides methods for constructing the full name of a member in a .NET module. /// - public static class FullNameGenerator + public sealed class MemberNameGenerator : ITypeSignatureVisitor { + public static MemberNameGenerator Instance + { + get; + } = new(); + /// /// Computes the full name of a field definition, including its declaring type's full name, as well as its /// field type. @@ -132,17 +138,115 @@ private static string GetParameterTypesString(MethodSignatureBase? signature) /// /// The type to obtain the full name for. /// The full name. - public static string GetTypeFullName(this ITypeDescriptor type) + public static string GetTypeFullName(ITypeDescriptor type) + { + var builder = new StringBuilder(); + return AppendTypeFullName(builder, type).ToString(); + } + + private static StringBuilder AppendTypeFullName(StringBuilder state, ITypeDescriptor type) + { + if (type.DeclaringType is { } declaringType) + { + AppendTypeFullName(state, declaringType); + state.Append('+'); + } + else if (!string.IsNullOrEmpty(type.Namespace)) + { + state.Append(type.Namespace); + state.Append('.'); + } + + return AppendTypeName(state, type); + } + + private static StringBuilder AppendTypeName(StringBuilder state, ITypeDescriptor type) { + if (type is TypeSignature signature) + return signature.AcceptVisitor(Instance, state); + string name = type.Name ?? MetadataMember.NullName; + state.Append(name); - if (type.DeclaringType is not null) - return $"{type.DeclaringType.FullName}+{name}"; + return state; + } - return !string.IsNullOrEmpty(type.Namespace) - ? $"{type.Namespace}.{name}" - : name; + /// + StringBuilder ITypeSignatureVisitor.VisitArrayType(ArrayTypeSignature signature, StringBuilder state) + { + throw new System.NotImplementedException(); } + /// + StringBuilder ITypeSignatureVisitor.VisitBoxedType(BoxedTypeSignature signature, StringBuilder state) + { + throw new System.NotImplementedException(); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitByReferenceType(ByReferenceTypeSignature signature, StringBuilder state) + { + throw new System.NotImplementedException(); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitCorLibType(CorLibTypeSignature signature, StringBuilder state) + { + throw new System.NotImplementedException(); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitCustomModifierType(CustomModifierTypeSignature signature, StringBuilder state) + { + throw new System.NotImplementedException(); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitGenericInstanceType(GenericInstanceTypeSignature signature, StringBuilder state) + { + throw new System.NotImplementedException(); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitGenericParameter(GenericParameterSignature signature, StringBuilder state) + { + throw new System.NotImplementedException(); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitPinnedType(PinnedTypeSignature signature, StringBuilder state) + { + throw new System.NotImplementedException(); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitPointerType(PointerTypeSignature signature, StringBuilder state) + { + throw new System.NotImplementedException(); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitSentinelType(SentinelTypeSignature signature, StringBuilder state) + { + throw new System.NotImplementedException(); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitSzArrayType(SzArrayTypeSignature signature, StringBuilder state) + { + throw new System.NotImplementedException(); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitTypeDefOrRef(TypeDefOrRefSignature signature, StringBuilder state) + { + throw new System.NotImplementedException(); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitFunctionPointerType(FunctionPointerTypeSignature signature, StringBuilder state) + { + throw new System.NotImplementedException(); + } } } diff --git a/test/AsmResolver.DotNet.Tests/EventDefinitionTest.cs b/test/AsmResolver.DotNet.Tests/EventDefinitionTest.cs index 6550c0d0d..0d55ccfbb 100644 --- a/test/AsmResolver.DotNet.Tests/EventDefinitionTest.cs +++ b/test/AsmResolver.DotNet.Tests/EventDefinitionTest.cs @@ -49,5 +49,15 @@ public void ReadEventSemantics() Assert.NotNull(@event.RemoveMethod); } + [Fact] + public void ReadFullName() + { + var module = ModuleDefinition.FromFile(typeof(SingleEvent).Assembly.Location); + var @event = (EventDefinition) module.LookupMember( + typeof(SingleEvent).GetEvent(nameof(SingleEvent.SimpleEvent)).MetadataToken); + + Assert.Equal("System.EventHandler AsmResolver.DotNet.TestCases.Events.SingleEvent::SimpleEvent", @event.FullName); + } + } -} \ No newline at end of file +} diff --git a/test/AsmResolver.DotNet.Tests/FieldDefinitionTest.cs b/test/AsmResolver.DotNet.Tests/FieldDefinitionTest.cs index 28737ee06..ffaf57fe7 100644 --- a/test/AsmResolver.DotNet.Tests/FieldDefinitionTest.cs +++ b/test/AsmResolver.DotNet.Tests/FieldDefinitionTest.cs @@ -80,6 +80,16 @@ public void PersistentFieldSignature() Assert.True(newField.Signature.FieldType.IsTypeOf("System", "Byte"), "Field type should be System.Byte"); } + [Fact] + public void ReadFullName() + { + var module = ModuleDefinition.FromFile(typeof(SingleField).Assembly.Location); + var field = (FieldDefinition) module.LookupMember( + typeof(SingleField).GetField(nameof(SingleField.IntField)).MetadataToken); + + Assert.Equal("System.Int32 AsmResolver.DotNet.TestCases.Fields.SingleField::IntField", field.FullName); + } + [Fact] public void ReadFieldRva() { diff --git a/test/AsmResolver.DotNet.Tests/MethodDefinitionTest.cs b/test/AsmResolver.DotNet.Tests/MethodDefinitionTest.cs index 1e2a6fc8f..ad21af54f 100644 --- a/test/AsmResolver.DotNet.Tests/MethodDefinitionTest.cs +++ b/test/AsmResolver.DotNet.Tests/MethodDefinitionTest.cs @@ -131,6 +131,54 @@ public void ReadMultipleParameterStatic() "Expected third parameter to be of type AsmResolver.TestCases.DotNet.MultipleMethods."); } + [Fact] + public void ReadParameterlessInt32MethodFullName() + { + var module = ModuleDefinition.FromFile(typeof(MultipleMethods).Assembly.Location); + var method = (MethodDefinition) module.LookupMember( + typeof(MultipleMethods).GetMethod(nameof(MultipleMethods.IntParameterlessMethod)).MetadataToken); + + Assert.Equal( + "System.Int32 AsmResolver.DotNet.TestCases.Methods.MultipleMethods::IntParameterlessMethod()", + method.FullName); + } + + [Fact] + public void ReadParameterlessMethodFullName() + { + var module = ModuleDefinition.FromFile(typeof(MultipleMethods).Assembly.Location); + var method = (MethodDefinition) module.LookupMember( + typeof(MultipleMethods).GetMethod(nameof(MultipleMethods.VoidParameterlessMethod)).MetadataToken); + + Assert.Equal( + "System.Void AsmResolver.DotNet.TestCases.Methods.MultipleMethods::VoidParameterlessMethod()", + method.FullName); + } + + [Fact] + public void ReadSingleParameterMethodFullName() + { + var module = ModuleDefinition.FromFile(typeof(MultipleMethods).Assembly.Location); + var method = (MethodDefinition) module.LookupMember( + typeof(MultipleMethods).GetMethod(nameof(MultipleMethods.SingleParameterMethod)).MetadataToken); + + Assert.Equal( + "System.Void AsmResolver.DotNet.TestCases.Methods.MultipleMethods::SingleParameterMethod(System.Int32)", + method.FullName); + } + + [Fact] + public void ReadMultipleParametersMethodFullName() + { + var module = ModuleDefinition.FromFile(typeof(MultipleMethods).Assembly.Location); + var method = (MethodDefinition) module.LookupMember( + typeof(MultipleMethods).GetMethod(nameof(MultipleMethods.MultipleParameterMethod)).MetadataToken); + + Assert.Equal( + "System.Void AsmResolver.DotNet.TestCases.Methods.MultipleMethods::MultipleParameterMethod(System.Int32, System.String, AsmResolver.DotNet.TestCases.Methods.MultipleMethods)", + method.FullName); + } + [Fact] public void ReadNormalMethod() { diff --git a/test/AsmResolver.DotNet.Tests/MethodSpecificationTest.cs b/test/AsmResolver.DotNet.Tests/MethodSpecificationTest.cs index 2083e9946..975789ed7 100644 --- a/test/AsmResolver.DotNet.Tests/MethodSpecificationTest.cs +++ b/test/AsmResolver.DotNet.Tests/MethodSpecificationTest.cs @@ -25,6 +25,25 @@ public void ReadMethod() Assert.Equal("GenericMethodInGenericType", specification.Method!.Name); } + [Fact] + public void ReadFullName() + { + var module = ModuleDefinition.FromFile(typeof(GenericsTestClass).Assembly.Location); + var method = (MethodDefinition) module.LookupMember(typeof(GenericsTestClass) + .GetMethod(nameof(GenericsTestClass.MethodInstantiationFromGenericType))!.MetadataToken); + + var call = method.CilMethodBody!.Instructions.First(i => i.OpCode.Code == CilCode.Call); + Assert.IsAssignableFrom(call.Operand); + + var specification = (MethodSpecification) call.Operand; + Assert.Equal( + "System.Void AsmResolver.DotNet.TestCases.Generics.GenericType`3::GenericMethodInGenericType()", + specification.FullName); + Assert.Equal( + "System.Void AsmResolver.DotNet.TestCases.Generics.GenericType`3::GenericMethodInGenericType()", + specification.Method!.FullName); + } + [Fact] public void ReadSignature() { diff --git a/test/AsmResolver.DotNet.Tests/PropertyDefinitionTest.cs b/test/AsmResolver.DotNet.Tests/PropertyDefinitionTest.cs index f639bfb2f..5bf3c0825 100644 --- a/test/AsmResolver.DotNet.Tests/PropertyDefinitionTest.cs +++ b/test/AsmResolver.DotNet.Tests/PropertyDefinitionTest.cs @@ -76,5 +76,25 @@ public void ReadReadWritePropertySemantics() var property = type.Properties.First(m => m.Name == nameof(MultipleProperties.ReadWriteProperty)); Assert.Equal(2, property.Semantics.Count); } + + [Fact] + public void ReadParameterlessPropertyFullName() + { + var module = ModuleDefinition.FromFile(typeof(MultipleProperties).Assembly.Location); + var property = (PropertyDefinition) module.LookupMember( + typeof(MultipleProperties).GetProperty(nameof(MultipleProperties.ReadOnlyProperty)).MetadataToken); + + Assert.Equal("System.Int32 AsmResolver.DotNet.TestCases.Properties.MultipleProperties::ReadOnlyProperty", property.FullName); + } + + [Fact] + public void ReadParameterPropertyFullName() + { + var module = ModuleDefinition.FromFile(typeof(MultipleProperties).Assembly.Location); + var property = (PropertyDefinition) module.LookupMember( + typeof(MultipleProperties).GetProperty("Item").MetadataToken); + + Assert.Equal("System.Int32 AsmResolver.DotNet.TestCases.Properties.MultipleProperties::Item[System.Int32]", property.FullName); + } } -} \ No newline at end of file +} diff --git a/test/AsmResolver.DotNet.Tests/Signatures/TypeSignatureTest.cs b/test/AsmResolver.DotNet.Tests/Signatures/TypeSignatureTest.cs new file mode 100644 index 000000000..6093847d5 --- /dev/null +++ b/test/AsmResolver.DotNet.Tests/Signatures/TypeSignatureTest.cs @@ -0,0 +1,104 @@ +using AsmResolver.DotNet.Signatures; +using AsmResolver.DotNet.Signatures.Types; +using AsmResolver.PE.DotNet.Metadata.Tables.Rows; +using Xunit; + +namespace AsmResolver.DotNet.Tests.Signatures +{ + public class TypeSignatureTest + { + private readonly TypeDefinition _dummyType = new("Namespace", "Type", TypeAttributes.Class); + + [Fact] + public void GetTypeDefOrRefFullName() + { + Assert.Equal("Namespace.Type", _dummyType.ToTypeSignature().FullName); + } + + [Fact] + public void GetArrayTypeFullName() + { + Assert.Equal("Namespace.Type[0...9, 0...19]", _dummyType + .ToTypeSignature() + .MakeArrayType( + new ArrayDimension(10), + new ArrayDimension(20)) + .FullName); + } + + [Fact] + public void GetByReferenceTypeFullName() + { + Assert.Equal("Namespace.Type&", _dummyType + .ToTypeSignature() + .MakeByReferenceType() + .FullName); + } + + [Fact] + public void GetCorLibTypeFullName() + { + var module = new ModuleDefinition("Dummy"); + Assert.Equal("System.String", module.CorLibTypeFactory.String.FullName); + } + + [Fact] + public void GetFunctionPointerTypeFullName() + { + var module = new ModuleDefinition("Dummy"); + Assert.Equal("System.String *(System.Object, System.Int32)", + MethodSignature.CreateStatic( + module.CorLibTypeFactory.String, + module.CorLibTypeFactory.Object, + module.CorLibTypeFactory.Int32) + .MakeFunctionPointerType().FullName); + } + + [Fact] + public void GetInstanceFunctionPointerTypeFullName() + { + var module = new ModuleDefinition("Dummy"); + Assert.Equal("instance System.String *(System.Object, System.Int32)", + MethodSignature.CreateInstance( + module.CorLibTypeFactory.String, + module.CorLibTypeFactory.Object, + module.CorLibTypeFactory.Int32) + .MakeFunctionPointerType().FullName); + } + + [Fact] + public void GetGenericInstanceTypeFullName() + { + var module = new ModuleDefinition("Dummy"); + Assert.Equal("Namespace.Type>", + _dummyType.MakeGenericInstanceType( + module.CorLibTypeFactory.Int32, + _dummyType.MakeGenericInstanceType(module.CorLibTypeFactory.Object)).FullName); + } + + [Fact] + public void GetGenericParameterFullName() + { + Assert.Equal("!2", new GenericParameterSignature(GenericParameterType.Type, 2).FullName); + Assert.Equal("!!2", new GenericParameterSignature(GenericParameterType.Method, 2).FullName); + } + + [Fact] + public void GetPointerTypeFullName() + { + Assert.Equal("Namespace.Type*", _dummyType + .ToTypeSignature() + .MakePointerType() + .FullName); + } + + [Fact] + public void GetSzArrayTypeFullName() + { + Assert.Equal("Namespace.Type[]", _dummyType + .ToTypeSignature() + .MakeSzArrayType() + .FullName); + } + } +} diff --git a/test/AsmResolver.DotNet.Tests/TypeDefinitionTest.cs b/test/AsmResolver.DotNet.Tests/TypeDefinitionTest.cs index a30204992..4f321ad7b 100644 --- a/test/AsmResolver.DotNet.Tests/TypeDefinitionTest.cs +++ b/test/AsmResolver.DotNet.Tests/TypeDefinitionTest.cs @@ -89,6 +89,14 @@ public void ReadNamespace() Assert.Equal("HelloWorld", module.TopLevelTypes[1].Namespace); } + [Fact] + public void ReadTopLevelTypeFullName() + { + var module = ModuleDefinition.FromFile(typeof(Class).Assembly.Location); + var type = (TypeDefinition) module.LookupMember(typeof(Class).MetadataToken); + Assert.Equal("AsmResolver.DotNet.TestCases.Types.Class", type.FullName); + } + [Fact] public void NonNullNamespaceIsPersistentAfterRebuild() { @@ -179,6 +187,22 @@ public void ReadNestedTypes() Assert.Same(module, nested4.Module); } + [Fact] + public void ReadNestedFullName() + { + var module = ModuleDefinition.FromFile(typeof(TopLevelClass1).Assembly.Location); + var type = (TypeDefinition) module.LookupMember(typeof(TopLevelClass1.Nested1).MetadataToken); + Assert.Equal("AsmResolver.DotNet.TestCases.NestedClasses.TopLevelClass1+Nested1", type.FullName); + } + + [Fact] + public void ReadNestedNestedFullName() + { + var module = ModuleDefinition.FromFile(typeof(TopLevelClass1).Assembly.Location); + var type = (TypeDefinition) module.LookupMember(typeof(TopLevelClass1.Nested1.Nested1Nested2).MetadataToken); + Assert.Equal("AsmResolver.DotNet.TestCases.NestedClasses.TopLevelClass1+Nested1+Nested1Nested2", type.FullName); + } + [Fact] public void ResolveNestedType() { From 4f40ad4f85c4caeb96160d1808027346450d51d2 Mon Sep 17 00:00:00 2001 From: Washi Date: Tue, 29 Nov 2022 21:56:20 +0100 Subject: [PATCH 21/29] Refactor FullNameGenerator to a MemberNameGenerator that uses a single StringBuilder per entire full name. --- src/AsmResolver.DotNet/EventDefinition.cs | 2 +- src/AsmResolver.DotNet/ExportedType.cs | 2 +- src/AsmResolver.DotNet/FieldDefinition.cs | 2 +- src/AsmResolver.DotNet/MemberNameGenerator.cs | 374 +++++++++++++----- src/AsmResolver.DotNet/MemberReference.cs | 5 +- src/AsmResolver.DotNet/MethodDefinition.cs | 9 +- src/AsmResolver.DotNet/MethodSpecification.cs | 8 +- src/AsmResolver.DotNet/PropertyDefinition.cs | 2 +- .../Signatures/Types/TypeSignature.cs | 2 +- src/AsmResolver.DotNet/TypeDefinition.cs | 2 +- src/AsmResolver.DotNet/TypeReference.cs | 2 +- src/AsmResolver.DotNet/TypeSpecification.cs | 2 +- 12 files changed, 293 insertions(+), 119 deletions(-) diff --git a/src/AsmResolver.DotNet/EventDefinition.cs b/src/AsmResolver.DotNet/EventDefinition.cs index 02b84bc36..cc36a78d7 100644 --- a/src/AsmResolver.DotNet/EventDefinition.cs +++ b/src/AsmResolver.DotNet/EventDefinition.cs @@ -94,7 +94,7 @@ public Utf8String? Name string? INameProvider.Name => Name; /// - public string FullName => FullNameGenerator.GetEventFullName(Name, DeclaringType, EventType); + public string FullName => MemberNameGenerator.GetEventFullName(this); /// /// Gets or sets the delegate type of the event. diff --git a/src/AsmResolver.DotNet/ExportedType.cs b/src/AsmResolver.DotNet/ExportedType.cs index 323bc800e..c6253d0ba 100644 --- a/src/AsmResolver.DotNet/ExportedType.cs +++ b/src/AsmResolver.DotNet/ExportedType.cs @@ -94,7 +94,7 @@ public Utf8String? Namespace string? ITypeDescriptor.Namespace => Namespace; /// - public string FullName => this.GetTypeFullName(); + public string FullName => MemberNameGenerator.GetTypeFullName(this); /// public ModuleDefinition? Module diff --git a/src/AsmResolver.DotNet/FieldDefinition.cs b/src/AsmResolver.DotNet/FieldDefinition.cs index 5f1f8d345..0d464fb01 100644 --- a/src/AsmResolver.DotNet/FieldDefinition.cs +++ b/src/AsmResolver.DotNet/FieldDefinition.cs @@ -105,7 +105,7 @@ public FieldSignature? Signature } /// - public string FullName => FullNameGenerator.GetFieldFullName(Name, DeclaringType, Signature); + public string FullName => MemberNameGenerator.GetFieldFullName(this); /// /// Gets or sets the attributes associated to the field. diff --git a/src/AsmResolver.DotNet/MemberNameGenerator.cs b/src/AsmResolver.DotNet/MemberNameGenerator.cs index 949c83b2e..be08dad1c 100644 --- a/src/AsmResolver.DotNet/MemberNameGenerator.cs +++ b/src/AsmResolver.DotNet/MemberNameGenerator.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -11,141 +12,245 @@ namespace AsmResolver.DotNet /// public sealed class MemberNameGenerator : ITypeSignatureVisitor { + private MemberNameGenerator() + { + } + + /// + /// Gets the singleton instance for the member name generator. + /// public static MemberNameGenerator Instance { get; } = new(); + /// + /// Computes the full name of a type descriptor, including its namespace and/or declaring types. + /// + /// The type to obtain the full name for. + /// The full name. + public static string GetTypeFullName(ITypeDescriptor type) + { + var state = new StringBuilder(); + return AppendTypeFullName(state, type).ToString(); + } + /// /// Computes the full name of a field definition, including its declaring type's full name, as well as its /// field type. /// - /// The name of the field. - /// The declaring type of the field, if available. - /// The signature of the field. + /// The field /// The full name - public static string GetFieldFullName(string? name, ITypeDescriptor? declaringType, FieldSignature? signature) + public static string GetFieldFullName(IFieldDescriptor descriptor) { - string fieldTypeString = signature?.FieldType.FullName ?? TypeSignature.NullTypeToString; + var state = new StringBuilder(); + + AppendTypeFullName(state, descriptor.Signature?.FieldType); + state.Append(' '); + AppendMemberDeclaringType(state, descriptor.DeclaringType); - return declaringType is null - ? $"{fieldTypeString} {name}" - : $"{fieldTypeString} {declaringType}::{name}"; + return state.Append(descriptor.Name ?? MetadataMember.NullName).ToString(); } /// - /// Computes the full name of a method definition, including its declaring type's full name, as well as its + /// Computes the full name of a method reference, including its declaring type's full name, as well as its /// return type and parameters. /// - /// The name of the method. - /// The declaring type of the method if available. - /// The signature of the method. + /// The reference /// The full name - public static string GetMethodFullName(string? name, ITypeDescriptor? declaringType, MethodSignature? signature) + public static string GetMethodFullName(MemberReference reference) { - if (signature?.GenericParameterCount > 0) - { - return GetMethodFullName(name, declaringType, signature, - Enumerable.Repeat("?", signature.GenericParameterCount)); - } + var state = new StringBuilder(); + + var signature = reference.Signature as MethodSignature; + + AppendTypeFullName(state, signature?.ReturnType); + state.Append(' '); + AppendMemberDeclaringType(state, reference.DeclaringType); + state.Append(reference.Name ?? MetadataMember.NullName); + + AppendTypeArgumentPlaceholders(state, signature); + + state.Append('('); + AppendSignatureParameterTypes(state, signature); + state.Append(')'); + + return state.ToString(); + } + + /// + /// Computes the full name of a method definition, including its declaring type's full name, as well as its + /// return type, parameters and any type arguments. + /// + /// The definition + /// The full name + public static string GetMethodFullName(MethodDefinition definition) + { + var state = new StringBuilder(); + + var signature = definition.Signature; - string returnTypeString = signature?.ReturnType.FullName ?? TypeSignature.NullTypeToString; - string parameterTypesString = GetParameterTypesString(signature); + AppendTypeFullName(state, signature?.ReturnType); + state.Append(' '); + AppendMemberDeclaringType(state, definition.DeclaringType); + state.Append(definition.Name ?? MetadataMember.NullName.Value); - return declaringType is null - ? $"{returnTypeString} {name}({parameterTypesString})" - : $"{returnTypeString} {declaringType}::{name}({parameterTypesString})"; + AppendTypeParameters(state, definition.GenericParameters); + + state.Append('('); + AppendSignatureParameterTypes(state, signature); + state.Append(')'); + + return state.ToString(); } /// /// Computes the full name of a method specification, including its declaring type's full name, as well as its /// return type, parameters and any type arguments. /// - /// The name of the method. - /// The declaring type of the method if available. - /// The signature of the method. - /// The type arguments. + /// The specification /// The full name - public static string GetMethodFullName( - string? name, - ITypeDescriptor? declaringType, - MethodSignature? signature, - IEnumerable typeArguments) + public static string GetMethodFullName(MethodSpecification specification) { - string returnTypeString = signature?.ReturnType.FullName ?? TypeSignature.NullTypeToString; - string parameterTypesString = GetParameterTypesString(signature); + var state = new StringBuilder(); + + var signature = specification.Method?.Signature; - string[] argumentNames = typeArguments.ToArray(); - string typeArgumentsString = argumentNames.Length>0 - ? $"<{string.Join(", ", argumentNames)}>" - : string.Empty; + AppendTypeFullName(state, signature?.ReturnType); + state.Append(' '); + AppendMemberDeclaringType(state, specification.DeclaringType); + state.Append(specification.Name); - return declaringType is null - ? $"{returnTypeString} {name}{typeArgumentsString}({parameterTypesString})" - : $"{returnTypeString} {declaringType}::{name}{typeArgumentsString}({parameterTypesString})"; + AppendTypeParameters(state, specification.Signature?.TypeArguments ?? Array.Empty()); + + state.Append('('); + AppendSignatureParameterTypes(state, signature); + state.Append(')'); + + return state.ToString(); } /// /// Computes the full name of a property definition, including its declaring type's full name, as well as its /// return type and parameters. /// - /// The name of the property. - /// The declaring type of the property if available. - /// The signature of the property. + /// The property /// The full name - public static string GetPropertyFullName(string? name, ITypeDescriptor? declaringType, PropertySignature? signature) + public static string GetPropertyFullName(PropertyDefinition definition) { - string propertyTypeString = signature?.ReturnType.FullName ?? TypeSignature.NullTypeToString; - string parameterTypesString = signature?.ParameterTypes.Count > 0 - ? $"[{GetParameterTypesString(signature)}]" - : string.Empty; + var state = new StringBuilder(); + + var signature = definition.Signature; - return declaringType is null - ? $"{propertyTypeString} {name}{parameterTypesString}" - : $"{propertyTypeString} {declaringType}::{name}{parameterTypesString}"; + AppendTypeFullName(state, signature?.ReturnType); + state.Append(' '); + AppendMemberDeclaringType(state, definition.DeclaringType); + state.Append(definition.Name ?? MetadataMember.NullName); + + if (signature?.ParameterTypes.Count > 0) + { + state.Append('['); + AppendSignatureParameterTypes(state, signature); + state.Append(']'); + } + + return state.ToString(); } /// /// Computes the full name of a event definition, including its declaring type's full name, as well as its /// event type. /// - /// The name of the field. - /// The declaring type of the field, if available. - /// The type of the event. + /// The event /// The full name - public static string GetEventFullName(string? name, ITypeDescriptor? declaringType, ITypeDefOrRef? eventType) + public static string GetEventFullName(EventDefinition definition) { - return declaringType is null - ? $"{eventType} {name}" - : $"{eventType} {declaringType}::{name}"; + var state = new StringBuilder(); + + AppendTypeFullName(state, definition.EventType); + state.Append(' '); + AppendMemberDeclaringType(state, definition.DeclaringType); + state.Append(definition.Name); + + return state.ToString(); + } + + private static StringBuilder AppendMemberDeclaringType(StringBuilder state, ITypeDescriptor? declaringType) + { + if (declaringType is not null) + { + AppendTypeFullName(state, declaringType); + state.Append("::"); + } + + return state; } - private static string GetParameterTypesString(MethodSignatureBase? signature) + private static StringBuilder AppendSignatureParameterTypes(StringBuilder state, MethodSignatureBase? signature) { if (signature is null) - return string.Empty; + return state; + + for (int i = 0; i < signature.ParameterTypes.Count; i++) + { + signature.ParameterTypes[i].AcceptVisitor(Instance, state); + if (i < signature.ParameterTypes.Count - 1) + state.Append(", "); + } - string parametersString = string.Join(", ", signature.ParameterTypes); - string sentinelSuffix = signature.IsSentinel - ? ", ..." - : string.Empty; + if (signature.IsSentinel) + state.Append("..."); - return $"{parametersString}{sentinelSuffix}"; + return state; } - /// - /// Computes the full name of a type descriptor, including its namespace and/or declaring types. - /// - /// The type to obtain the full name for. - /// The full name. - public static string GetTypeFullName(ITypeDescriptor type) + private static StringBuilder AppendTypeParameters(StringBuilder state, IList typeArguments) + { + if (typeArguments.Count > 0) + { + state.Append('<'); + AppendCommaSeparatedCollection(state, typeArguments, static (s, t) => s.Append(t.Name)); + state.Append('>'); + } + + return state; + } + + private static StringBuilder AppendTypeParameters(StringBuilder state, IList typeArguments) { - var builder = new StringBuilder(); - return AppendTypeFullName(builder, type).ToString(); + if (typeArguments.Count > 0) + { + state.Append('<'); + AppendCommaSeparatedCollection(state, typeArguments, static (s, t) => t.AcceptVisitor(Instance, s)); + state.Append('>'); + } + + return state; + } + + private static StringBuilder AppendTypeFullName(StringBuilder state, ITypeDescriptor? type) + { + switch (type) + { + case TypeSignature signature: + return signature.AcceptVisitor(Instance, state); + + case ITypeDefOrRef reference: + return AppendTypeFullName(state, reference); + + case null: + return state.Append(TypeSignature.NullTypeToString); + + default: + throw new ArgumentOutOfRangeException(nameof(type)); + } } - private static StringBuilder AppendTypeFullName(StringBuilder state, ITypeDescriptor type) + private static StringBuilder AppendTypeFullName(StringBuilder state, ITypeDefOrRef? type) { + if (type is null) + return state.Append(TypeSignature.NullTypeToString); + if (type.DeclaringType is { } declaringType) { AppendTypeFullName(state, declaringType); @@ -157,16 +262,50 @@ private static StringBuilder AppendTypeFullName(StringBuilder state, ITypeDescri state.Append('.'); } - return AppendTypeName(state, type); + return state.Append(type.Name ?? MetadataMember.NullName); + } + + private static StringBuilder AppendTypeArgumentPlaceholders(StringBuilder state, MethodSignature? signature) + { + if (signature?.GenericParameterCount > 0) + { + state.Append('<'); + AppendCommaSeparatedCollection(state, + Enumerable.Range(0, signature.GenericParameterCount).ToArray(), + static (s, _) => s.Append('?')); + state.Append('>'); + } + + return state; } - private static StringBuilder AppendTypeName(StringBuilder state, ITypeDescriptor type) + private static StringBuilder AppendMethodSignature(StringBuilder state, MethodSignature signature) { - if (type is TypeSignature signature) - return signature.AcceptVisitor(Instance, state); + if (signature.HasThis) + state.Append("instance "); - string name = type.Name ?? MetadataMember.NullName; - state.Append(name); + signature.ReturnType.AcceptVisitor(Instance, state); + + state.Append(" *"); + + AppendTypeArgumentPlaceholders(state, signature); + + state.Append('('); + AppendSignatureParameterTypes(state, signature); + return state.Append(')'); + } + + private static StringBuilder AppendCommaSeparatedCollection( + StringBuilder state, + IList collection, + Action action) + { + for (int i = 0; i < collection.Count; i++) + { + action(state, collection[i]); + if (i < collection.Count - 1) + state.Append(", "); + } return state; } @@ -174,79 +313,126 @@ private static StringBuilder AppendTypeName(StringBuilder state, ITypeDescriptor /// StringBuilder ITypeSignatureVisitor.VisitArrayType(ArrayTypeSignature signature, StringBuilder state) { - throw new System.NotImplementedException(); + signature.BaseType.AcceptVisitor(this, state); + + state.Append('['); + + AppendCommaSeparatedCollection(state, signature.Dimensions, static (s, d) => + { + if (d.LowerBound.HasValue) + { + if (d.Size.HasValue) + { + AppendDimensionBound(s, d.LowerBound.Value, d.Size.Value); + } + else + { + s.Append(d.LowerBound.Value) + .Append("..."); + } + } + + if (d.Size.HasValue) + AppendDimensionBound(s, 0, d.Size.Value); + + static void AppendDimensionBound(StringBuilder state, int low, int size) + { + state.Append(low) + .Append("...") + .Append(low + size - 1); + } + }); + + return state.Append(']'); } /// StringBuilder ITypeSignatureVisitor.VisitBoxedType(BoxedTypeSignature signature, StringBuilder state) { - throw new System.NotImplementedException(); + return signature.BaseType.AcceptVisitor(this, state); } /// StringBuilder ITypeSignatureVisitor.VisitByReferenceType(ByReferenceTypeSignature signature, StringBuilder state) { - throw new System.NotImplementedException(); + return signature.BaseType + .AcceptVisitor(this, state) + .Append('&'); } /// StringBuilder ITypeSignatureVisitor.VisitCorLibType(CorLibTypeSignature signature, StringBuilder state) { - throw new System.NotImplementedException(); + return state.Append("System.").Append(signature.Name); } /// StringBuilder ITypeSignatureVisitor.VisitCustomModifierType(CustomModifierTypeSignature signature, StringBuilder state) { - throw new System.NotImplementedException(); + signature.BaseType.AcceptVisitor(this, state); + state.Append(signature.IsRequired ? " modreq(" : " modopt("); + AppendTypeFullName(state, signature.ModifierType); + return state.Append(')'); } /// StringBuilder ITypeSignatureVisitor.VisitGenericInstanceType(GenericInstanceTypeSignature signature, StringBuilder state) { - throw new System.NotImplementedException(); + AppendTypeFullName(state, signature.GenericType); + return AppendTypeParameters(state, signature.TypeArguments); } /// StringBuilder ITypeSignatureVisitor.VisitGenericParameter(GenericParameterSignature signature, StringBuilder state) { - throw new System.NotImplementedException(); + state.Append(signature.ParameterType switch + { + GenericParameterType.Type => "!", + GenericParameterType.Method => "!!", + _ => throw new ArgumentOutOfRangeException() + }); + + return state.Append(signature.Index); } /// StringBuilder ITypeSignatureVisitor.VisitPinnedType(PinnedTypeSignature signature, StringBuilder state) { - throw new System.NotImplementedException(); + return signature.BaseType.AcceptVisitor(this, state); } /// StringBuilder ITypeSignatureVisitor.VisitPointerType(PointerTypeSignature signature, StringBuilder state) { - throw new System.NotImplementedException(); + return signature.BaseType + .AcceptVisitor(this, state) + .Append('*'); } /// StringBuilder ITypeSignatureVisitor.VisitSentinelType(SentinelTypeSignature signature, StringBuilder state) { - throw new System.NotImplementedException(); + return state.Append("<<>>"); } /// StringBuilder ITypeSignatureVisitor.VisitSzArrayType(SzArrayTypeSignature signature, StringBuilder state) { - throw new System.NotImplementedException(); + return signature.BaseType + .AcceptVisitor(this, state) + .Append("[]"); } /// StringBuilder ITypeSignatureVisitor.VisitTypeDefOrRef(TypeDefOrRefSignature signature, StringBuilder state) { - throw new System.NotImplementedException(); + return AppendTypeFullName(state, signature.Type); } /// StringBuilder ITypeSignatureVisitor.VisitFunctionPointerType(FunctionPointerTypeSignature signature, StringBuilder state) { - throw new System.NotImplementedException(); + return AppendMethodSignature(state, signature.Signature); } } } diff --git a/src/AsmResolver.DotNet/MemberReference.cs b/src/AsmResolver.DotNet/MemberReference.cs index bbc335a99..276ebb1a7 100644 --- a/src/AsmResolver.DotNet/MemberReference.cs +++ b/src/AsmResolver.DotNet/MemberReference.cs @@ -103,9 +103,10 @@ public string FullName get { if (IsField) - return FullNameGenerator.GetFieldFullName(Name, DeclaringType, (FieldSignature) Signature); + return MemberNameGenerator.GetFieldFullName(this); if (IsMethod) - return FullNameGenerator.GetMethodFullName(Name, DeclaringType, (MethodSignature) Signature); + return MemberNameGenerator.GetMethodFullName(this); + return Name ?? NullName; } } diff --git a/src/AsmResolver.DotNet/MethodDefinition.cs b/src/AsmResolver.DotNet/MethodDefinition.cs index 7d81b58af..2e40249fc 100644 --- a/src/AsmResolver.DotNet/MethodDefinition.cs +++ b/src/AsmResolver.DotNet/MethodDefinition.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Threading; using AsmResolver.Collections; using AsmResolver.DotNet.Code; @@ -101,13 +100,7 @@ public MethodSignature? Signature } /// - public string FullName => FullNameGenerator.GetMethodFullName( - Name, - DeclaringType, - Signature, - GenericParameters.Count > 0 - ? GenericParameters.Select(x => x.Name?.Value ?? NullName) - : Enumerable.Empty()); + public string FullName => MemberNameGenerator.GetMethodFullName(this); /// /// Gets or sets the attributes associated to the method. diff --git a/src/AsmResolver.DotNet/MethodSpecification.cs b/src/AsmResolver.DotNet/MethodSpecification.cs index 31ca1226e..e9b9a3210 100644 --- a/src/AsmResolver.DotNet/MethodSpecification.cs +++ b/src/AsmResolver.DotNet/MethodSpecification.cs @@ -1,9 +1,7 @@ using System.Collections.Generic; -using System.Linq; using System.Threading; using AsmResolver.Collections; using AsmResolver.DotNet.Signatures; -using AsmResolver.DotNet.Signatures.Types; using AsmResolver.PE.DotNet.Metadata.Tables; namespace AsmResolver.DotNet @@ -69,11 +67,7 @@ public GenericInstanceMethodSignature? Signature string? INameProvider.Name => Name; /// - public string FullName => FullNameGenerator.GetMethodFullName( - Name, - DeclaringType, - Method?.Signature, - Signature?.TypeArguments.Select(x => x.FullName) ?? Enumerable.Empty()); + public string FullName => MemberNameGenerator.GetMethodFullName(this); /// public ModuleDefinition? Module => Method?.Module; diff --git a/src/AsmResolver.DotNet/PropertyDefinition.cs b/src/AsmResolver.DotNet/PropertyDefinition.cs index 8a795d0e3..ac35369ab 100644 --- a/src/AsmResolver.DotNet/PropertyDefinition.cs +++ b/src/AsmResolver.DotNet/PropertyDefinition.cs @@ -109,7 +109,7 @@ public Utf8String? Name string? INameProvider.Name => Name; /// - public string FullName => FullNameGenerator.GetPropertyFullName(Name, DeclaringType, Signature); + public string FullName => MemberNameGenerator.GetPropertyFullName(this); /// /// Gets or sets the signature of the property. This includes the property type, as well as any parameters the diff --git a/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs b/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs index 161c5978c..6bd697c38 100644 --- a/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs @@ -27,7 +27,7 @@ public abstract string? Namespace } /// - public string FullName => this.GetTypeFullName(); + public string FullName => MemberNameGenerator.GetTypeFullName(this); /// public abstract IResolutionScope? Scope diff --git a/src/AsmResolver.DotNet/TypeDefinition.cs b/src/AsmResolver.DotNet/TypeDefinition.cs index c1e8fd6aa..ffe56e169 100644 --- a/src/AsmResolver.DotNet/TypeDefinition.cs +++ b/src/AsmResolver.DotNet/TypeDefinition.cs @@ -115,7 +115,7 @@ public Utf8String? Name /// /// Gets the full name (including namespace or declaring type full name) of the type. /// - public string FullName => this.GetTypeFullName(); + public string FullName => MemberNameGenerator.GetTypeFullName(this); /// /// Gets or sets the attributes associated to the type. diff --git a/src/AsmResolver.DotNet/TypeReference.cs b/src/AsmResolver.DotNet/TypeReference.cs index 637de2a95..2db296a1a 100644 --- a/src/AsmResolver.DotNet/TypeReference.cs +++ b/src/AsmResolver.DotNet/TypeReference.cs @@ -90,7 +90,7 @@ public Utf8String? Namespace string? ITypeDescriptor.Namespace => Namespace; /// - public string FullName => this.GetTypeFullName(); + public string FullName => MemberNameGenerator.GetTypeFullName(this); /// public IResolutionScope? Scope diff --git a/src/AsmResolver.DotNet/TypeSpecification.cs b/src/AsmResolver.DotNet/TypeSpecification.cs index aa4420351..d601ec7a7 100644 --- a/src/AsmResolver.DotNet/TypeSpecification.cs +++ b/src/AsmResolver.DotNet/TypeSpecification.cs @@ -61,7 +61,7 @@ public TypeSignature? Signature string? ITypeDescriptor.Namespace => Namespace; /// - public string FullName => this.GetTypeFullName(); + public string FullName => MemberNameGenerator.GetTypeFullName(this); /// public ModuleDefinition? Module => Signature?.Module; From ccbad16fbb5bfae29c1c4ec44656a5f902d37621 Mon Sep 17 00:00:00 2001 From: Washi Date: Tue, 29 Nov 2022 22:45:43 +0100 Subject: [PATCH 22/29] Cache string builder per thread. --- src/AsmResolver.DotNet/MemberNameGenerator.cs | 27 ++++++++++++++----- .../Types/GenericInstanceTypeSignature.cs | 9 +------ 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/AsmResolver.DotNet/MemberNameGenerator.cs b/src/AsmResolver.DotNet/MemberNameGenerator.cs index be08dad1c..41b907d74 100644 --- a/src/AsmResolver.DotNet/MemberNameGenerator.cs +++ b/src/AsmResolver.DotNet/MemberNameGenerator.cs @@ -12,6 +12,9 @@ namespace AsmResolver.DotNet /// public sealed class MemberNameGenerator : ITypeSignatureVisitor { + [ThreadStatic] + private static StringBuilder? _builder; + private MemberNameGenerator() { } @@ -24,6 +27,13 @@ public static MemberNameGenerator Instance get; } = new(); + private static StringBuilder GetBuilder() + { + _builder ??= new StringBuilder(); + _builder.Clear(); + return _builder; + } + /// /// Computes the full name of a type descriptor, including its namespace and/or declaring types. /// @@ -31,7 +41,7 @@ public static MemberNameGenerator Instance /// The full name. public static string GetTypeFullName(ITypeDescriptor type) { - var state = new StringBuilder(); + var state = GetBuilder(); return AppendTypeFullName(state, type).ToString(); } @@ -43,7 +53,7 @@ public static string GetTypeFullName(ITypeDescriptor type) /// The full name public static string GetFieldFullName(IFieldDescriptor descriptor) { - var state = new StringBuilder(); + var state = GetBuilder(); AppendTypeFullName(state, descriptor.Signature?.FieldType); state.Append(' '); @@ -60,7 +70,7 @@ public static string GetFieldFullName(IFieldDescriptor descriptor) /// The full name public static string GetMethodFullName(MemberReference reference) { - var state = new StringBuilder(); + var state = GetBuilder(); var signature = reference.Signature as MethodSignature; @@ -86,7 +96,7 @@ public static string GetMethodFullName(MemberReference reference) /// The full name public static string GetMethodFullName(MethodDefinition definition) { - var state = new StringBuilder(); + var state = GetBuilder(); var signature = definition.Signature; @@ -112,7 +122,7 @@ public static string GetMethodFullName(MethodDefinition definition) /// The full name public static string GetMethodFullName(MethodSpecification specification) { - var state = new StringBuilder(); + var state = GetBuilder(); var signature = specification.Method?.Signature; @@ -138,7 +148,7 @@ public static string GetMethodFullName(MethodSpecification specification) /// The full name public static string GetPropertyFullName(PropertyDefinition definition) { - var state = new StringBuilder(); + var state = GetBuilder(); var signature = definition.Signature; @@ -165,7 +175,7 @@ public static string GetPropertyFullName(PropertyDefinition definition) /// The full name public static string GetEventFullName(EventDefinition definition) { - var state = new StringBuilder(); + var state = GetBuilder(); AppendTypeFullName(state, definition.EventType); state.Append(' '); @@ -251,6 +261,9 @@ private static StringBuilder AppendTypeFullName(StringBuilder state, ITypeDefOrR if (type is null) return state.Append(TypeSignature.NullTypeToString); + if (type is TypeSpecification specification) + return AppendTypeFullName(state, specification.Signature); + if (type.DeclaringType is { } declaringType) { AppendTypeFullName(state, declaringType); diff --git a/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs b/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs index 67045f1da..6de88d607 100644 --- a/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs @@ -84,14 +84,7 @@ public ITypeDefOrRef GenericType public IList TypeArguments => _typeArguments; /// - public override string? Name - { - get - { - string genericArgString = string.Join(", ", TypeArguments); - return $"{GenericType?.Name ?? NullTypeToString}<{genericArgString}>"; - } - } + public override string? Name => GenericType.Name ?? NullTypeToString; /// public override string? Namespace => GenericType.Namespace; From c2c239d434f66f41765878eccb917e2ebc38f330 Mon Sep 17 00:00:00 2001 From: Washi Date: Wed, 30 Nov 2022 14:50:43 +0100 Subject: [PATCH 23/29] Add missing 'method' keyword for fnptr type sigs. --- src/AsmResolver.DotNet/MemberNameGenerator.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/AsmResolver.DotNet/MemberNameGenerator.cs b/src/AsmResolver.DotNet/MemberNameGenerator.cs index 41b907d74..5612709db 100644 --- a/src/AsmResolver.DotNet/MemberNameGenerator.cs +++ b/src/AsmResolver.DotNet/MemberNameGenerator.cs @@ -103,7 +103,7 @@ public static string GetMethodFullName(MethodDefinition definition) AppendTypeFullName(state, signature?.ReturnType); state.Append(' '); AppendMemberDeclaringType(state, definition.DeclaringType); - state.Append(definition.Name ?? MetadataMember.NullName.Value); + state.Append(definition.Name ?? MetadataMember.NullName); AppendTypeParameters(state, definition.GenericParameters); @@ -445,6 +445,7 @@ StringBuilder ITypeSignatureVisitor.VisitTypeDefOr /// StringBuilder ITypeSignatureVisitor.VisitFunctionPointerType(FunctionPointerTypeSignature signature, StringBuilder state) { + state.Append("method "); return AppendMethodSignature(state, signature.Signature); } } From 618f229a4e2d13932b12a7aa64b5910f68634e1a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Dec 2022 14:11:28 +0000 Subject: [PATCH 24/29] Bump xunit.analyzers from 1.0.0 to 1.1.0 Bumps [xunit.analyzers](https://github.com/xunit/xunit.analyzers) from 1.0.0 to 1.1.0. - [Release notes](https://github.com/xunit/xunit.analyzers/releases) - [Commits](https://github.com/xunit/xunit.analyzers/commits) --- updated-dependencies: - dependency-name: xunit.analyzers dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .../AsmResolver.DotNet.Dynamic.Tests.csproj | 2 +- test/AsmResolver.PE.Tests/AsmResolver.PE.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/AsmResolver.DotNet.Dynamic.Tests/AsmResolver.DotNet.Dynamic.Tests.csproj b/test/AsmResolver.DotNet.Dynamic.Tests/AsmResolver.DotNet.Dynamic.Tests.csproj index 9b6e3a775..28574f7f7 100644 --- a/test/AsmResolver.DotNet.Dynamic.Tests/AsmResolver.DotNet.Dynamic.Tests.csproj +++ b/test/AsmResolver.DotNet.Dynamic.Tests/AsmResolver.DotNet.Dynamic.Tests.csproj @@ -10,7 +10,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/AsmResolver.PE.Tests/AsmResolver.PE.Tests.csproj b/test/AsmResolver.PE.Tests/AsmResolver.PE.Tests.csproj index 3945a4696..2c4967fcf 100644 --- a/test/AsmResolver.PE.Tests/AsmResolver.PE.Tests.csproj +++ b/test/AsmResolver.PE.Tests/AsmResolver.PE.Tests.csproj @@ -18,7 +18,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From db61d5776ddaa4acb8d04091e3eb1cb12bf64d81 Mon Sep 17 00:00:00 2001 From: Washi Date: Tue, 6 Dec 2022 21:50:46 +0100 Subject: [PATCH 25/29] Add fullnamegenerator benchmark, add comparison benchmark jobs with older asmresolver versions. --- src/AsmResolver.DotNet/ModuleDefinition.cs | 12 ++++ .../AsmResolver.Benchmarks.csproj | 14 +++-- .../FullNameGeneratorBenchmark.cs | 29 +++++++++ test/AsmResolver.Benchmarks/Program.cs | 60 +++++++++++++++++-- 4 files changed, 104 insertions(+), 11 deletions(-) create mode 100644 test/AsmResolver.Benchmarks/FullNameGeneratorBenchmark.cs diff --git a/src/AsmResolver.DotNet/ModuleDefinition.cs b/src/AsmResolver.DotNet/ModuleDefinition.cs index ecf5895ce..13a5c9669 100644 --- a/src/AsmResolver.DotNet/ModuleDefinition.cs +++ b/src/AsmResolver.DotNet/ModuleDefinition.cs @@ -279,6 +279,18 @@ protected ModuleDefinition(MetadataToken token) #pragma warning restore 8618 + /// + /// Defines a new .NET module that references mscorlib version 4.0.0.0. + /// + /// The name of the module. + /// + /// This constructor co-exists with the Utf8String overload for backwards compatibility. + /// + public ModuleDefinition(string? name) + : this((Utf8String?) name) + { + } + /// /// Defines a new .NET module that references mscorlib version 4.0.0.0. /// diff --git a/test/AsmResolver.Benchmarks/AsmResolver.Benchmarks.csproj b/test/AsmResolver.Benchmarks/AsmResolver.Benchmarks.csproj index bd140f317..9d08fac34 100644 --- a/test/AsmResolver.Benchmarks/AsmResolver.Benchmarks.csproj +++ b/test/AsmResolver.Benchmarks/AsmResolver.Benchmarks.csproj @@ -2,19 +2,21 @@ Exe - net6.0;netcoreapp3.1 + net6.0 + enable + - - - - - + + + + + diff --git a/test/AsmResolver.Benchmarks/FullNameGeneratorBenchmark.cs b/test/AsmResolver.Benchmarks/FullNameGeneratorBenchmark.cs new file mode 100644 index 000000000..06b08df4b --- /dev/null +++ b/test/AsmResolver.Benchmarks/FullNameGeneratorBenchmark.cs @@ -0,0 +1,29 @@ +using AsmResolver.DotNet; +using AsmResolver.DotNet.Signatures; +using BenchmarkDotNet.Attributes; + +namespace AsmResolver.Benchmarks +{ + [MemoryDiagnoser] + public class FullNameGeneratorBenchmark + { + private MemberReference _memberReference = null!; + + [GlobalSetup] + public void Setup() + { + var module = new ModuleDefinition("Dummy"); + var factory = module.CorLibTypeFactory; + _memberReference = factory.CorLibScope + .CreateTypeReference("System", "Span`1") + .MakeGenericInstanceType(factory.Int32) + .ToTypeDefOrRef() + .CreateMemberReference(".ctor", MethodSignature.CreateStatic( + factory.Void, + factory.Int32)); + } + + [Benchmark] + public string FullName() => _memberReference.FullName; + } +} diff --git a/test/AsmResolver.Benchmarks/Program.cs b/test/AsmResolver.Benchmarks/Program.cs index 1d3da5a53..c56e6149e 100644 --- a/test/AsmResolver.Benchmarks/Program.cs +++ b/test/AsmResolver.Benchmarks/Program.cs @@ -1,16 +1,66 @@ using System; +using System.CommandLine; +using System.CommandLine.Builder; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Running; namespace AsmResolver.Benchmarks { internal static class Program { - public static void Main(string[] args) + public static async Task Main(string[] args) { - if (args.Length == 0) - BenchmarkRunner.Run(typeof(Program).Assembly); - else - BenchmarkRunner.Run(Type.GetType($"AsmResolver.Benchmarks.{args[0]}")); + var root = new RootCommand(); + + var runCommand = new Command("run", "Run benchmarks"); + root.AddCommand(runCommand); + + var baselineVersionOption = new Option("--baseline", + "Compare the results to a different nuget version of AsmResolver."); + var onlyOption = new Option("--type", + "Only run the benchmarks in the specified benchmark type."); + + runCommand.AddOption(baselineVersionOption); + runCommand.AddOption(onlyOption); + + runCommand.SetHandler((baselineVersion, benchmarkType) => + { + var config = new ManualConfig(); + var job = Job.Default; + + if (!string.IsNullOrEmpty(baselineVersion)) + { + config.AddJob(job + .WithNuGet(new NuGetReferenceList + { + new("AsmResolver", baselineVersion), + new("AsmResolver.PE.File", baselineVersion), + new("AsmResolver.PE", baselineVersion), + new("AsmResolver.PE.Win32Resources", baselineVersion), + new("AsmResolver.DotNet", baselineVersion), + new("AsmResolver.DotNet.Dynamic", baselineVersion), + }).WithId(baselineVersion) + .AsBaseline()); + } + + config.AddExporter(DefaultConfig.Instance.GetExporters().ToArray()); + config.AddLogger(DefaultConfig.Instance.GetLoggers().ToArray()); + config.AddColumnProvider(DefaultConfig.Instance.GetColumnProviders().ToArray()); + config.HideColumns("NuGetReferences"); + config.AddJob(job); + + if (string.IsNullOrEmpty(benchmarkType)) + BenchmarkRunner.Run(Assembly.GetExecutingAssembly(), config); + else + BenchmarkRunner.Run(Type.GetType($"AsmResolver.Benchmarks.{benchmarkType}"), config); + + }, baselineVersionOption, onlyOption); + + return await root.InvokeAsync(args); } } } From 8a8803abc7e483df3f4e54ed552182d8a4ca75f8 Mon Sep 17 00:00:00 2001 From: Washi Date: Tue, 6 Dec 2022 22:00:03 +0100 Subject: [PATCH 26/29] Fix unit tests for fnptr type signatures. --- test/AsmResolver.DotNet.Tests/Signatures/TypeSignatureTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/AsmResolver.DotNet.Tests/Signatures/TypeSignatureTest.cs b/test/AsmResolver.DotNet.Tests/Signatures/TypeSignatureTest.cs index 6093847d5..cf5ab2bf7 100644 --- a/test/AsmResolver.DotNet.Tests/Signatures/TypeSignatureTest.cs +++ b/test/AsmResolver.DotNet.Tests/Signatures/TypeSignatureTest.cs @@ -46,7 +46,7 @@ public void GetCorLibTypeFullName() public void GetFunctionPointerTypeFullName() { var module = new ModuleDefinition("Dummy"); - Assert.Equal("System.String *(System.Object, System.Int32)", + Assert.Equal("method System.String *(System.Object, System.Int32)", MethodSignature.CreateStatic( module.CorLibTypeFactory.String, module.CorLibTypeFactory.Object, @@ -58,7 +58,7 @@ public void GetFunctionPointerTypeFullName() public void GetInstanceFunctionPointerTypeFullName() { var module = new ModuleDefinition("Dummy"); - Assert.Equal("instance System.String *(System.Object, System.Int32)", + Assert.Equal("method instance System.String *(System.Object, System.Int32)", MethodSignature.CreateInstance( module.CorLibTypeFactory.String, module.CorLibTypeFactory.Object, From 11ad3a2f77585ae0a4d5125be7e123a0cc70f8e7 Mon Sep 17 00:00:00 2001 From: Washi Date: Wed, 7 Dec 2022 13:59:21 +0100 Subject: [PATCH 27/29] Add missing modopt/modreq tests, re-add type arguments in generic instance type name. --- src/AsmResolver.DotNet/MemberNameGenerator.cs | 14 +++++---- .../Types/GenericInstanceTypeSignature.cs | 9 +++++- .../Signatures/TypeSignatureTest.cs | 30 ++++++++++++++++--- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/AsmResolver.DotNet/MemberNameGenerator.cs b/src/AsmResolver.DotNet/MemberNameGenerator.cs index 5612709db..79170b52a 100644 --- a/src/AsmResolver.DotNet/MemberNameGenerator.cs +++ b/src/AsmResolver.DotNet/MemberNameGenerator.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; using AsmResolver.DotNet.Signatures; using AsmResolver.DotNet.Signatures.Types; @@ -22,7 +21,7 @@ private MemberNameGenerator() /// /// Gets the singleton instance for the member name generator. /// - public static MemberNameGenerator Instance + private static MemberNameGenerator Instance { get; } = new(); @@ -283,9 +282,14 @@ private static StringBuilder AppendTypeArgumentPlaceholders(StringBuilder state, if (signature?.GenericParameterCount > 0) { state.Append('<'); - AppendCommaSeparatedCollection(state, - Enumerable.Range(0, signature.GenericParameterCount).ToArray(), - static (s, _) => s.Append('?')); + + for (int i = 0; i < signature.GenericParameterCount; i++) + { + state.Append('?'); + if (i < signature.GenericParameterCount - 1) + state.Append(", "); + } + state.Append('>'); } diff --git a/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs b/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs index 6de88d607..990652f5f 100644 --- a/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/Types/GenericInstanceTypeSignature.cs @@ -84,7 +84,14 @@ public ITypeDefOrRef GenericType public IList TypeArguments => _typeArguments; /// - public override string? Name => GenericType.Name ?? NullTypeToString; + public override string? Name + { + get + { + string genericArgString = string.Join(", ", TypeArguments); + return $"{GenericType.Name ?? NullTypeToString}<{genericArgString}>"; + } + } /// public override string? Namespace => GenericType.Namespace; diff --git a/test/AsmResolver.DotNet.Tests/Signatures/TypeSignatureTest.cs b/test/AsmResolver.DotNet.Tests/Signatures/TypeSignatureTest.cs index cf5ab2bf7..69f25cbe7 100644 --- a/test/AsmResolver.DotNet.Tests/Signatures/TypeSignatureTest.cs +++ b/test/AsmResolver.DotNet.Tests/Signatures/TypeSignatureTest.cs @@ -70,10 +70,12 @@ public void GetInstanceFunctionPointerTypeFullName() public void GetGenericInstanceTypeFullName() { var module = new ModuleDefinition("Dummy"); - Assert.Equal("Namespace.Type>", - _dummyType.MakeGenericInstanceType( - module.CorLibTypeFactory.Int32, - _dummyType.MakeGenericInstanceType(module.CorLibTypeFactory.Object)).FullName); + var genericInstance = _dummyType.MakeGenericInstanceType( + module.CorLibTypeFactory.Int32, + _dummyType.MakeGenericInstanceType(module.CorLibTypeFactory.Object)); + + Assert.Equal("Type>", genericInstance.Name); + Assert.Equal("Namespace.Type>", genericInstance.FullName); } [Fact] @@ -100,5 +102,25 @@ public void GetSzArrayTypeFullName() .MakeSzArrayType() .FullName); } + + [Fact] + public void GetRequiredModifierTypeFullName() + { + Assert.Equal("Namespace.Type modreq(Namespace.Type)", + _dummyType + .ToTypeSignature() + .MakeModifierType(_dummyType, true) + .FullName); + } + + [Fact] + public void GetOptionalModifierTypeFullName() + { + Assert.Equal("Namespace.Type modopt(Namespace.Type)", + _dummyType + .ToTypeSignature() + .MakeModifierType(_dummyType, false) + .FullName); + } } } From 37b4ca7e51644c7983502e06a10952a8f340fac8 Mon Sep 17 00:00:00 2001 From: Washi Date: Wed, 7 Dec 2022 14:27:18 +0100 Subject: [PATCH 28/29] Bump version to 5.0.0 --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 13a85bb60..128e21272 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,7 +7,7 @@ https://github.com/Washi1337/AsmResolver git 10 - 5.0.0-beta.2 + 5.0.0 From 1d39ca1a9b8f5b904cf4add183ce3129f29270a5 Mon Sep 17 00:00:00 2001 From: Washi Date: Wed, 7 Dec 2022 22:00:01 +0100 Subject: [PATCH 29/29] BUGFIX: Ensure stable sorts in sorted metadata table buffers. --- .../Tables/SortedMetadataTableBuffer.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/AsmResolver.DotNet/Builder/Metadata/Tables/SortedMetadataTableBuffer.cs b/src/AsmResolver.DotNet/Builder/Metadata/Tables/SortedMetadataTableBuffer.cs index 7f33b1694..9928a14f1 100644 --- a/src/AsmResolver.DotNet/Builder/Metadata/Tables/SortedMetadataTableBuffer.cs +++ b/src/AsmResolver.DotNet/Builder/Metadata/Tables/SortedMetadataTableBuffer.cs @@ -15,7 +15,14 @@ public class SortedMetadataTableBuffer : ISortedMetadataTableBuffer< where TKey : notnull where TRow : struct, IMetadataRow { - private readonly List<(TKey Key, TRow Row)> _entries = new(); + /// + /// The entries that this table will contain. + /// - Key: The original key to be able to assign metadata tokens easily after sorting. + /// - Row: The metadata row that was constructed for this key. + /// - InputIndex: An index to ensure a stable sort. + /// + private readonly List<(TKey Key, TRow Row, int InputIndex)> _entries = new(); + private readonly Dictionary _newTokens = new(); private readonly MetadataTable _table; private readonly EntryComparer _comparer; @@ -48,7 +55,7 @@ public SortedMetadataTableBuffer(MetadataTable table, int primaryColumn, i /// public void Add(TKey originalKey, in TRow row) { - _entries.Add((originalKey, row)); + _entries.Add((originalKey, row, _entries.Count)); } /// @@ -61,7 +68,7 @@ public void Sort() for (uint rid = 1; rid <= _entries.Count; rid++) { - var (member, _) = _entries[(int) (rid - 1)]; + var member = _entries[(int) (rid - 1)].Key; _newTokens[member] = new MetadataToken(_table.TableIndex, rid); } } @@ -73,6 +80,7 @@ public void Sort() public void FlushToTable() { Sort(); + _table.Clear(); foreach (var row in _entries) _table.Add(row.Row); @@ -85,7 +93,7 @@ public void Clear() _table.Clear(); } - private sealed class EntryComparer : IComparer<(TKey Key, TRow Row)> + private sealed class EntryComparer : IComparer<(TKey Key, TRow Row, int InputIndex)> { private readonly int _primaryColumn; private readonly int _secondaryColumn; @@ -96,11 +104,13 @@ public EntryComparer(int primaryColumn, int secondaryColumn) _secondaryColumn = secondaryColumn; } - public int Compare((TKey Key, TRow Row) x, (TKey Key, TRow Row) y) + public int Compare((TKey Key, TRow Row, int InputIndex) x, (TKey Key, TRow Row, int InputIndex) y) { int result = x.Row[_primaryColumn].CompareTo(y.Row[_primaryColumn]); if (result == 0) result = x.Row[_secondaryColumn].CompareTo(y.Row[_secondaryColumn]); + if (result == 0) + result = x.InputIndex.CompareTo(y.InputIndex); return result; } }