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 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 969ef3f0a..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(in 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/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 - + 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 f699d00fc..d4432896c 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; @@ -34,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; @@ -50,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; @@ -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; } 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; 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; } } diff --git a/src/AsmResolver.DotNet/DefaultMetadataResolver.cs b/src/AsmResolver.DotNet/DefaultMetadataResolver.cs index fcfc69b8b..0d625ed02 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; @@ -222,39 +226,39 @@ 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++) { 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/EventDefinition.cs b/src/AsmResolver.DotNet/EventDefinition.cs index d21d1036b..cc36a78d7 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; @@ -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 39b7e5c3e..0d464fb01 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; @@ -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/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/FullNameGenerator.cs b/src/AsmResolver.DotNet/FullNameGenerator.cs deleted file mode 100644 index fe2409c38..000000000 --- a/src/AsmResolver.DotNet/FullNameGenerator.cs +++ /dev/null @@ -1,148 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using AsmResolver.DotNet.Signatures; -using AsmResolver.DotNet.Signatures.Types; - -namespace AsmResolver.DotNet -{ - /// - /// Provides methods for constructing the full name of a member in a .NET module. - /// - public static class FullNameGenerator - { - /// - /// 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 full name - public static string GetFieldFullName(string? name, ITypeDescriptor? declaringType, FieldSignature? signature) - { - string fieldTypeString = signature?.FieldType.FullName ?? TypeSignature.NullTypeToString; - - return declaringType is null - ? $"{fieldTypeString} {name}" - : $"{fieldTypeString} {declaringType}::{name}"; - } - - /// - /// Computes the full name of a method definition, 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 full name - public static string GetMethodFullName(string? name, ITypeDescriptor? declaringType, MethodSignature? signature) - { - if (signature?.GenericParameterCount > 0) - { - return GetMethodFullName(name, declaringType, signature, - Enumerable.Repeat("?", signature.GenericParameterCount)); - } - - string returnTypeString = signature?.ReturnType.FullName ?? TypeSignature.NullTypeToString; - string parameterTypesString = GetParameterTypesString(signature); - - return declaringType is null - ? $"{returnTypeString} {name}({parameterTypesString})" - : $"{returnTypeString} {declaringType}::{name}({parameterTypesString})"; - } - - /// - /// 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 full name - public static string GetMethodFullName( - string? name, - ITypeDescriptor? declaringType, - MethodSignature? signature, - IEnumerable typeArguments) - { - string returnTypeString = signature?.ReturnType.FullName ?? TypeSignature.NullTypeToString; - string parameterTypesString = GetParameterTypesString(signature); - - string[] argumentNames = typeArguments.ToArray(); - string typeArgumentsString = argumentNames.Length>0 - ? $"<{string.Join(", ", argumentNames)}>" - : string.Empty; - - return declaringType is null - ? $"{returnTypeString} {name}{typeArgumentsString}({parameterTypesString})" - : $"{returnTypeString} {declaringType}::{name}{typeArgumentsString}({parameterTypesString})"; - } - - /// - /// 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 full name - public static string GetPropertyFullName(string? name, ITypeDescriptor? declaringType, PropertySignature? signature) - { - string propertyTypeString = signature?.ReturnType.FullName ?? TypeSignature.NullTypeToString; - string parameterTypesString = signature?.ParameterTypes.Count > 0 - ? $"[{GetParameterTypesString(signature)}]" - : string.Empty; - - return declaringType is null - ? $"{propertyTypeString} {name}{parameterTypesString}" - : $"{propertyTypeString} {declaringType}::{name}{parameterTypesString}"; - } - - /// - /// 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 full name - public static string GetEventFullName(string? name, ITypeDescriptor? declaringType, ITypeDefOrRef? eventType) - { - return declaringType is null - ? $"{eventType} {name}" - : $"{eventType} {declaringType}::{name}"; - } - - private static string GetParameterTypesString(MethodSignatureBase? signature) - { - if (signature is null) - return string.Empty; - - string parametersString = string.Join(", ", signature.ParameterTypes); - string sentinelSuffix = signature.IsSentinel - ? ", ..." - : string.Empty; - - return $"{parametersString}{sentinelSuffix}"; - } - - /// - /// 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(this ITypeDescriptor type) - { - string name = type.Name ?? MetadataMember.NullName; - - if (type.DeclaringType is not null) - return $"{type.DeclaringType.FullName}+{name}"; - - return !string.IsNullOrEmpty(type.Namespace) - ? $"{type.Namespace}.{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 18143ac19..233b45d02 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..80b4485d0 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/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/MemberNameGenerator.cs b/src/AsmResolver.DotNet/MemberNameGenerator.cs new file mode 100644 index 000000000..79170b52a --- /dev/null +++ b/src/AsmResolver.DotNet/MemberNameGenerator.cs @@ -0,0 +1,456 @@ +using System; +using System.Collections.Generic; +using System.Text; +using AsmResolver.DotNet.Signatures; +using AsmResolver.DotNet.Signatures.Types; + +namespace AsmResolver.DotNet +{ + /// + /// Provides methods for constructing the full name of a member in a .NET module. + /// + public sealed class MemberNameGenerator : ITypeSignatureVisitor + { + [ThreadStatic] + private static StringBuilder? _builder; + + private MemberNameGenerator() + { + } + + /// + /// Gets the singleton instance for the member name generator. + /// + private 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. + /// + /// The type to obtain the full name for. + /// The full name. + public static string GetTypeFullName(ITypeDescriptor type) + { + var state = GetBuilder(); + 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 field + /// The full name + public static string GetFieldFullName(IFieldDescriptor descriptor) + { + var state = GetBuilder(); + + AppendTypeFullName(state, descriptor.Signature?.FieldType); + state.Append(' '); + AppendMemberDeclaringType(state, descriptor.DeclaringType); + + return state.Append(descriptor.Name ?? MetadataMember.NullName).ToString(); + } + + /// + /// Computes the full name of a method reference, including its declaring type's full name, as well as its + /// return type and parameters. + /// + /// The reference + /// The full name + public static string GetMethodFullName(MemberReference reference) + { + var state = GetBuilder(); + + 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 = GetBuilder(); + + var signature = definition.Signature; + + AppendTypeFullName(state, signature?.ReturnType); + state.Append(' '); + AppendMemberDeclaringType(state, definition.DeclaringType); + state.Append(definition.Name ?? MetadataMember.NullName); + + 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 specification + /// The full name + public static string GetMethodFullName(MethodSpecification specification) + { + var state = GetBuilder(); + + var signature = specification.Method?.Signature; + + AppendTypeFullName(state, signature?.ReturnType); + state.Append(' '); + AppendMemberDeclaringType(state, specification.DeclaringType); + state.Append(specification.Name); + + 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 property + /// The full name + public static string GetPropertyFullName(PropertyDefinition definition) + { + var state = GetBuilder(); + + var signature = definition.Signature; + + 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 event + /// The full name + public static string GetEventFullName(EventDefinition definition) + { + var state = GetBuilder(); + + 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 StringBuilder AppendSignatureParameterTypes(StringBuilder state, MethodSignatureBase? signature) + { + if (signature is null) + 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(", "); + } + + if (signature.IsSentinel) + state.Append("..."); + + return state; + } + + 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) + { + 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, ITypeDefOrRef? type) + { + 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); + state.Append('+'); + } + else if (!string.IsNullOrEmpty(type.Namespace)) + { + state.Append(type.Namespace); + state.Append('.'); + } + + return state.Append(type.Name ?? MetadataMember.NullName); + } + + private static StringBuilder AppendTypeArgumentPlaceholders(StringBuilder state, MethodSignature? signature) + { + if (signature?.GenericParameterCount > 0) + { + state.Append('<'); + + for (int i = 0; i < signature.GenericParameterCount; i++) + { + state.Append('?'); + if (i < signature.GenericParameterCount - 1) + state.Append(", "); + } + + state.Append('>'); + } + + return state; + } + + private static StringBuilder AppendMethodSignature(StringBuilder state, MethodSignature signature) + { + if (signature.HasThis) + state.Append("instance "); + + 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; + } + + /// + StringBuilder ITypeSignatureVisitor.VisitArrayType(ArrayTypeSignature signature, StringBuilder state) + { + 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) + { + return signature.BaseType.AcceptVisitor(this, state); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitByReferenceType(ByReferenceTypeSignature signature, StringBuilder state) + { + return signature.BaseType + .AcceptVisitor(this, state) + .Append('&'); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitCorLibType(CorLibTypeSignature signature, StringBuilder state) + { + return state.Append("System.").Append(signature.Name); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitCustomModifierType(CustomModifierTypeSignature signature, StringBuilder state) + { + 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) + { + AppendTypeFullName(state, signature.GenericType); + return AppendTypeParameters(state, signature.TypeArguments); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitGenericParameter(GenericParameterSignature signature, StringBuilder state) + { + state.Append(signature.ParameterType switch + { + GenericParameterType.Type => "!", + GenericParameterType.Method => "!!", + _ => throw new ArgumentOutOfRangeException() + }); + + return state.Append(signature.Index); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitPinnedType(PinnedTypeSignature signature, StringBuilder state) + { + return signature.BaseType.AcceptVisitor(this, state); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitPointerType(PointerTypeSignature signature, StringBuilder state) + { + return signature.BaseType + .AcceptVisitor(this, state) + .Append('*'); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitSentinelType(SentinelTypeSignature signature, StringBuilder state) + { + return state.Append("<<>>"); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitSzArrayType(SzArrayTypeSignature signature, StringBuilder state) + { + return signature.BaseType + .AcceptVisitor(this, state) + .Append("[]"); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitTypeDefOrRef(TypeDefOrRefSignature signature, StringBuilder state) + { + return AppendTypeFullName(state, signature.Type); + } + + /// + StringBuilder ITypeSignatureVisitor.VisitFunctionPointerType(FunctionPointerTypeSignature signature, StringBuilder state) + { + state.Append("method "); + return AppendMethodSignature(state, signature.Signature); + } + } +} diff --git a/src/AsmResolver.DotNet/MemberReference.cs b/src/AsmResolver.DotNet/MemberReference.cs index 69a274884..276ebb1a7 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; @@ -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 a65658523..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; @@ -68,7 +67,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; @@ -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/ModuleDefinition.cs b/src/AsmResolver.DotNet/ModuleDefinition.cs index c463b7e2a..13a5c9669 100644 --- a/src/AsmResolver.DotNet/ModuleDefinition.cs +++ b/src/AsmResolver.DotNet/ModuleDefinition.cs @@ -283,7 +283,19 @@ protected ModuleDefinition(MetadataToken token) /// 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. + /// + /// The name of the module. + 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..ac35369ab 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; @@ -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/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 083dd51e9..768f687fd 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 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 1a867ef11..e1250020c 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 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 0d09b8e0a..42675f8ab 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 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 929366651..7878db964 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 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 d2dd3ae74..1d235e66a 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 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 49ab50915..65e82da76 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 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 47f29d5ac..4f05ed1ba 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedTypeSpecification.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedTypeSpecification.cs @@ -41,9 +41,9 @@ public SerializedTypeSpecification(ModuleReaderContext context, MetadataToken to $"Invalid blob signature for type specification {MetadataToken.ToString()}"); } - var context = new BlobReadContext(_context); - context.TraversedTokens.Add(MetadataToken); - return TypeSignature.FromReader(context, ref reader); + 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/BlobReadContext.cs deleted file mode 100644 index 1af93d92d..000000000 --- a/src/AsmResolver.DotNet/Signatures/BlobReadContext.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using AsmResolver.DotNet.Serialized; -using AsmResolver.DotNet.Signatures.Types; -using AsmResolver.PE.DotNet.Metadata.Tables; - -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 - { - /// - /// Creates a new instance of the structure. - /// - /// The original read context. - public BlobReadContext(ModuleReaderContext readerContext) - : this(readerContext, PhysicalTypeSignatureResolver.Instance, Enumerable.Empty()) - { - } - - /// - /// 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) - : this(readerContext, resolver, Enumerable.Empty()) - { - } - - /// - /// 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 HashSet(traversedTokens); - } - - /// - /// Gets the module reader context. - /// - public ModuleReaderContext ReaderContext - { - get; - } - - /// - /// Gets the object responsible for resolving raw type metadata tokens and addresses. - /// - public ITypeSignatureResolver TypeSignatureResolver - { - get; - } - - /// - /// Gets a collection of metadata tokens that were traversed during the parsing of metadata. - /// - public ISet TraversedTokens - { - get; - } - } -} diff --git a/src/AsmResolver.DotNet/Signatures/BlobReaderContext.cs b/src/AsmResolver.DotNet/Signatures/BlobReaderContext.cs new file mode 100644 index 000000000..b9395871a --- /dev/null +++ b/src/AsmResolver.DotNet/Signatures/BlobReaderContext.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using AsmResolver.DotNet.Serialized; +using AsmResolver.DotNet.Signatures.Types; +using AsmResolver.PE.DotNet.Metadata.Tables; + +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 BlobReaderContext + { + private Stack? _traversedTokens; + + /// + /// Creates a new instance of the structure. + /// + /// The original read context. + public BlobReaderContext(ModuleReaderContext readerContext) + : this(readerContext, PhysicalTypeSignatureResolver.Instance) + { + } + + /// + /// Creates a new instance of the structure. + /// + /// The original read context. + /// The object responsible for resolving raw type metadata tokens and addresses. + public BlobReaderContext(ModuleReaderContext readerContext, ITypeSignatureResolver resolver) + { + ReaderContext = readerContext; + TypeSignatureResolver = resolver; + _traversedTokens = null; + } + + /// + /// Gets the module reader context. + /// + public ModuleReaderContext ReaderContext + { + get; + } + + /// + /// Gets the object responsible for resolving raw type metadata tokens and addresses. + /// + 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) + { + 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 12d04b0e2..e58903d30 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 BlobReaderContext 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 BlobReaderContext 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/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 f1d69e005..971839ab8 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 BlobReaderContext 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..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( - in BlobReadContext context, + ref BlobReaderContext 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..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(in BlobReadContext context, ref BinaryStreamReader reader) + public static LocalVariablesSignature FromReader(ref BlobReaderContext 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/Marshal/LPArrayMarshalDescriptor.cs b/src/AsmResolver.DotNet/Signatures/Marshal/LPArrayMarshalDescriptor.cs index 7346b3616..b57d2ed18 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,18 @@ public static LPArrayMarshalDescriptor FromReader(ref BinaryStreamReader reader) return descriptor; } + /// + /// Creates a new empty instance of the class. + /// + 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 +60,7 @@ public LPArrayMarshalDescriptor(NativeType arrayElementType) /// /// Gets the type of elements stored in the array. /// - public NativeType ArrayElementType + public NativeType? ArrayElementType { get; set; @@ -86,20 +97,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/src/AsmResolver.DotNet/Signatures/MethodSignature.cs b/src/AsmResolver.DotNet/Signatures/MethodSignature.cs index 3600c6e32..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(in BlobReadContext context, ref BinaryStreamReader reader) + public static MethodSignature FromReader(ref BlobReaderContext 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..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(in BlobReadContext context, ref BinaryStreamReader reader) + protected void ReadParametersAndReturnType(ref BlobReaderContext 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..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(in 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) @@ -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/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 081ec88e2..682ec1f4e 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 BlobReaderContext 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..990652f5f 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 BlobReaderContext 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; } @@ -89,7 +89,7 @@ public override string? Name get { string genericArgString = string.Join(", ", TypeArguments); - return $"{GenericType?.Name ?? NullTypeToString}<{genericArgString}>"; + return $"{GenericType.Name ?? NullTypeToString}<{genericArgString}>"; } } diff --git a/src/AsmResolver.DotNet/Signatures/Types/ITypeSignatureResolver.cs b/src/AsmResolver.DotNet/Signatures/Types/ITypeSignatureResolver.cs index 9b7faec67..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(in 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(in 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 b85f6b6aa..a934931e8 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 BlobReaderContext 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); @@ -34,6 +34,9 @@ public virtual ITypeDefOrRef ResolveToken(in BlobReadContext context, MetadataTo 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(in BlobReadContext context, MetadataTo } /// - public virtual TypeSignature ResolveRuntimeType(in 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 78ce8e2b9..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 @@ -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 BlobReaderContext 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 BlobReaderContext 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); } /// @@ -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; diff --git a/src/AsmResolver.DotNet/TypeDefinition.cs b/src/AsmResolver.DotNet/TypeDefinition.cs index b9f658544..ffe56e169 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; @@ -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/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. diff --git a/src/AsmResolver.DotNet/TypeReference.cs b/src/AsmResolver.DotNet/TypeReference.cs index fef21335e..2db296a1a 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; @@ -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; 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: 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..0ee3f5782 --- /dev/null +++ b/src/AsmResolver.Symbols.Pdb/Records/ProcedureReferenceSymbol.cs @@ -0,0 +1,101 @@ +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; + } + } + + /// + /// 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. + /// + 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(); +} 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; /// 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; 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); } } } 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..28574f7f7 100644 --- a/test/AsmResolver.DotNet.Dynamic.Tests/AsmResolver.DotNet.Dynamic.Tests.csproj +++ b/test/AsmResolver.DotNet.Dynamic.Tests/AsmResolver.DotNet.Dynamic.Tests.csproj @@ -8,9 +8,9 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/AsmResolver.DotNet.Tests/AsmResolver.DotNet.Tests.csproj b/test/AsmResolver.DotNet.Tests/AsmResolver.DotNet.Tests.csproj index 830749a8d..393b05e33 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 @@ -9,7 +9,7 @@ - + all 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); + } } } 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/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 +} 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 diff --git a/test/AsmResolver.DotNet.Tests/Signatures/TypeSignatureTest.cs b/test/AsmResolver.DotNet.Tests/Signatures/TypeSignatureTest.cs new file mode 100644 index 000000000..69f25cbe7 --- /dev/null +++ b/test/AsmResolver.DotNet.Tests/Signatures/TypeSignatureTest.cs @@ -0,0 +1,126 @@ +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("method 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("method 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"); + var genericInstance = _dummyType.MakeGenericInstanceType( + module.CorLibTypeFactory.Int32, + _dummyType.MakeGenericInstanceType(module.CorLibTypeFactory.Object)); + + Assert.Equal("Type>", genericInstance.Name); + Assert.Equal("Namespace.Type>", genericInstance.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); + } + + [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); + } + } +} 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() { 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..2c4967fcf 100644 --- a/test/AsmResolver.PE.Tests/AsmResolver.PE.Tests.csproj +++ b/test/AsmResolver.PE.Tests/AsmResolver.PE.Tests.csproj @@ -16,9 +16,9 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive 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..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,13 +8,13 @@ - + 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..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,13 +8,13 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all 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..ae534649b --- /dev/null +++ b/test/AsmResolver.Symbols.Pdb.Tests/Records/ProcedureReferenceSymbolTest.cs @@ -0,0 +1,27 @@ +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(s => !s.IsLocal).Name); + } + + [Fact] + public void LocalName() + { + Assert.Equal("__get_entropy", _fixture.SimplePdb.Symbols.OfType().First(s => s.IsLocal).Name); + } +} 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