Browse Source

Fixed unsupport for types containing multiple regular constructors, none of which chain calls to the primary constructor.

Here's an example:
```cs
    public record struct CopilotContextId
    {
        public CopilotContextId() => this.Id = Guid.NewGuid();

        public CopilotContextId(Guid id) => this.Id = id;

        public Guid Id { get; }
    }
```
pull/3598/head
sonyps5201314 5 months ago
parent
commit
27f5275718
  1. 80
      ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs

80
ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs

@ -218,38 +218,98 @@ namespace ICSharpCode.Decompiler.CSharp @@ -218,38 +218,98 @@ namespace ICSharpCode.Decompiler.CSharp
}
var subst = recordTypeDef.AsParameterizedType().GetSubstitution();
var instanceCtors = new List<IMethod>();
foreach (var method in recordTypeDef.Methods)
{
cancellationToken.ThrowIfCancellationRequested();
if (method.IsStatic || !method.IsConstructor)
continue;
var m = method.Specialize(subst);
if (IsPrimaryConstructor(m, method))
return method;
if (IsCopyConstructor(method))
continue;
instanceCtors.Add(method);
}
IMethod primaryConstructor = null;
bool CheckForInitPrimaryConstructor(IMethod method, IMethod unspecializedMethod, Block body)
{
if (primaryConstructor == null)
{
if (!IsPrimaryConstructor(method, unspecializedMethod, body))
{
return false;
}
primaryConstructor = unspecializedMethod;
return true;
}
return false;
}
bool mustFindChainingWithThis_Or_IsPrimaryConstructor = instanceCtors.Count > 1;
for (int i = instanceCtors.Count - 1; i >= 0; i--)
{
var unspecializedMethod = instanceCtors[i];
var method = unspecializedMethod.Specialize(subst);
Block body = null;
if (mustFindChainingWithThis_Or_IsPrimaryConstructor)
{
body = DecompileBody(method);
if (body != null)
{
var first = body.Instructions.FirstOrDefault();
if (first is Call call && call.Method.IsConstructor)
{
if (call.Arguments.Count > 1 && call.Arguments[0].MatchLdThis())
{
// is constructor chaining with this
continue;
}
}
// Also handle value-type chaining syntax compiled as a stobj with a newobj value:
// e.g. `stobj T(ldloc this, newobj T::.ctor(...))` corresponds to `this = new T(...)` in source.
else if (first is StObj stobj)
{
// Check target is 'this'
if (stobj.Target.MatchLdThis())
{
// Check value is a newobj (constructor call)
if (stobj.Value is NewObj newObj && newObj.Method.IsConstructor && newObj.Method.DeclaringType == recordTypeDef)
{
// is constructor chaining with this for value-types
continue;
}
}
}
}
}
if (!CheckForInitPrimaryConstructor(method, unspecializedMethod, body))
{
primaryConstructor = null;
primaryCtorParameterToAutoPropertyOrBackingField.Clear();
autoPropertyOrBackingFieldToPrimaryCtorParameter.Clear();
break;
}
return null;
}
return primaryConstructor;
//判定主构造函数的唯一准则是:
//首先它不能是拷贝构造函数,且能通过IsPrimaryConstructorFast的判定逻辑,
//首先它不能是拷贝构造函数(已在外部排除),且能通过IsPrimaryConstructorFast的判定逻辑,
//然后这个构造函数中前面部分都是类成员属性/字段赋值语句,
//如果不是结构体的话,之后紧跟着是调用基类构造函数的语句即"base..ctor(...);",
//再之后紧跟着就是返回语句了,即"base..ctor(...);"之后不存在任何其他初始化语句。
//注意:不存在捕获参数的情况下,主构造函数的参数和类成员之间无任何对应关系!
//注意:目前即使一个函数的原始代码形式是常规构造函数,但是如果它符合本IsPrimaryConstructor函数的判定规则也会将它翻译成主构造函数形式
bool IsPrimaryConstructor(IMethod method, IMethod unspecializedMethod)
bool IsPrimaryConstructor(IMethod method, IMethod unspecializedMethod, Block body)
{
Debug.Assert(method.IsConstructor);
if (IsCopyConstructor(method))
return false;
if (!IsPrimaryConstructorFast(method, isStruct, false))
return false;
var body = DecompileBody(method);
if (body == null)
body = DecompileBody(method);
if (body == null)
return false;

Loading…
Cancel
Save