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