From 27f5275718039eaf791ccd4b970a081090353ce2 Mon Sep 17 00:00:00 2001 From: sonyps5201314 Date: Wed, 29 Oct 2025 00:39:51 +0800 Subject: [PATCH] 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; } } ``` --- .../CSharp/RecordDecompiler.cs | 84 ++++++++++++++++--- 1 file changed, 72 insertions(+), 12 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs b/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs index 87e417b8a..4ea7b9253 100644 --- a/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs @@ -218,38 +218,98 @@ namespace ICSharpCode.Decompiler.CSharp } var subst = recordTypeDef.AsParameterizedType().GetSubstitution(); + var instanceCtors = new List(); 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; - primaryCtorParameterToAutoPropertyOrBackingField.Clear(); - autoPropertyOrBackingFieldToPrimaryCtorParameter.Clear(); + if (IsCopyConstructor(method)) + continue; + instanceCtors.Add(method); } - return null; + 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 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;