Browse Source

Extract DecompileBodyForAnalysis from RecordDecompiler

Structural recognition of compiler-generated code needs method bodies as
ILAst decompiled with a fixed set of settings, so that recognition does
not depend on user-visible options. RecordDecompiler had this pipeline
as a private helper; hoist it to CSharpDecompiler so other recognizers
can share it, deriving the generic context from the method's declaring
type instead of a captured type definition.

Assisted-by: Claude:claude-fable-5:Claude Code
Claude-Session: https://claude.ai/code/session_01Btdypgm8utyxqt1Etn2BDi
pull/3889/head
Siegfried Pammer 2 months ago committed by Siegfried Pammer
parent
commit
bbac02a79f
  1. 50
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 39
      ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs

50
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -176,6 +176,56 @@ namespace ICSharpCode.Decompiler.CSharp @@ -176,6 +176,56 @@ namespace ICSharpCode.Decompiler.CSharp
};
}
/// <summary>
/// Decompiles the body of <paramref name="method"/> to ILAst for structural analysis,
/// e.g. for recognizing compiler-generated code (see RecordDecompiler).
/// Runs the IL transform pipeline with a fixed set of decompiler settings, so the
/// resulting shape is independent of the user-visible settings, and stops before the
/// late transforms (variable naming etc.) that are only needed for code output.
/// </summary>
internal static Block? DecompileBodyForAnalysis(IMethod method, IDecompilerTypeSystem typeSystem, CancellationToken cancellationToken)
{
if (method.MetadataToken.IsNil)
return null;
var module = typeSystem.MainModule;
var metadata = module.metadata;
var methodDefHandle = (MethodDefinitionHandle)method.MetadataToken;
var methodDef = metadata.GetMethodDefinition(methodDefHandle);
if (!methodDef.HasBody())
return null;
var genericContext = new GenericContext(
classTypeParameters: method.DeclaringTypeDefinition?.TypeParameters,
methodTypeParameters: null);
var body = module.MetadataFile.GetMethodBody(methodDef.RelativeVirtualAddress);
var ilReader = new ILReader(module);
var il = ilReader.ReadIL(methodDefHandle, body, genericContext, ILFunctionKind.TopLevelFunction, cancellationToken);
var settings = new DecompilerSettings(LanguageVersion.CSharp1);
var transforms = GetILTransforms();
// Remove the last couple transforms -- we don't need variable names etc. here
int lastBlockTransform = transforms.FindLastIndex(t => t is BlockILTransform);
transforms.RemoveRange(lastBlockTransform + 1, transforms.Count - (lastBlockTransform + 1));
// Use CombineExitsTransform so that "return other != null && ...;" is a single statement even in release builds
transforms.Add(new CombineExitsTransform());
il.RunTransforms(transforms,
new ILTransformContext(il, typeSystem, debugInfo: null, settings) {
CancellationToken = cancellationToken
});
if (il.Body is BlockContainer container)
{
return container.EntryPoint;
}
else if (il.Body is Block block)
{
return block;
}
else
{
return null;
}
}
List<IAstTransform> astTransforms = GetAstTransforms();
public Stepper Stepper { get; set; } = new Stepper();

39
ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs

@ -1259,44 +1259,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1259,44 +1259,9 @@ namespace ICSharpCode.Decompiler.CSharp
Block? DecompileBody(IMethod method)
{
if (method == null || method.MetadataToken.IsNil)
return null;
var metadata = typeSystem.MainModule.metadata;
var methodDefHandle = (MethodDefinitionHandle)method.MetadataToken;
var methodDef = metadata.GetMethodDefinition(methodDefHandle);
if (!methodDef.HasBody())
return null;
var genericContext = new GenericContext(
classTypeParameters: recordTypeDef.TypeParameters,
methodTypeParameters: null);
var body = typeSystem.MainModule.MetadataFile.GetMethodBody(methodDef.RelativeVirtualAddress);
var ilReader = new ILReader(typeSystem.MainModule);
var il = ilReader.ReadIL(methodDefHandle, body, genericContext, ILFunctionKind.TopLevelFunction, cancellationToken);
var settings = new DecompilerSettings(LanguageVersion.CSharp1);
var transforms = CSharpDecompiler.GetILTransforms();
// Remove the last couple transforms -- we don't need variable names etc. here
int lastBlockTransform = transforms.FindLastIndex(t => t is BlockILTransform);
transforms.RemoveRange(lastBlockTransform + 1, transforms.Count - (lastBlockTransform + 1));
// Use CombineExitsTransform so that "return other != null && ...;" is a single statement even in release builds
transforms.Add(new CombineExitsTransform());
il.RunTransforms(transforms,
new ILTransformContext(il, typeSystem, debugInfo: null, settings) {
CancellationToken = cancellationToken
});
if (il.Body is BlockContainer container)
{
return container.EntryPoint;
}
else if (il.Body is Block block)
{
return block;
}
else
{
if (method == null)
return null;
}
return CSharpDecompiler.DecompileBodyForAnalysis(method, typeSystem, cancellationToken);
}
}
}

Loading…
Cancel
Save