diff --git a/src/DotNet/ModuleDefMD.cs b/src/DotNet/ModuleDefMD.cs index 99c194cf9..2918b4b8b 100644 --- a/src/DotNet/ModuleDefMD.cs +++ b/src/DotNet/ModuleDefMD.cs @@ -1478,17 +1478,17 @@ Resource CreateResource(uint rid) { if (token.Rid == 0) { if (TryCreateResourceStream(mr.Offset, out var dataReaderFactory, out uint resourceOffset, out uint resourceLength)) - return new EmbeddedResource(mr.Name, dataReaderFactory, resourceOffset, resourceLength, mr.Flags) { Rid = rid, Offset = mr.Offset }; - return new EmbeddedResource(mr.Name, Array2.Empty(), mr.Flags) { Rid = rid, Offset = mr.Offset }; + return new EmbeddedResourceMD(this, mr, dataReaderFactory, resourceOffset, resourceLength); + return new EmbeddedResourceMD(this, mr, Array2.Empty()); } if (mr.Implementation is FileDef file) - return new LinkedResource(mr.Name, file, mr.Flags) { Rid = rid, Offset = mr.Offset }; + return new LinkedResourceMD(this, mr, file); if (mr.Implementation is AssemblyRef asmRef) - return new AssemblyLinkedResource(mr.Name, asmRef, mr.Flags) { Rid = rid, Offset = mr.Offset }; + return new AssemblyLinkedResourceMD(this, mr, asmRef); - return new EmbeddedResource(mr.Name, Array2.Empty(), mr.Flags) { Rid = rid, Offset = mr.Offset }; + return new EmbeddedResourceMD(this, mr, Array2.Empty()); } [HandleProcessCorruptedStateExceptions, SecurityCritical] // Req'd on .NET 4.0 diff --git a/src/DotNet/ModuleLoader.cs b/src/DotNet/ModuleLoader.cs index f723d9b20..16ba05561 100644 --- a/src/DotNet/ModuleLoader.cs +++ b/src/DotNet/ModuleLoader.cs @@ -542,6 +542,7 @@ void Load(Resource obj) { Add(obj.Offset); Add(obj.Name); Add(obj.Attributes); + Add(obj.CustomAttributes); switch (obj.ResourceType) { case ResourceType.Embedded: diff --git a/src/DotNet/Resource.cs b/src/DotNet/Resource.cs index 4f48033b9..58bb2e11a 100644 --- a/src/DotNet/Resource.cs +++ b/src/DotNet/Resource.cs @@ -1,8 +1,11 @@ // dnlib: See LICENSE.txt for more info using System; +using System.Collections.Generic; +using System.Threading; using dnlib.IO; using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb; namespace dnlib.DotNet { /// @@ -28,9 +31,9 @@ public enum ResourceType { /// /// Resource base class /// - public abstract class Resource : IMDTokenProvider { - uint rid; - uint? offset; + public abstract class Resource : IMDTokenProvider, IHasCustomAttribute, IHasCustomDebugInformation { + private protected uint rid; + private protected uint? offset; UTF8String name; ManifestResourceAttributes flags; @@ -90,6 +93,50 @@ public ManifestResourceAttributes Visibility { /// public bool IsPrivate => (flags & ManifestResourceAttributes.VisibilityMask) == ManifestResourceAttributes.Private; + /// + public int HasCustomAttributeTag => 18; + + /// + /// Gets all custom attributes + /// + public CustomAttributeCollection CustomAttributes { + get { + if (customAttributes is null) + InitializeCustomAttributes(); + return customAttributes; + } + } + /// + protected CustomAttributeCollection customAttributes; + /// Initializes + protected virtual void InitializeCustomAttributes() => + Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); + + /// + public bool HasCustomAttributes => CustomAttributes.Count > 0; + + /// + public int HasCustomDebugInformationTag => 18; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } + } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + /// /// Constructor /// @@ -104,7 +151,7 @@ protected Resource(UTF8String name, ManifestResourceAttributes flags) { /// /// A resource that is embedded in a .NET module. This is the most common type of resource. /// - public sealed class EmbeddedResource : Resource { + public class EmbeddedResource : Resource { readonly DataReaderFactory dataReaderFactory; readonly uint resourceStartOffset; readonly uint resourceLength; @@ -152,10 +199,45 @@ public EmbeddedResource(UTF8String name, DataReaderFactory dataReaderFactory, ui public override string ToString() => $"{UTF8String.ToSystemStringOrEmpty(Name)} - size: {(resourceLength)}"; } + sealed class EmbeddedResourceMD : EmbeddedResource, IMDTokenProviderMD { + /// The module where this instance is located + readonly ModuleDefMD readerModule; + + readonly uint origRid; + + /// + public uint OrigRid => origRid; + + /// + protected override void InitializeCustomAttributes() { + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.ManifestResource, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); + Interlocked.CompareExchange(ref customAttributes, tmp, null); + } + + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + + public EmbeddedResourceMD(ModuleDefMD readerModule, ManifestResource mr, byte[] data) + : this(readerModule, mr, ByteArrayDataReaderFactory.Create(data, filename: null), 0, (uint)data.Length) { + } + + public EmbeddedResourceMD(ModuleDefMD readerModule, ManifestResource mr, DataReaderFactory dataReaderFactory, uint offset, uint length) + : base(mr.Name, dataReaderFactory, offset, length, mr.Flags) { + this.readerModule = readerModule; + origRid = rid = mr.Rid; + this.offset = mr.Offset; + } + } + /// /// A reference to a resource in another assembly /// - public sealed class AssemblyLinkedResource : Resource { + public class AssemblyLinkedResource : Resource { AssemblyRef asmRef; /// @@ -182,10 +264,40 @@ public AssemblyLinkedResource(UTF8String name, AssemblyRef asmRef, ManifestResou public override string ToString() => $"{UTF8String.ToSystemStringOrEmpty(Name)} - assembly: {asmRef.FullName}"; } + sealed class AssemblyLinkedResourceMD : AssemblyLinkedResource, IMDTokenProviderMD { + /// The module where this instance is located + readonly ModuleDefMD readerModule; + + readonly uint origRid; + + /// + public uint OrigRid => origRid; + + /// + protected override void InitializeCustomAttributes() { + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.ManifestResource, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); + Interlocked.CompareExchange(ref customAttributes, tmp, null); + } + + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + + public AssemblyLinkedResourceMD(ModuleDefMD readerModule, ManifestResource mr, AssemblyRef asmRef) : base(mr.Name, asmRef, mr.Flags) { + this.readerModule = readerModule; + origRid = rid = mr.Rid; + offset = mr.Offset; + } + } + /// /// A resource that is stored in a file on disk /// - public sealed class LinkedResource : Resource { + public class LinkedResource : Resource { FileDef file; /// @@ -224,4 +336,34 @@ public LinkedResource(UTF8String name, FileDef file, ManifestResourceAttributes /// public override string ToString() => $"{UTF8String.ToSystemStringOrEmpty(Name)} - file: {UTF8String.ToSystemStringOrEmpty(FileName)}"; } + + sealed class LinkedResourceMD : LinkedResource, IMDTokenProviderMD { + /// The module where this instance is located + readonly ModuleDefMD readerModule; + + readonly uint origRid; + + /// + public uint OrigRid => origRid; + + /// + protected override void InitializeCustomAttributes() { + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.ManifestResource, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); + Interlocked.CompareExchange(ref customAttributes, tmp, null); + } + + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + + public LinkedResourceMD(ModuleDefMD readerModule, ManifestResource mr, FileDef file) : base(mr.Name, file, mr.Flags) { + this.readerModule = readerModule; + origRid = rid = mr.Rid; + offset = mr.Offset; + } + } } diff --git a/src/DotNet/Writer/Metadata.cs b/src/DotNet/Writer/Metadata.cs index eacb25b41..f6d4ca802 100644 --- a/src/DotNet/Writer/Metadata.cs +++ b/src/DotNet/Writer/Metadata.cs @@ -2980,8 +2980,8 @@ uint AddEmbeddedResource(EmbeddedResource er) { rid = tablesHeap.ManifestResourceTable.Add(row); manifestResourceInfos.Add(er, rid); embeddedResourceToByteArray[er] = netResources.Add(er.CreateReader()); - //TODO: Add custom attributes - //TODO: Add custom debug infos + AddCustomAttributes(Table.ManifestResource, rid, er); + AddCustomDebugInformationList(Table.ManifestResource, rid, er); return rid; } @@ -2999,8 +2999,8 @@ uint AddAssemblyLinkedResource(AssemblyLinkedResource alr) { AddImplementation(alr.Assembly)); rid = tablesHeap.ManifestResourceTable.Add(row); manifestResourceInfos.Add(alr, rid); - //TODO: Add custom attributes - //TODO: Add custom debug infos + AddCustomAttributes(Table.ManifestResource, rid, alr); + AddCustomDebugInformationList(Table.ManifestResource, rid, alr); return rid; } @@ -3018,8 +3018,8 @@ uint AddLinkedResource(LinkedResource lr) { AddImplementation(lr.File)); rid = tablesHeap.ManifestResourceTable.Add(row); manifestResourceInfos.Add(lr, rid); - //TODO: Add custom attributes - //TODO: Add custom debug infos + AddCustomAttributes(Table.ManifestResource, rid, lr); + AddCustomDebugInformationList(Table.ManifestResource, rid, lr); return rid; }