Browse Source

RecordDecompiler: nullable enable

pull/3622/head
Siegfried Pammer 1 month ago
parent
commit
2bb06709e9
  1. 65
      ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs

65
ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs

@ -16,9 +16,12 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
#nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using System.Threading; using System.Threading;
@ -40,8 +43,8 @@ namespace ICSharpCode.Decompiler.CSharp
readonly bool isInheritedRecord; readonly bool isInheritedRecord;
readonly bool isStruct; readonly bool isStruct;
readonly bool isSealed; readonly bool isSealed;
readonly IMethod primaryCtor; readonly IMethod? primaryCtor;
readonly IType baseClass; readonly IType? baseClass;
readonly Dictionary<IField, IProperty> backingFieldToAutoProperty = new Dictionary<IField, IProperty>(); readonly Dictionary<IField, IProperty> backingFieldToAutoProperty = new Dictionary<IField, IProperty>();
readonly Dictionary<IProperty, IField> autoPropertyToBackingField = new Dictionary<IProperty, IField>(); readonly Dictionary<IProperty, IField> autoPropertyToBackingField = new Dictionary<IProperty, IField>();
readonly Dictionary<IParameter, IProperty> primaryCtorParameterToAutoProperty = new Dictionary<IParameter, IProperty>(); readonly Dictionary<IParameter, IProperty> primaryCtorParameterToAutoProperty = new Dictionary<IParameter, IProperty>();
@ -76,7 +79,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
bool IsAutoProperty(IProperty p, out IField field) bool IsAutoProperty(IProperty p, [NotNullWhen(true)] out IField? field)
{ {
field = null; field = null;
if (p.IsStatic) if (p.IsStatic)
@ -109,7 +112,7 @@ namespace ICSharpCode.Decompiler.CSharp
return field.Name == $"<{p.Name}>k__BackingField"; return field.Name == $"<{p.Name}>k__BackingField";
} }
bool IsAutoGetter(IMethod method, out IField field) bool IsAutoGetter(IMethod method, [NotNullWhen(true)] out IField? field)
{ {
field = null; field = null;
var body = DecompileBody(method); var body = DecompileBody(method);
@ -130,7 +133,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
bool IsAutoSetter(IMethod method, out IField field) bool IsAutoSetter(IMethod method, [NotNullWhen(true)] out IField? field)
{ {
field = null; field = null;
Debug.Assert(!method.IsStatic); Debug.Assert(!method.IsStatic);
@ -138,7 +141,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (body == null) if (body == null)
return false; return false;
// this.field = value; // this.field = value;
ILInstruction valueInst; ILInstruction? valueInst;
if (method.IsStatic) if (method.IsStatic)
{ {
if (!body.Instructions[0].MatchStsFld(out field, out valueInst)) if (!body.Instructions[0].MatchStsFld(out field, out valueInst))
@ -159,7 +162,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
IMethod DetectPrimaryConstructor() IMethod? DetectPrimaryConstructor()
{ {
Debug.Assert(recordTypeDef.IsRecord); Debug.Assert(recordTypeDef.IsRecord);
if (!settings.UsePrimaryConstructorSyntax) if (!settings.UsePrimaryConstructorSyntax)
@ -168,14 +171,14 @@ namespace ICSharpCode.Decompiler.CSharp
var subst = recordTypeDef.AsParameterizedType().GetSubstitution(); var subst = recordTypeDef.AsParameterizedType().GetSubstitution();
Dictionary<string, IMember> nameToMemberMap = new Dictionary<string, IMember>(StringComparer.Ordinal); Dictionary<string, IMember> nameToMemberMap = new Dictionary<string, IMember>(StringComparer.Ordinal);
Dictionary<IMethod, IMethod> ctorChainMap = new Dictionary<IMethod, IMethod>(); Dictionary<IMethod, IMethod?> ctorChainMap = new Dictionary<IMethod, IMethod?>();
foreach (IMember m in recordTypeDef.GetMembers(m => m.SymbolKind is SymbolKind.Property or SymbolKind.Field, GetMemberOptions.ReturnMemberDefinitions)) foreach (IMember m in recordTypeDef.GetMembers(m => m.SymbolKind is SymbolKind.Property or SymbolKind.Field, GetMemberOptions.ReturnMemberDefinitions))
{ {
nameToMemberMap[m.Name] = m; nameToMemberMap[m.Name] = m;
} }
IMethod guessedPrimaryCtor = null; IMethod? guessedPrimaryCtor = null;
foreach (var method in recordTypeDef.Methods) foreach (var method in recordTypeDef.Methods)
{ {
@ -191,10 +194,10 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
continue; continue;
} }
IMethod chainedCtor = (IMethod)FindChainedCtor(body)?.MemberDefinition; IMethod? chainedCtor = (IMethod?)FindChainedCtor(body)?.MemberDefinition;
ctorChainMap[method] = chainedCtor; ctorChainMap[method] = chainedCtor;
if (chainedCtor != null && chainedCtor.DeclaringTypeDefinition.Equals(recordTypeDef)) if (chainedCtor != null && chainedCtor.DeclaringTypeDefinition!.Equals(recordTypeDef))
{ {
continue; continue;
} }
@ -242,7 +245,7 @@ namespace ICSharpCode.Decompiler.CSharp
foreach (var (parameter, property) in primaryCtorParameterToAutoProperty.ToArray()) foreach (var (parameter, property) in primaryCtorParameterToAutoProperty.ToArray())
{ {
if (!parameter.Owner.Equals(guessedPrimaryCtor)) if (!parameter.Owner!.Equals(guessedPrimaryCtor))
{ {
primaryCtorParameterToAutoProperty.Remove(parameter); primaryCtorParameterToAutoProperty.Remove(parameter);
} }
@ -302,13 +305,13 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
if (v.Kind != VariableKind.Parameter || v.Index < parameterIndex) if (v.Kind != VariableKind.Parameter || v.Index < parameterIndex)
return false; return false;
parameterIndex = v.Index.Value; parameterIndex = v.Index!.Value;
} }
else if (valueInst.MatchLdObj(out valueInst, out _) && valueInst.MatchLdLoc(out v)) else if (valueInst.MatchLdObj(out valueInst, out _) && valueInst.MatchLdLoc(out v))
{ {
if (v.Kind != VariableKind.Parameter || v.Index < parameterIndex) if (v.Kind != VariableKind.Parameter || v.Index < parameterIndex)
return false; return false;
parameterIndex = v.Index.Value; parameterIndex = v.Index!.Value;
if (method.Parameters[parameterIndex].ReferenceKind is ReferenceKind.None) if (method.Parameters[parameterIndex].ReferenceKind is ReferenceKind.None)
{ {
return false; return false;
@ -338,7 +341,7 @@ namespace ICSharpCode.Decompiler.CSharp
return returnInst != null && returnInst.MatchReturn(out var retVal) && retVal.MatchNop(); return returnInst != null && returnInst.MatchReturn(out var retVal) && retVal.MatchNop();
} }
IMethod FindChainedCtor(Block body) IMethod? FindChainedCtor(Block body)
{ {
// look for a call instruction or assignment to a chained constructor // look for a call instruction or assignment to a chained constructor
foreach (var inst in body.Instructions) foreach (var inst in body.Instructions)
@ -379,7 +382,7 @@ namespace ICSharpCode.Decompiler.CSharp
/// <summary> /// <summary>
/// Gets the detected primary constructor. Returns null, if there was no primary constructor detected. /// Gets the detected primary constructor. Returns null, if there was no primary constructor detected.
/// </summary> /// </summary>
public IMethod PrimaryConstructor => primaryCtor; public IMethod? PrimaryConstructor => primaryCtor;
public bool IsInheritedRecord => isInheritedRecord; public bool IsInheritedRecord => isInheritedRecord;
@ -472,9 +475,9 @@ namespace ICSharpCode.Decompiler.CSharp
&& autoPropertyToPrimaryCtorParameter.ContainsKey((IProperty)property.Specialize(subst)); && autoPropertyToPrimaryCtorParameter.ContainsKey((IProperty)property.Specialize(subst));
} }
internal Dictionary<IParameter, (IProperty, IField)> GetParameterToBackingStoreMap() internal Dictionary<IParameter, (IProperty?, IField)> GetParameterToBackingStoreMap()
{ {
var result = new Dictionary<IParameter, (IProperty, IField)>(); var result = new Dictionary<IParameter, (IProperty?, IField)>();
foreach (var (parameter, property) in primaryCtorParameterToAutoProperty) foreach (var (parameter, property) in primaryCtorParameterToAutoProperty)
{ {
result.Add(parameter, (property, autoPropertyToBackingField[property])); result.Add(parameter, (property, autoPropertyToBackingField[property]));
@ -549,7 +552,7 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
if (!(member is IField field)) if (!(member is IField field))
{ {
if (!autoPropertyToBackingField.TryGetValue((IProperty)member, out field)) if (!autoPropertyToBackingField.TryGetValue((IProperty)member, out field!))
continue; continue;
} }
if (pos >= body.Instructions.Count) if (pos >= body.Instructions.Count)
@ -668,7 +671,7 @@ namespace ICSharpCode.Decompiler.CSharp
trueInst = Block.Unwrap(trueInst); trueInst = Block.Unwrap(trueInst);
if (!MatchStringBuilderAppend(trueInst, builder, out var val)) if (!MatchStringBuilderAppend(trueInst, builder, out var val))
return false; return false;
if (!(val.MatchLdStr(out string text) && text == ", ")) if (!(val.MatchLdStr(out string? text) && text == ", "))
return false; return false;
pos++; pos++;
@ -700,7 +703,7 @@ namespace ICSharpCode.Decompiler.CSharp
callvirt Append(ldloc builder, ldstr " = ") callvirt Append(ldloc builder, ldstr " = ")
callvirt Append(ldloc builder, constrained[System.Int32].callvirt ToString(ldflda B(ldloc this))) callvirt Append(ldloc builder, constrained[System.Int32].callvirt ToString(ldflda B(ldloc this)))
leave IL_0000 (ldc.i4 1) */ leave IL_0000 (ldc.i4 1) */
if (!MatchStringBuilderAppendConstant(out string text)) if (!MatchStringBuilderAppendConstant(out string? text))
return false; return false;
string expectedText = (needsComma ? ", " : "") + member.Name + " = "; string expectedText = (needsComma ? ", " : "") + member.Name + " = ";
if (text != expectedText) if (text != expectedText)
@ -775,10 +778,10 @@ namespace ICSharpCode.Decompiler.CSharp
return true; return true;
} }
bool MatchStringBuilderAppendConstant(out string text) bool MatchStringBuilderAppendConstant([NotNullWhen(true)] out string? text)
{ {
text = null; text = null;
while (MatchStringBuilderAppend(body.Instructions[pos], builder, out var val) && val.MatchLdStr(out string valText)) while (MatchStringBuilderAppend(body.Instructions[pos], builder, out var val) && val.MatchLdStr(out string? valText))
{ {
text += valText; text += valText;
pos++; pos++;
@ -787,7 +790,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
private bool MatchStringBuilderAppend(ILInstruction inst, ILVariable sb, out ILInstruction val) private bool MatchStringBuilderAppend(ILInstruction inst, ILVariable sb, [NotNullWhen(true)] out ILInstruction? val)
{ {
val = null; val = null;
if (!(inst is CallVirt { Method: { Name: "Append", DeclaringType: { Namespace: "System.Text", Name: "StringBuilder" } } } call)) if (!(inst is CallVirt { Method: { Name: "Append", DeclaringType: { Namespace: "System.Text", Name: "StringBuilder" } } } call))
@ -863,7 +866,7 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
return val != null && val.Length == 1 && call.Arguments[1].MatchLdcI4(val[0]); return val != null && val.Length == 1 && call.Arguments[1].MatchLdcI4(val[0]);
} }
return call.Arguments[1].MatchLdStr(out string val1) && val1 == val; return call.Arguments[1].MatchLdStr(out string? val1) && val1 == val;
} }
} }
@ -1015,7 +1018,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
private bool MatchGetEqualityContract(ILInstruction inst, out ILInstruction target) private bool MatchGetEqualityContract(ILInstruction inst, [NotNullWhen(true)] out ILInstruction? target)
{ {
target = null; target = null;
if (!(inst is CallInstruction { Method: { Name: "get_EqualityContract" } } call)) if (!(inst is CallInstruction { Method: { Name: "get_EqualityContract" } } call))
@ -1149,7 +1152,7 @@ namespace ICSharpCode.Decompiler.CSharp
bool IsGeneratedDeconstruct(IMethod method) bool IsGeneratedDeconstruct(IMethod method)
{ {
Debug.Assert(method.Name == "Deconstruct" && method.Parameters.Count == primaryCtor.Parameters.Count); Debug.Assert(method.Name == "Deconstruct" && method.Parameters.Count == primaryCtor?.Parameters.Count);
if (!method.ReturnType.IsKnownType(KnownTypeCode.Void)) if (!method.ReturnType.IsKnownType(KnownTypeCode.Void))
return false; return false;
@ -1205,7 +1208,7 @@ namespace ICSharpCode.Decompiler.CSharp
return returnInst != null && returnInst.MatchReturn(out var retVal) && retVal.MatchNop(); return returnInst != null && returnInst.MatchReturn(out var retVal) && retVal.MatchNop();
} }
bool MatchMemberAccess(ILInstruction inst, out ILInstruction target, out IMember member) bool MatchMemberAccess(ILInstruction inst, [NotNullWhen(true)] out ILInstruction? target, [NotNullWhen(true)] out IMember? member)
{ {
target = null; target = null;
member = null; member = null;
@ -1222,9 +1225,9 @@ namespace ICSharpCode.Decompiler.CSharp
member = property; member = property;
return true; return true;
} }
else if (inst.MatchLdFld(out target, out IField field)) else if (inst.MatchLdFld(out target, out IField? field))
{ {
if (backingFieldToAutoProperty.TryGetValue(field, out property)) if (backingFieldToAutoProperty.TryGetValue(field, out property!))
member = property; member = property;
else else
member = field; member = field;
@ -1236,7 +1239,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
Block DecompileBody(IMethod method) Block? DecompileBody(IMethod method)
{ {
if (method == null || method.MetadataToken.IsNil) if (method == null || method.MetadataToken.IsNil)
return null; return null;

Loading…
Cancel
Save