|
|
@ -61,67 +61,68 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms |
|
|
|
|
|
|
|
|
|
|
|
public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) |
|
|
|
public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (!(constructorDeclaration.Body.Statements.FirstOrDefault() is ExpressionStatement stmt)) |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
var currentCtor = (IMethod)constructorDeclaration.GetSymbol(); |
|
|
|
var currentCtor = (IMethod)constructorDeclaration.GetSymbol(); |
|
|
|
ConstructorInitializer ci; |
|
|
|
ConstructorInitializer ci = null; |
|
|
|
switch (stmt.Expression) |
|
|
|
if (constructorDeclaration.Body.Statements.FirstOrDefault() is ExpressionStatement stmt) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// Pattern for reference types:
|
|
|
|
switch (stmt.Expression) |
|
|
|
// this..ctor(...);
|
|
|
|
{ |
|
|
|
case InvocationExpression invocation: |
|
|
|
// Pattern for reference types:
|
|
|
|
if (!(invocation.Target is MemberReferenceExpression mre) || mre.MemberName != ".ctor") |
|
|
|
// this..ctor(...);
|
|
|
|
return; |
|
|
|
case InvocationExpression invocation: |
|
|
|
if (!(invocation.GetSymbol() is IMethod ctor && ctor.IsConstructor)) |
|
|
|
if (!(invocation.Target is MemberReferenceExpression mre) || mre.MemberName != ".ctor") |
|
|
|
return; |
|
|
|
return; |
|
|
|
ci = new ConstructorInitializer(); |
|
|
|
if (!(invocation.GetSymbol() is IMethod ctor && ctor.IsConstructor)) |
|
|
|
var target = mre.Target; |
|
|
|
return; |
|
|
|
// Ignore casts, those might be added if references are missing.
|
|
|
|
ci = new ConstructorInitializer(); |
|
|
|
if (target is CastExpression cast) |
|
|
|
var target = mre.Target; |
|
|
|
target = cast.Expression; |
|
|
|
// Ignore casts, those might be added if references are missing.
|
|
|
|
if (target is ThisReferenceExpression or BaseReferenceExpression) |
|
|
|
if (target is CastExpression cast) |
|
|
|
{ |
|
|
|
target = cast.Expression; |
|
|
|
if (ctor.DeclaringTypeDefinition == currentCtor.DeclaringTypeDefinition) |
|
|
|
if (target is ThisReferenceExpression or BaseReferenceExpression) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (ctor.DeclaringTypeDefinition == currentCtor.DeclaringTypeDefinition) |
|
|
|
|
|
|
|
ci.ConstructorInitializerType = ConstructorInitializerType.This; |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
ci.ConstructorInitializerType = ConstructorInitializerType.Base; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
// Move arguments from invocation to initializer:
|
|
|
|
|
|
|
|
invocation.Arguments.MoveTo(ci.Arguments); |
|
|
|
|
|
|
|
// Add the initializer: (unless it is the default 'base()')
|
|
|
|
|
|
|
|
if (!(ci.ConstructorInitializerType == ConstructorInitializerType.Base && ci.Arguments.Count == 0)) |
|
|
|
|
|
|
|
constructorDeclaration.Initializer = ci.CopyAnnotationsFrom(invocation); |
|
|
|
|
|
|
|
// Remove the statement:
|
|
|
|
|
|
|
|
stmt.Remove(); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
// Pattern for value types:
|
|
|
|
|
|
|
|
// this = new TSelf(...);
|
|
|
|
|
|
|
|
case AssignmentExpression assignment: |
|
|
|
|
|
|
|
if (!(assignment.Right is ObjectCreateExpression oce && oce.GetSymbol() is IMethod ctor2 && ctor2.DeclaringTypeDefinition == currentCtor.DeclaringTypeDefinition)) |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
ci = new ConstructorInitializer(); |
|
|
|
|
|
|
|
if (assignment.Left is ThisReferenceExpression) |
|
|
|
ci.ConstructorInitializerType = ConstructorInitializerType.This; |
|
|
|
ci.ConstructorInitializerType = ConstructorInitializerType.This; |
|
|
|
else |
|
|
|
else |
|
|
|
ci.ConstructorInitializerType = ConstructorInitializerType.Base; |
|
|
|
return; |
|
|
|
} |
|
|
|
// Move arguments from invocation to initializer:
|
|
|
|
else |
|
|
|
oce.Arguments.MoveTo(ci.Arguments); |
|
|
|
return; |
|
|
|
// Add the initializer: (unless it is the default 'base()')
|
|
|
|
// Move arguments from invocation to initializer:
|
|
|
|
if (!(ci.ConstructorInitializerType == ConstructorInitializerType.Base && ci.Arguments.Count == 0)) |
|
|
|
invocation.Arguments.MoveTo(ci.Arguments); |
|
|
|
constructorDeclaration.Initializer = ci.CopyAnnotationsFrom(oce); |
|
|
|
// Add the initializer: (unless it is the default 'base()')
|
|
|
|
// Remove the statement:
|
|
|
|
if (!(ci.ConstructorInitializerType == ConstructorInitializerType.Base && ci.Arguments.Count == 0)) |
|
|
|
stmt.Remove(); |
|
|
|
constructorDeclaration.Initializer = ci.CopyAnnotationsFrom(invocation); |
|
|
|
break; |
|
|
|
// Remove the statement:
|
|
|
|
default: |
|
|
|
stmt.Remove(); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
// Pattern for value types:
|
|
|
|
|
|
|
|
// this = new TSelf(...);
|
|
|
|
|
|
|
|
case AssignmentExpression assignment: |
|
|
|
|
|
|
|
if (!(assignment.Right is ObjectCreateExpression oce && oce.GetSymbol() is IMethod ctor2 && ctor2.DeclaringTypeDefinition == currentCtor.DeclaringTypeDefinition)) |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
ci = new ConstructorInitializer(); |
|
|
|
|
|
|
|
if (assignment.Left is ThisReferenceExpression) |
|
|
|
|
|
|
|
ci.ConstructorInitializerType = ConstructorInitializerType.This; |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
return; |
|
|
|
return; |
|
|
|
// Move arguments from invocation to initializer:
|
|
|
|
} |
|
|
|
oce.Arguments.MoveTo(ci.Arguments); |
|
|
|
|
|
|
|
// Add the initializer: (unless it is the default 'base()')
|
|
|
|
|
|
|
|
if (!(ci.ConstructorInitializerType == ConstructorInitializerType.Base && ci.Arguments.Count == 0)) |
|
|
|
|
|
|
|
constructorDeclaration.Initializer = ci.CopyAnnotationsFrom(oce); |
|
|
|
|
|
|
|
// Remove the statement:
|
|
|
|
|
|
|
|
stmt.Remove(); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
if (context.DecompileRun.RecordDecompilers.TryGetValue(currentCtor.DeclaringTypeDefinition, out var record) |
|
|
|
if (context.DecompileRun.RecordDecompilers.TryGetValue(currentCtor.DeclaringTypeDefinition, out var record) |
|
|
|
&& currentCtor.Equals(record.PrimaryConstructor) |
|
|
|
&& currentCtor.Equals(record.PrimaryConstructor)) |
|
|
|
&& ci.ConstructorInitializerType == ConstructorInitializerType.Base) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
if (record.IsInheritedRecord && |
|
|
|
if (record.IsInheritedRecord && |
|
|
|
|
|
|
|
ci?.ConstructorInitializerType == ConstructorInitializerType.Base && |
|
|
|
constructorDeclaration.Parent is TypeDeclaration { BaseTypes: { Count: >= 1 } } typeDecl) |
|
|
|
constructorDeclaration.Parent is TypeDeclaration { BaseTypes: { Count: >= 1 } } typeDecl) |
|
|
|
{ |
|
|
|
{ |
|
|
|
var baseType = typeDecl.BaseTypes.First(); |
|
|
|
var baseType = typeDecl.BaseTypes.First(); |
|
|
@ -172,7 +173,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms |
|
|
|
if (instanceCtorsNotChainingWithThis.Length > 0) |
|
|
|
if (instanceCtorsNotChainingWithThis.Length > 0) |
|
|
|
{ |
|
|
|
{ |
|
|
|
var ctorMethodDef = instanceCtorsNotChainingWithThis[0].GetSymbol() as IMethod; |
|
|
|
var ctorMethodDef = instanceCtorsNotChainingWithThis[0].GetSymbol() as IMethod; |
|
|
|
if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsReferenceType == false) |
|
|
|
ITypeDefinition declaringTypeDefinition = ctorMethodDef?.DeclaringTypeDefinition; |
|
|
|
|
|
|
|
if (ctorMethodDef != null && declaringTypeDefinition?.IsReferenceType == false && !declaringTypeDefinition.IsRecord) |
|
|
|
return; |
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
bool ctorIsUnsafe = instanceCtorsNotChainingWithThis.All(c => c.HasModifier(Modifiers.Unsafe)); |
|
|
|
bool ctorIsUnsafe = instanceCtorsNotChainingWithThis.All(c => c.HasModifier(Modifiers.Unsafe)); |
|
|
@ -180,13 +182,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms |
|
|
|
if (!context.DecompileRun.RecordDecompilers.TryGetValue(ctorMethodDef.DeclaringTypeDefinition, out var record)) |
|
|
|
if (!context.DecompileRun.RecordDecompilers.TryGetValue(ctorMethodDef.DeclaringTypeDefinition, out var record)) |
|
|
|
record = null; |
|
|
|
record = null; |
|
|
|
|
|
|
|
|
|
|
|
//Filter out copy constructor of records
|
|
|
|
// Filter out copy constructor of records
|
|
|
|
if (record != null) |
|
|
|
if (record != null) |
|
|
|
instanceCtorsNotChainingWithThis = instanceCtorsNotChainingWithThis.Where(ctor => !record.IsCopyConstructor(ctor.GetSymbol() as IMethod)).ToArray(); |
|
|
|
instanceCtorsNotChainingWithThis = instanceCtorsNotChainingWithThis.Where(ctor => !record.IsCopyConstructor(ctor.GetSymbol() as IMethod)).ToArray(); |
|
|
|
|
|
|
|
|
|
|
|
// Recognize field or property initializers:
|
|
|
|
// Recognize field or property initializers:
|
|
|
|
// Translate first statement in all ctors (if all ctors have the same statement) into an initializer.
|
|
|
|
// Translate first statement in all ctors (if all ctors have the same statement) into an initializer.
|
|
|
|
bool allSame; |
|
|
|
bool allSame; |
|
|
|
|
|
|
|
bool isPrimaryCtor = declaringTypeDefinition.IsReferenceType == true && declaringTypeDefinition.IsRecord; |
|
|
|
do |
|
|
|
do |
|
|
|
{ |
|
|
|
{ |
|
|
|
Match m = fieldInitializerPattern.Match(instanceCtorsNotChainingWithThis[0].Body.FirstOrDefault()); |
|
|
|
Match m = fieldInitializerPattern.Match(instanceCtorsNotChainingWithThis[0].Body.FirstOrDefault()); |
|
|
@ -211,11 +214,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms |
|
|
|
// remove record ctor parameter assignments
|
|
|
|
// remove record ctor parameter assignments
|
|
|
|
if (!IsPropertyDeclaredByPrimaryCtor(fieldOrPropertyOrEvent as IProperty, record)) |
|
|
|
if (!IsPropertyDeclaredByPrimaryCtor(fieldOrPropertyOrEvent as IProperty, record)) |
|
|
|
break; |
|
|
|
break; |
|
|
|
|
|
|
|
isPrimaryCtor = true; |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
else |
|
|
|
{ |
|
|
|
{ |
|
|
|
// cannot transform if member is not found
|
|
|
|
// cannot transform if member is not found
|
|
|
|
if (fieldOrPropertyOrEventDecl == null) |
|
|
|
if (fieldOrPropertyOrEventDecl == null || !isPrimaryCtor) |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|