Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nullable reference types #435

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Examples/Examples.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<OutputType>Exe</OutputType>
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

.NET module/assembly reader/writer library

v4 breaking changes compared to v3
----------------------------------

- Nullable reference types was enabled. Some things that used to return `null` may now return some default value, eg. when it has some invalid metadata.
- `UTF8String(string)` constructor doesn't allow a `null` argument.
- Some constructors that could be called without initializing all non-nullable reference type fields have been removed or updated to take more parameters.
- TODO:

Opening a .NET assembly/module
------------------------------

Expand Down
9 changes: 6 additions & 3 deletions src/DotNet/AssemblyDef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using dnlib.DotNet.Pdb;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace dnlib.DotNet {
/// <summary>
Expand Down Expand Up @@ -155,8 +156,9 @@ public CustomAttributeCollection CustomAttributes {
}
}
/// <summary/>
protected CustomAttributeCollection customAttributes;
protected CustomAttributeCollection? customAttributes;
/// <summary>Initializes <see cref="customAttributes"/></summary>
[MemberNotNull(nameof(customAttributes))]
protected virtual void InitializeCustomAttributes() =>
Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null);

Expand All @@ -181,8 +183,9 @@ public IList<PdbCustomDebugInfo> CustomDebugInfos {
}
}
/// <summary/>
protected IList<PdbCustomDebugInfo> customDebugInfos;
protected IList<PdbCustomDebugInfo>? customDebugInfos;
/// <summary>Initializes <see cref="customDebugInfos"/></summary>
[MemberNotNull(nameof(customDebugInfos))]
protected virtual void InitializeCustomDebugInfos() =>
Interlocked.CompareExchange(ref customDebugInfos, new List<PdbCustomDebugInfo>(), null);
/// <inheritdoc/>
Expand All @@ -197,7 +200,7 @@ protected virtual void InitializeCustomDebugInfos() =>
/// Gets the manifest (main) module. This is always the first module in <see cref="Modules"/>.
/// <c>null</c> is returned if <see cref="Modules"/> is empty.
/// </summary>
public ModuleDef ManifestModule => Modules.Count == 0 ? null : Modules[0];
public ModuleDef? ManifestModule => Modules.Count == 0 ? null : Modules[0];

/// <summary>
/// Modify <see cref="attributes"/> property: <see cref="attributes"/> =
Expand Down
9 changes: 3 additions & 6 deletions src/DotNet/AssemblyHash.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,8 @@ public void Dispose() {
/// <see cref="AssemblyHashAlgorithm.SHA1"/> will be used as the hash algorithm.</remarks>
/// <param name="data">The data</param>
/// <param name="hashAlgo">The algorithm to use</param>
/// <returns>Hashed data or null if <paramref name="data"/> was <c>null</c></returns>
/// <returns>Hashed data</returns>
public static byte[] Hash(byte[] data, AssemblyHashAlgorithm hashAlgo) {
if (data is null)
return null;

using (var asmHash = new AssemblyHash(hashAlgo)) {
asmHash.Hash(data);
return asmHash.ComputeHash();
Expand Down Expand Up @@ -88,7 +85,7 @@ public void Hash(Stream stream, uint length, byte[] buffer) {
/// </summary>
public byte[] ComputeHash() {
hasher.TransformFinalBlock(Array2.Empty<byte>(), 0, 0);
return hasher.Hash;
return hasher.Hash ?? throw new InvalidOperationException();
}

/// <summary>
Expand All @@ -98,7 +95,7 @@ public byte[] ComputeHash() {
/// order, is used as the public key token</remarks>
/// <param name="publicKeyData">The data</param>
/// <returns>A new <see cref="PublicKeyToken"/> instance</returns>
public static PublicKeyToken CreatePublicKeyToken(byte[] publicKeyData) {
public static PublicKeyToken CreatePublicKeyToken(byte[]? publicKeyData) {
if (publicKeyData is null)
return new PublicKeyToken();
var hash = Hash(publicKeyData, AssemblyHashAlgorithm.SHA1);
Expand Down
2 changes: 1 addition & 1 deletion src/DotNet/AssemblyHashAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public enum AssemblyHashAlgorithm : uint {
}

public static partial class Extensions {
internal static string GetName(this AssemblyHashAlgorithm hashAlg) =>
internal static string? GetName(this AssemblyHashAlgorithm hashAlg) =>
hashAlg switch {
AssemblyHashAlgorithm.MD2 => null,
AssemblyHashAlgorithm.MD4 => null,
Expand Down
10 changes: 5 additions & 5 deletions src/DotNet/AssemblyNameComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public enum AssemblyNameComparerFlags {
/// <param name="a">First</param>
/// <param name="b">Second</param>
/// <returns>&lt; 0 if a &lt; b, 0 if a == b, &gt; 0 if a &gt; b</returns>
public int CompareTo(IAssembly a, IAssembly b) {
public int CompareTo(IAssembly? a, IAssembly? b) {
if (a == b)
return 0;
if (a is null)
Expand Down Expand Up @@ -128,7 +128,7 @@ public int CompareTo(IAssembly a, IAssembly b) {
/// <param name="a">First</param>
/// <param name="b">Second</param>
/// <returns><c>true</c> if equal, <c>false</c> otherwise</returns>
public bool Equals(IAssembly a, IAssembly b) => CompareTo(a, b) == 0;
public bool Equals(IAssembly? a, IAssembly? b) => CompareTo(a, b) == 0;

/// <summary>
/// Figures out which of two assembly names is closer to another assembly name
Expand All @@ -138,11 +138,11 @@ public int CompareTo(IAssembly a, IAssembly b) {
/// <param name="b">Second</param>
/// <returns>-1 if both are equally close, 0 if <paramref name="a"/> is closest, 1 if
/// <paramref name="b"/> is closest</returns>
public int CompareClosest(IAssembly requested, IAssembly a, IAssembly b) {
public int CompareClosest(IAssembly requested, IAssembly? a, IAssembly? b) {
if (a == b)
return 0;
if (a is null)
return !CompareName ? 1 : UTF8String.CaseInsensitiveEquals(requested.Name, b.Name) ? 1 : 0;
return !CompareName ? 1 : UTF8String.CaseInsensitiveEquals(requested.Name, b?.Name) ? 1 : 0;
if (b is null)
return !CompareName ? 0 : UTF8String.CaseInsensitiveEquals(requested.Name, a.Name) ? 0 : 1;

Expand Down Expand Up @@ -236,7 +236,7 @@ public int CompareClosest(IAssembly requested, IAssembly a, IAssembly b) {
/// </summary>
/// <param name="a">Assembly name</param>
/// <returns>The hash code</returns>
public int GetHashCode(IAssembly a) {
public int GetHashCode(IAssembly? a) {
if (a is null)
return 0;

Expand Down
8 changes: 4 additions & 4 deletions src/DotNet/AssemblyNameInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ namespace dnlib.DotNet {
/// </summary>
public sealed class AssemblyNameInfo : IAssembly {
AssemblyHashAlgorithm hashAlgId;
Version version;
Version? version;
AssemblyAttributes flags;
PublicKeyBase publicKeyOrToken;
UTF8String name;
UTF8String culture;
UTF8String? culture;

/// <summary>
/// Gets/sets the <see cref="AssemblyHashAlgorithm"/>
Expand All @@ -26,7 +26,7 @@ public AssemblyHashAlgorithm HashAlgId {
/// <summary>
/// Gets/sets the <see cref="Version"/> or <c>null</c> if none specified
/// </summary>
public Version Version {
public Version? Version {
get => version;
set => version = value;
}
Expand Down Expand Up @@ -58,7 +58,7 @@ public UTF8String Name {
/// <summary>
/// Gets/sets the culture or <c>null</c> if none specified
/// </summary>
public UTF8String Culture {
public UTF8String? Culture {
get => culture;
set => culture = value;
}
Expand Down
7 changes: 5 additions & 2 deletions src/DotNet/AssemblyRef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Threading;
using dnlib.DotNet.MD;
Expand Down Expand Up @@ -123,8 +124,9 @@ public CustomAttributeCollection CustomAttributes {
}
}
/// <summary/>
protected CustomAttributeCollection customAttributes;
protected CustomAttributeCollection? customAttributes;
/// <summary>Initializes <see cref="customAttributes"/></summary>
[MemberNotNull(nameof(customAttributes))]
protected virtual void InitializeCustomAttributes() =>
Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null);

Expand All @@ -148,8 +150,9 @@ public IList<PdbCustomDebugInfo> CustomDebugInfos {
}
}
/// <summary/>
protected IList<PdbCustomDebugInfo> customDebugInfos;
protected IList<PdbCustomDebugInfo>? customDebugInfos;
/// <summary>Initializes <see cref="customDebugInfos"/></summary>
[MemberNotNull(nameof(customDebugInfos))]
protected virtual void InitializeCustomDebugInfos() =>
Interlocked.CompareExchange(ref customDebugInfos, new List<PdbCustomDebugInfo>(), null);

Expand Down
32 changes: 16 additions & 16 deletions src/DotNet/AssemblyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ public AssemblyResolver(ModuleContext defaultModuleContext) {
}

/// <inheritdoc/>
public AssemblyDef Resolve(IAssembly assembly, ModuleDef sourceModule) {
public AssemblyDef? Resolve(IAssembly? assembly, ModuleDef? sourceModule) {
if (assembly is null)
return null;

Expand Down Expand Up @@ -387,7 +387,7 @@ static string GetAssemblyNameKey(IAssembly asmName) {
return asmName.FullNameToken;
}

AssemblyDef Resolve2(IAssembly assembly, ModuleDef sourceModule) {
AssemblyDef Resolve2(IAssembly assembly, ModuleDef? sourceModule) {
if (cachedAssemblies.TryGetValue(GetAssemblyNameKey(assembly), out var resolvedAssembly))
return resolvedAssembly;

Expand Down Expand Up @@ -530,7 +530,7 @@ IEnumerable<string> FindAssemblies2(IAssembly assembly, IEnumerable<string> path
/// <param name="sourceModule">The module that needs to resolve an assembly or <c>null</c></param>
/// <param name="matchExactly">We're trying to find an exact match</param>
/// <returns><c>null</c> or an enumerable of full paths to try</returns>
protected virtual IEnumerable<string> PreFindAssemblies(IAssembly assembly, ModuleDef sourceModule, bool matchExactly) {
protected virtual IEnumerable<string> PreFindAssemblies(IAssembly assembly, ModuleDef? sourceModule, bool matchExactly) {
foreach (var path in FindAssemblies2(assembly, preSearchPaths))
yield return path;
}
Expand All @@ -542,7 +542,7 @@ protected virtual IEnumerable<string> PreFindAssemblies(IAssembly assembly, Modu
/// <param name="sourceModule">The module that needs to resolve an assembly or <c>null</c></param>
/// <param name="matchExactly">We're trying to find an exact match</param>
/// <returns><c>null</c> or an enumerable of full paths to try</returns>
protected virtual IEnumerable<string> PostFindAssemblies(IAssembly assembly, ModuleDef sourceModule, bool matchExactly) {
protected virtual IEnumerable<string> PostFindAssemblies(IAssembly assembly, ModuleDef? sourceModule, bool matchExactly) {
foreach (var path in FindAssemblies2(assembly, postSearchPaths))
yield return path;
}
Expand All @@ -554,7 +554,7 @@ protected virtual IEnumerable<string> PostFindAssemblies(IAssembly assembly, Mod
/// <param name="sourceModule">The module that needs to resolve an assembly or <c>null</c></param>
/// <param name="matchExactly">We're trying to find an exact match</param>
/// <returns><c>null</c> or an enumerable of full paths to try</returns>
protected virtual IEnumerable<string> FindAssemblies(IAssembly assembly, ModuleDef sourceModule, bool matchExactly) {
protected virtual IEnumerable<string> FindAssemblies(IAssembly assembly, ModuleDef? sourceModule, bool matchExactly) {
if (assembly.IsContentTypeWindowsRuntime) {
string path;
try {
Expand All @@ -577,13 +577,13 @@ protected virtual IEnumerable<string> FindAssemblies(IAssembly assembly, ModuleD
yield return path;
}

IEnumerable<string> FindAssembliesGac(IAssembly assembly, ModuleDef sourceModule, bool matchExactly) {
IEnumerable<string> FindAssembliesGac(IAssembly assembly, ModuleDef? sourceModule, bool matchExactly) {
if (matchExactly)
return FindAssembliesGacExactly(assembly, sourceModule);
return FindAssembliesGacAny(assembly, sourceModule);
}

IEnumerable<GacInfo> GetGacInfos(ModuleDef sourceModule) {
IEnumerable<GacInfo> GetGacInfos(ModuleDef? sourceModule) {
int version = sourceModule is null ? int.MinValue : sourceModule.IsClr40 ? 4 : 2;
// Try the correct GAC first (eg. GAC4 if it's a .NET Framework 4 assembly)
foreach (var gacInfo in gacInfos) {
Expand All @@ -596,7 +596,7 @@ IEnumerable<GacInfo> GetGacInfos(ModuleDef sourceModule) {
}
}

IEnumerable<string> FindAssembliesGacExactly(IAssembly assembly, ModuleDef sourceModule) {
IEnumerable<string> FindAssembliesGacExactly(IAssembly assembly, ModuleDef? sourceModule) {
foreach (var gacInfo in GetGacInfos(sourceModule)) {
foreach (var path in FindAssembliesGacExactly(gacInfo, assembly, sourceModule))
yield return path;
Expand All @@ -607,7 +607,7 @@ IEnumerable<string> FindAssembliesGacExactly(IAssembly assembly, ModuleDef sourc
}
}

static IEnumerable<string> GetExtraMonoPaths(IAssembly assembly, ModuleDef sourceModule) {
static IEnumerable<string> GetExtraMonoPaths(IAssembly assembly, ModuleDef? sourceModule) {
if (extraMonoPaths is not null) {
foreach (var dir in extraMonoPaths) {
string file;
Expand All @@ -624,7 +624,7 @@ static IEnumerable<string> GetExtraMonoPaths(IAssembly assembly, ModuleDef sourc
}
}

IEnumerable<string> FindAssembliesGacExactly(GacInfo gacInfo, IAssembly assembly, ModuleDef sourceModule) {
IEnumerable<string> FindAssembliesGacExactly(GacInfo gacInfo, IAssembly assembly, ModuleDef? sourceModule) {
var pkt = PublicKeyBase.ToPublicKeyToken(assembly.PublicKeyOrToken);
if (gacInfo is not null && pkt is not null) {
string pktString = pkt.ToString();
Expand All @@ -650,7 +650,7 @@ IEnumerable<string> FindAssembliesGacExactly(GacInfo gacInfo, IAssembly assembly
}
}

IEnumerable<string> FindAssembliesGacAny(IAssembly assembly, ModuleDef sourceModule) {
IEnumerable<string> FindAssembliesGacAny(IAssembly assembly, ModuleDef? sourceModule) {
foreach (var gacInfo in GetGacInfos(sourceModule)) {
foreach (var path in FindAssembliesGacAny(gacInfo, assembly, sourceModule))
yield return path;
Expand All @@ -661,7 +661,7 @@ IEnumerable<string> FindAssembliesGacAny(IAssembly assembly, ModuleDef sourceMod
}
}

IEnumerable<string> FindAssembliesGacAny(GacInfo gacInfo, IAssembly assembly, ModuleDef sourceModule) {
IEnumerable<string> FindAssembliesGacAny(GacInfo gacInfo, IAssembly assembly, ModuleDef? sourceModule) {
if (gacInfo is not null) {
var asmSimpleName = UTF8String.ToSystemStringOrEmpty(assembly.Name);
foreach (var subDir in gacInfo.SubDirs) {
Expand Down Expand Up @@ -695,7 +695,7 @@ IEnumerable<string> GetDirs(string baseDir) {
return dirs;
}

IEnumerable<string> FindAssembliesModuleSearchPaths(IAssembly assembly, ModuleDef sourceModule, bool matchExactly) {
IEnumerable<string> FindAssembliesModuleSearchPaths(IAssembly assembly, ModuleDef? sourceModule, bool matchExactly) {
string asmSimpleName = UTF8String.ToSystemStringOrEmpty(assembly.Name);
var searchPaths = GetSearchPaths(sourceModule);
var exts = assembly.IsContentTypeWindowsRuntime ? winMDAssemblyExtensions : assemblyExtensions;
Expand Down Expand Up @@ -725,7 +725,7 @@ IEnumerable<string> FindAssembliesModuleSearchPaths(IAssembly assembly, ModuleDe
/// </summary>
/// <param name="module">The module or <c>null</c> if unknown</param>
/// <returns>A list of all search paths to use for this module</returns>
IEnumerable<string> GetSearchPaths(ModuleDef module) {
IEnumerable<string> GetSearchPaths(ModuleDef? module) {
var keyModule = module;
if (keyModule is null)
keyModule = nullModule;
Expand All @@ -742,14 +742,14 @@ IEnumerable<string> GetSearchPaths(ModuleDef module) {
/// </summary>
/// <param name="module">The module or <c>null</c> if unknown</param>
/// <returns>A list of search paths</returns>
protected virtual IEnumerable<string> GetModuleSearchPaths(ModuleDef module) => GetModulePrivateSearchPaths(module);
protected virtual IEnumerable<string> GetModuleSearchPaths(ModuleDef? module) => GetModulePrivateSearchPaths(module);

/// <summary>
/// Gets all private assembly search paths as found in the module's <c>.config</c> file.
/// </summary>
/// <param name="module">The module or <c>null</c> if unknown</param>
/// <returns>A list of search paths</returns>
protected IEnumerable<string> GetModulePrivateSearchPaths(ModuleDef module) {
protected IEnumerable<string> GetModulePrivateSearchPaths(ModuleDef? module) {
if (module is null)
return Array2.Empty<string>();
var asm = module.Assembly;
Expand Down
6 changes: 3 additions & 3 deletions src/DotNet/Constant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ public ElementType Type {
/// <summary>
/// From column Constant.Value
/// </summary>
public object Value {
public object? Value {
get => value;
set => this.value = value;
}
/// <summary/>
protected object value;
protected object? value;
}

/// <summary>
Expand Down Expand Up @@ -128,7 +128,7 @@ public ConstantMD(ModuleDefMD readerModule, uint rid) {
value = GetValue(type, ref reader);
}

static object GetValue(ElementType etype, ref DataReader reader) {
static object? GetValue(ElementType etype, ref DataReader reader) {
switch (etype) {
case ElementType.Boolean:
if (reader.Length < 1)
Expand Down
10 changes: 3 additions & 7 deletions src/DotNet/CorLibTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,9 @@ public CorLibTypes(ModuleDef module)
/// <param name="module">The owner module</param>
/// <param name="corLibAssemblyRef">Corlib assembly reference or <c>null</c> if a default
/// assembly reference should be created</param>
public CorLibTypes(ModuleDef module, AssemblyRef corLibAssemblyRef) {
public CorLibTypes(ModuleDef module, AssemblyRef? corLibAssemblyRef) {
this.module = module;
this.corLibAssemblyRef = corLibAssemblyRef ?? CreateCorLibAssemblyRef();
Initialize();
}

AssemblyRef CreateCorLibAssemblyRef() => module.UpdateRowId(AssemblyRefUser.CreateMscorlibReferenceCLR20());

void Initialize() {
bool isCorLib = module.Assembly.IsCorLib();
typeVoid = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Void"), ElementType.Void);
typeBoolean = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Boolean"), ElementType.Boolean);
Expand All @@ -127,6 +121,8 @@ void Initialize() {
typeObject = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Object"), ElementType.Object);
}

AssemblyRef CreateCorLibAssemblyRef() => module.UpdateRowId(AssemblyRefUser.CreateMscorlibReferenceCLR20());

ITypeDefOrRef CreateCorLibTypeRef(bool isCorLib, string name) {
var tr = new TypeRefUser(module, "System", name, corLibAssemblyRef);
if (isCorLib) {
Expand Down
Loading