Browse Source

The IsPrimaryConstructor function is reimplemented based on some of dotPeek's judgment rules and the definition and characteristics of the primary constructor. However, it is found that dotPeek's judgment logic is also problematic. For example, it does not support the following scenario: ``` class WebPair(string name, string? value, ref readonly object encode) { public string Name { get; } = name; public string? Value { get; } = value; string? WebValue { get; } = encode is null ? "111" : value; string? WebValue2 { get; } = encode.ToString(); } ```

pull/3598/head
sonyps5201314 6 months ago
parent
commit
aae1a28895
  1. 58
      ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs

58
ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs

@ -188,6 +188,11 @@ namespace ICSharpCode.Decompiler.CSharp
return null; return null;
//判定主构造函数的唯一准则是:
//构造函数中前面部分都是类成员属性/字段赋值语句,
//之后是调用基类构造函数的语句即"base..ctor();",
//再之后就是返回语句了,即"base..ctor();"之后不存在任何其他初始化语句。
//且主构造函数的参数和类成员之间无任何对应关系!
bool IsPrimaryConstructor(IMethod method, IMethod unspecializedMethod) bool IsPrimaryConstructor(IMethod method, IMethod unspecializedMethod)
{ {
Debug.Assert(method.IsConstructor); Debug.Assert(method.IsConstructor);
@ -198,9 +203,10 @@ namespace ICSharpCode.Decompiler.CSharp
if (method.Parameters.Count == 0) if (method.Parameters.Count == 0)
return false; return false;
for (int i = 0; i < method.Parameters.Count; i++) List<IMember> backingMembers = new List<IMember>();
foreach (var instruction in body.Instructions)
{ {
if (!body.Instructions[i].MatchStFld(out var target, out var field, out var valueInst)) if (!instruction.MatchStFld(out var target, out var field, out var valueInst))
break; break;
if (!target.MatchLdThis()) if (!target.MatchLdThis())
return false; return false;
@ -208,27 +214,39 @@ namespace ICSharpCode.Decompiler.CSharp
if (backingFieldToAutoProperty.TryGetValue(field, out var property)) if (backingFieldToAutoProperty.TryGetValue(field, out var property))
{ {
backingMember = property; backingMember = property;
backingMembers.Add(backingMember);
} }
else if (!recordTypeDef.IsRecord) else if (!recordTypeDef.IsRecord)
{ {
backingMember = field; backingMember = field;
backingMembers.Add(backingMember);
} }
else else
{ {
return false; return false;
} }
primaryCtorParameterToAutoPropertyOrBackingField.Add(unspecializedMethod.Parameters[i], backingMember);
autoPropertyOrBackingFieldToPrimaryCtorParameter.Add(backingMember, unspecializedMethod.Parameters[i]);
} }
if (!isStruct) if (!isStruct)
{ {
var baseCtorCall = body.Instructions.SecondToLastOrDefault() as CallInstruction; Call baseCtorCall;
IsBaseCtorCall(body.Instructions.SecondToLastOrDefault(), out baseCtorCall);
if (baseCtorCall == null) if (baseCtorCall == null)
return false; return false;
} }
for (int i = 0; i < method.Parameters.Count; i++)
{
var param = unspecializedMethod.Parameters[i];
//var backingMember = backingMembers.Find(x => x.Name == param.Name);
var backingMember = backingMembers.Count > i ? backingMembers[i] : null;
if (backingMember != null)
{
primaryCtorParameterToAutoPropertyOrBackingField.Add(param, backingMember);
autoPropertyOrBackingFieldToPrimaryCtorParameter.Add(backingMember, param);
}
}
var returnInst = body.Instructions.LastOrDefault(); var returnInst = body.Instructions.LastOrDefault();
return returnInst != null && returnInst.MatchReturn(out var retVal) && retVal.MatchNop(); return returnInst != null && returnInst.MatchReturn(out var retVal) && retVal.MatchNop();
} }
@ -403,6 +421,23 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
bool IsBaseCtorCall(ILInstruction instruction, out Call _baseCtorCall)
{
_baseCtorCall = null;
if (!(instruction is Call { Method: { IsConstructor: true } } baseCtorCall))
return false;
if (!object.Equals(baseCtorCall.Method.DeclaringType, baseClass))
return false;
if (baseCtorCall.Arguments.Count != (isInheritedRecord ? 2 : 1))
return false;
if (!baseCtorCall.Arguments[0].MatchLdThis())
return false;
_baseCtorCall = baseCtorCall;
return true;
}
private bool IsGeneratedCopyConstructor(IMethod method) private bool IsGeneratedCopyConstructor(IMethod method)
{ {
/* /*
@ -425,14 +460,11 @@ namespace ICSharpCode.Decompiler.CSharp
Debug.Assert(IsRecordType(other.Type)); Debug.Assert(IsRecordType(other.Type));
int pos = 0; int pos = 0;
// First instruction is the base constructor call // First instruction is the base constructor call
if (!(body.Instructions[pos] is Call { Method: { IsConstructor: true } } baseCtorCall)) Call baseCtorCall;
return false; if (!IsBaseCtorCall(body.Instructions[pos], out baseCtorCall))
if (!object.Equals(baseCtorCall.Method.DeclaringType, baseClass)) {
return false;
if (baseCtorCall.Arguments.Count != (isInheritedRecord ? 2 : 1))
return false;
if (!baseCtorCall.Arguments[0].MatchLdThis())
return false; return false;
}
if (isInheritedRecord) if (isInheritedRecord)
{ {
if (!baseCtorCall.Arguments[1].MatchLdLoc(other)) if (!baseCtorCall.Arguments[1].MatchLdLoc(other))

Loading…
Cancel
Save