Browse Source

Records: detect copy constructor

pull/2251/head
Daniel Grunwald 4 years ago
parent
commit
e02c4789f1
  1. 67
      ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs

67
ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs

@ -170,6 +170,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -170,6 +170,11 @@ namespace ICSharpCode.Decompiler.CSharp
/// </summary>
public bool MethodIsGenerated(IMethod method)
{
if (method.IsConstructor && method.Parameters.Count == 1
&& IsRecordType(method.Parameters[0].Type))
{
return IsGeneratedCopyConstructor(method);
}
switch (method.Name)
{
// Some members in records are always compiler-generated and lead to a
@ -226,6 +231,68 @@ namespace ICSharpCode.Decompiler.CSharp @@ -226,6 +231,68 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
private bool IsGeneratedCopyConstructor(IMethod method)
{
/*
call BaseClass..ctor(ldloc this, ldloc original)
stfld <X>k__BackingField(ldloc this, ldfld <X>k__BackingField(ldloc original))
leave IL_0000 (nop)
*/
Debug.Assert(method.IsConstructor && method.Parameters.Count == 1);
if (method.GetAttributes().Any() || method.GetReturnTypeAttributes().Any())
return false;
if (orderedMembers == null)
return false;
var body = DecompileBody(method);
if (body == null)
return false;
var variables = body.Ancestors.OfType<ILFunction>().Single().Variables;
var other = variables.Single(v => v.Kind == VariableKind.Parameter && v.Index == 0);
Debug.Assert(IsRecordType(other.Type));
int pos = 0;
// First instruction is the base constructor call
if (!(body.Instructions[pos] 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;
if (isInheritedRecord)
{
if (!baseCtorCall.Arguments[1].MatchLdLoc(other))
return false;
}
pos++;
// Then all the fields are copied over
foreach (var member in orderedMembers)
{
if (!(member is IField field))
{
if (!autoPropertyToBackingField.TryGetValue((IProperty)member, out field))
continue;
}
if (pos >= body.Instructions.Count)
return false;
if (!body.Instructions[pos].MatchStFld(out var lhsTarget, out var lhsField, out var valueInst))
return false;
if (!lhsTarget.MatchLdThis())
return false;
if (!lhsField.Equals(field))
return false;
if (!valueInst.MatchLdFld(out var rhsTarget, out var rhsField))
return false;
if (!rhsTarget.MatchLdThis())
return false;
if (!rhsField.Equals(field))
return false;
pos++;
}
return body.Instructions[pos] is Leave;
}
private bool IsGeneratedEqualityContract(IProperty property)
{
// Generated member:

Loading…
Cancel
Save