diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index 95eb3fcb7..0a0ebeae5 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -1311,6 +1311,20 @@ namespace ICSharpCode.Decompiler } } + bool aggressiveScalarReplacementOfAggregates = false; + + [Category("DecompilerSettings.Other")] + [Description("DecompilerSettings.AggressiveScalarReplacementOfAggregates")] + public bool AggressiveScalarReplacementOfAggregates { + get { return aggressiveScalarReplacementOfAggregates; } + set { + if (aggressiveScalarReplacementOfAggregates != value) { + aggressiveScalarReplacementOfAggregates = value; + OnPropertyChanged(); + } + } + } + CSharpFormattingOptions csharpFormattingOptions; [Browsable(false)] diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILVariableCollection.cs b/ICSharpCode.Decompiler/IL/Instructions/ILVariableCollection.cs index 2b79902f3..f27d962a7 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILVariableCollection.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILVariableCollection.cs @@ -102,6 +102,9 @@ namespace ICSharpCode.Decompiler.IL { for (int i = 0; i < list.Count;) { var v = list[i]; + // Note: we cannot remove display-class locals from the collection, + // even if they are unused - which is always the case, if TDCU succeeds, + // because they are necessary for PDB generation to produce correct results. if (v.IsDead && v.Kind != VariableKind.DisplayClassLocal) { RemoveAt(i); } else { diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 8e0d006cf..7a10ab9bb 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -20,6 +20,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Reflection.Metadata; +using ICSharpCode.Decompiler.Disassembler; +using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; @@ -197,8 +200,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms } else { definition = null; } - if (definition?.DeclaringTypeDefinition == null || definition.ParentModule.PEFile != context.PEFile) + if (definition == null) return null; + if (!context.Settings.AggressiveScalarReplacementOfAggregates) { + if (definition.DeclaringTypeDefinition == null || definition.ParentModule.PEFile != context.PEFile) + return null; + if (!IsPotentialClosure(context, definition)) + return null; + } DisplayClass result; switch (definition.Kind) { case TypeKind.Class: @@ -206,8 +215,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return null; if (!(v.StoreInstructions[0] is StLoc stloc)) return null; - // TODO thoroughly validate ctor - if (!(stloc.Value is NewObj newObj && newObj.Method.Parameters.Count == 0)) + if (!(stloc.Value is NewObj newObj && ValidateConstructor(newObj.Method))) return null; result = new DisplayClass(v, definition) { CaptureScope = v.CaptureScope, @@ -256,6 +264,42 @@ namespace ICSharpCode.Decompiler.IL.Transforms return result; } + private bool ValidateConstructor(IMethod method) + { + if (method.Parameters.Count != 0) + return false; + var handle = (MethodDefinitionHandle)method.MetadataToken; + var module = (MetadataModule)method.ParentModule; + var file = module.PEFile; + if (handle.IsNil || file == null) + return false; + var def = file.Metadata.GetMethodDefinition(handle); + if (def.RelativeVirtualAddress == 0) + return false; + var body = file.Reader.GetMethodBody(def.RelativeVirtualAddress); + if (!body.LocalSignature.IsNil) + return false; + var reader = body.GetILReader(); + if (reader.Length != 7) + return false; + // IL_0000: ldarg.0 + // IL_0001: call instance void [mscorlib]System.Object::.ctor() + // IL_0006: ret + if (reader.DecodeOpCode() != ILOpCode.Ldarg_0) + return false; + if (reader.DecodeOpCode() != ILOpCode.Call) + return false; + var baseCtorHandle = MetadataTokenHelpers.EntityHandleOrNil(reader.ReadInt32()); + if (baseCtorHandle.IsNil) + return false; + var objectCtor = module.ResolveMethod(baseCtorHandle, new TypeSystem.GenericContext()); + if (!objectCtor.DeclaringType.IsKnownType(KnownTypeCode.Object)) + return false; + if (objectCtor.Parameters.Count != 0) + return false; + return reader.DecodeOpCode() == ILOpCode.Ret; + } + VariableToDeclare AddVariable(DisplayClass result, ILInstruction init, out IField field) { if (!init.MatchStFld(out var target, out field, out var value)) @@ -399,15 +443,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - internal static bool IsSimpleDisplayClass(IType type) - { - if (!type.HasGeneratedName() || (!type.Name.Contains("DisplayClass") && !type.Name.Contains("AnonStorey"))) - return false; - if (type.DirectBaseTypes.Any(t => !t.IsKnownType(KnownTypeCode.Object))) - return false; - return true; - } - internal static bool IsPotentialClosure(ILTransformContext context, NewObj inst) { var decompilationContext = new SimpleTypeResolveContext(context.Function.Ancestors.OfType().Last().Method); diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 00e102b8d..c56eee03b 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -576,6 +576,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Aggressively perform Scalar Replacement Of Aggregates (SROA). + /// + public static string DecompilerSettings_AggressiveScalarReplacementOfAggregates { + get { + return ResourceManager.GetString("DecompilerSettings.AggressiveScalarReplacementOfAggregates", resourceCulture); + } + } + /// /// Looks up a localized string similar to Allow extension 'Add' methods in collection initializer expressions. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index 45e1d4246..b3e3f01ff 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -864,4 +864,7 @@ Do you want to continue? Culture + + Aggressively perform Scalar Replacement Of Aggregates (SROA) + \ No newline at end of file