Browse Source

WIP: DelegateConstruction: implemented basic transformations

pull/734/head
Siegfried Pammer 9 years ago
parent
commit
ab357e00a6
  1. 62
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 3
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 220
      ICSharpCode.Decompiler/CSharp/Transforms/DelegateConstruction.cs
  4. 3
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  5. 95
      ICSharpCode.Decompiler/IL/Transforms/RemoveCachedDelegateInitialization.cs
  6. 85
      ICSharpCode.Decompiler/NRExtensions.cs
  7. 11
      ICSharpCode.Decompiler/Util/CollectionExtensions.cs

62
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -59,6 +59,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -59,6 +59,7 @@ namespace ICSharpCode.Decompiler.CSharp
new InlineCompilerGeneratedVariables(),
new ExpressionTransforms(), // must run once before "the loop" to allow RemoveDeadVariablesInit
new RemoveDeadVariableInit(), // must run after ExpressionTransforms because it does not handle stobj(ldloca V, ...)
new RemoveCachedDelegateInitialization(),
new LoopingTransform(
new ExpressionTransforms(),
new TransformArrayInitializers(),
@ -68,7 +69,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -68,7 +69,7 @@ namespace ICSharpCode.Decompiler.CSharp
List<IAstTransform> astTransforms = new List<IAstTransform> {
//new PushNegation(),
//new DelegateConstruction(context),
new DelegateConstruction(),
new PatternStatementTransform(),
new ReplaceMethodCallsWithOperators(),
new IntroduceUnsafeModifier(),
@ -120,8 +121,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -120,8 +121,8 @@ namespace ICSharpCode.Decompiler.CSharp
if (method != null) {
if (method.IsGetter || method.IsSetter || method.IsAddOn || method.IsRemoveOn)
return true;
// if (settings.AnonymousMethods && method.HasGeneratedName() && method.IsCompilerGenerated())
// return true;
if (settings.AnonymousMethods && method.HasGeneratedName() && method.IsCompilerGenerated())
return true;
}
TypeDefinition type = member as TypeDefinition;
@ -144,8 +145,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -144,8 +145,8 @@ namespace ICSharpCode.Decompiler.CSharp
FieldDefinition field = member as FieldDefinition;
if (field != null) {
if (field.IsCompilerGenerated()) {
// if (settings.AnonymousMethods && IsAnonymousMethodCacheField(field))
// return true;
if (settings.AnonymousMethods && IsAnonymousMethodCacheField(field))
return true;
if (settings.AutomaticProperties && IsAutomaticPropertyBackingField(field))
return true;
// if (settings.SwitchStatementOnString && IsSwitchOnStringCache(field))
@ -592,7 +593,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -592,7 +593,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
FixParameterNames(methodDecl);
if (methodDefinition.HasBody) {
DecompileBody(methodDefinition, method, methodDecl, decompilationContext, typeSystemAstBuilder);
DecompileBody(methodDefinition, method, methodDecl, decompilationContext);
} else if (!method.IsAbstract && method.DeclaringType.Kind != TypeKind.Interface) {
methodDecl.Modifiers |= Modifiers.Extern;
}
@ -621,20 +622,29 @@ namespace ICSharpCode.Decompiler.CSharp @@ -621,20 +622,29 @@ namespace ICSharpCode.Decompiler.CSharp
return typeSystem;
}
void DecompileBody(MethodDefinition methodDefinition, IMethod method, EntityDeclaration entityDecl, ITypeResolveContext decompilationContext, TypeSystemAstBuilder typeSystemAstBuilder)
BlockStatement DecompileBodyInternal(MethodDefinition methodDefinition, IMethod method, EntityDeclaration entityDecl, ITypeResolveContext decompilationContext, string variablesPrefix = null)
{
var specializingTypeSystem = GetSpecializingTypeSystem(decompilationContext);
var ilReader = new ILReader(specializingTypeSystem);
var function = ilReader.ReadIL(methodDefinition.Body, CancellationToken);
function.CheckInvariant(ILPhase.Normal);
int i = 0;
var parameters = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index);
foreach (var parameter in entityDecl.GetChildrenByRole(Roles.Parameter)) {
ILVariable v;
if (parameters.TryGetValue(i, out v))
parameter.AddAnnotation(new ILVariableResolveResult(v, method.Parameters[i].Type));
i++;
if (entityDecl != null) {
int i = 0;
var parameters = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index);
foreach (var parameter in entityDecl.GetChildrenByRole(Roles.Parameter)) {
ILVariable v;
if (parameters.TryGetValue(i, out v))
parameter.AddAnnotation(new ILVariableResolveResult(v, method.Parameters[i].Type));
i++;
}
}
if (!string.IsNullOrWhiteSpace(variablesPrefix)) {
var variables = function.Variables.Where(v => v.Kind != VariableKind.Parameter);
foreach (var v in variables) {
v.Name = variablesPrefix + v.Name;
}
}
var context = new ILTransformContext { TypeSystem = specializingTypeSystem, CancellationToken = CancellationToken };
@ -643,10 +653,24 @@ namespace ICSharpCode.Decompiler.CSharp @@ -643,10 +653,24 @@ namespace ICSharpCode.Decompiler.CSharp
transform.Run(function, context);
function.CheckInvariant(ILPhase.Normal);
}
var statementBuilder = new StatementBuilder(decompilationContext, method);
var body = statementBuilder.ConvertAsBlock(function.Body);
body.AddAnnotation(function.Variables);
return body;
}
entityDecl.AddChild(body, Roles.Body);
internal BlockStatement DecompileLambdaBody(IMethod method)
{
MethodDefinition definition = typeSystem.GetCecil(method) as MethodDefinition;
if (definition == null)
throw new InvalidOperationException("Could not find method in type system");
return DecompileBodyInternal(definition, method, null, new SimpleTypeResolveContext(method), method.Name + "_");
}
void DecompileBody(MethodDefinition methodDefinition, IMethod method, EntityDeclaration entityDecl, ITypeResolveContext decompilationContext)
{
entityDecl.AddChild(DecompileBodyInternal(methodDefinition, method, entityDecl, decompilationContext), Roles.Body);
}
EntityDeclaration DoDecompile(FieldDefinition fieldDefinition, IField field, ITypeResolveContext decompilationContext)
@ -685,10 +709,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -685,10 +709,10 @@ namespace ICSharpCode.Decompiler.CSharp
setter = ((IndexerDeclaration)propertyDecl).Setter;
}
if (property.CanGet && property.Getter.HasBody) {
DecompileBody(propertyDefinition.GetMethod, property.Getter, getter, decompilationContext, typeSystemAstBuilder);
DecompileBody(propertyDefinition.GetMethod, property.Getter, getter, decompilationContext);
}
if (property.CanSet && property.Setter.HasBody) {
DecompileBody(propertyDefinition.SetMethod, property.Setter, setter, decompilationContext, typeSystemAstBuilder);
DecompileBody(propertyDefinition.SetMethod, property.Setter, setter, decompilationContext);
}
var accessor = propertyDefinition.GetMethod ?? propertyDefinition.SetMethod;
if (!accessor.HasOverrides && !accessor.DeclaringType.IsInterface && accessor.IsVirtual == accessor.IsNewSlot)
@ -707,10 +731,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -707,10 +731,10 @@ namespace ICSharpCode.Decompiler.CSharp
eventDecl.Name = ev.Name.Substring(lastDot + 1);
}
if (eventDefinition.AddMethod != null && eventDefinition.AddMethod.HasBody) {
DecompileBody(eventDefinition.AddMethod, ev.AddAccessor, ((CustomEventDeclaration)eventDecl).AddAccessor, decompilationContext, typeSystemAstBuilder);
DecompileBody(eventDefinition.AddMethod, ev.AddAccessor, ((CustomEventDeclaration)eventDecl).AddAccessor, decompilationContext);
}
if (eventDefinition.RemoveMethod != null && eventDefinition.RemoveMethod.HasBody) {
DecompileBody(eventDefinition.RemoveMethod, ev.RemoveAccessor, ((CustomEventDeclaration)eventDecl).RemoveAccessor, decompilationContext, typeSystemAstBuilder);
DecompileBody(eventDefinition.RemoveMethod, ev.RemoveAccessor, ((CustomEventDeclaration)eventDecl).RemoveAccessor, decompilationContext);
}
var accessor = eventDefinition.AddMethod ?? eventDefinition.RemoveMethod;
if (accessor.IsVirtual == accessor.IsNewSlot) {

3
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -730,7 +730,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -730,7 +730,7 @@ namespace ICSharpCode.Decompiler.CSharp
return HandleCallInstruction(inst);
}
static bool IsDelegateConstruction(CallInstruction inst)
internal static bool IsDelegateConstruction(CallInstruction inst)
{
return inst.Arguments.Count == 2
&& (inst.Arguments[1].OpCode == OpCode.LdFtn
@ -764,6 +764,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -764,6 +764,7 @@ namespace ICSharpCode.Decompiler.CSharp
var mre = new MemberReferenceExpression(target, method.Name);
mre.TypeArguments.AddRange(method.TypeArguments.Select(a => ConvertType(a)));
return new ObjectCreateExpression(ConvertType(inst.Method.DeclaringType), mre)
.WithAnnotation(new DelegateConstruction.Annotation(func.OpCode == OpCode.LdVirtFtn, target, method.Name))
.WithILInstruction(inst)
.WithRR(new ConversionResolveResult(
inst.Method.DeclaringType,

220
ICSharpCode.Decompiler/CSharp/Transforms/DelegateConstruction.cs

@ -20,11 +20,11 @@ using System; @@ -20,11 +20,11 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
using ICSharpCode.NRefactory.TypeSystem;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.CSharp.Transforms
@ -34,7 +34,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -34,7 +34,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// For anonymous methods, creates an AnonymousMethodExpression.
/// Also gets rid of any "Display Classes" left over after inlining an anonymous method.
/// </summary>
public class DelegateConstruction : ContextTrackingVisitor<object>
public class DelegateConstruction : ContextTrackingVisitor<object>, IAstTransform
{
internal sealed class Annotation
{
@ -43,91 +43,55 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -43,91 +43,55 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// </summary>
public readonly bool IsVirtual;
public Annotation(bool isVirtual)
public readonly Expression InvocationTarget;
public readonly string MethodName;
public Annotation(bool isVirtual, Expression invocationTarget, string methodName)
{
this.IsVirtual = isVirtual;
this.InvocationTarget = invocationTarget;
this.MethodName = methodName;
}
}
TransformContext context;
List<string> currentlyUsedVariableNames = new List<string>();
int nextLocalVariableIndex;
public DelegateConstruction(DecompilerContext context) : base(context)
public void Run(AstNode rootNode, TransformContext context)
{
this.context = context;
base.Initialize(context);
rootNode.AcceptVisitor(this);
}
public override object VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, object data)
public override object VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression)
{
if (objectCreateExpression.Arguments.Count == 2) {
Expression obj = objectCreateExpression.Arguments.First();
Expression func = objectCreateExpression.Arguments.Last();
Annotation annotation = func.Annotation<Annotation>();
if (annotation != null) {
IdentifierExpression methodIdent = (IdentifierExpression)((InvocationExpression)func).Arguments.Single();
MethodReference method = methodIdent.Annotation<MethodReference>();
if (method != null) {
if (HandleAnonymousMethod(objectCreateExpression, obj, method))
return null;
// Perform the transformation to "new Action(obj.func)".
obj.Remove();
methodIdent.Remove();
if (!annotation.IsVirtual && obj is ThisReferenceExpression) {
// maybe it's getting the pointer of a base method?
if (method.DeclaringType.GetElementType() != context.CurrentType) {
obj = new BaseReferenceExpression();
}
}
if (!annotation.IsVirtual && obj is NullReferenceExpression && !method.HasThis) {
// We're loading a static method.
// However it is possible to load extension methods with an instance, so we compare the number of arguments:
bool isExtensionMethod = false;
TypeReference delegateType = objectCreateExpression.Type.Annotation<TypeReference>();
if (delegateType != null) {
TypeDefinition delegateTypeDef = delegateType.Resolve();
if (delegateTypeDef != null) {
MethodDefinition invokeMethod = delegateTypeDef.Methods.FirstOrDefault(m => m.Name == "Invoke");
if (invokeMethod != null) {
isExtensionMethod = (invokeMethod.Parameters.Count + 1 == method.Parameters.Count);
}
}
}
if (!isExtensionMethod) {
obj = new TypeReferenceExpression { Type = AstBuilder.ConvertType(method.DeclaringType) };
}
}
// now transform the identifier into a member reference
MemberReferenceExpression mre = new MemberReferenceExpression();
mre.Target = obj;
mre.MemberName = methodIdent.Identifier;
methodIdent.TypeArguments.MoveTo(mre.TypeArguments);
mre.AddAnnotation(method);
objectCreateExpression.Arguments.Clear();
objectCreateExpression.Arguments.Add(mre);
return null;
}
}
Annotation annotation = objectCreateExpression.Annotation<Annotation>();
IMethod method = objectCreateExpression.GetSymbol() as IMethod;
if (annotation != null && method != null) {
if (HandleAnonymousMethod(objectCreateExpression, annotation.InvocationTarget, method))
return null;
}
return base.VisitObjectCreateExpression(objectCreateExpression, data);
return base.VisitObjectCreateExpression(objectCreateExpression);
}
internal static bool IsAnonymousMethod(DecompilerContext context, MethodDefinition method)
internal static bool IsAnonymousMethod(ITypeDefinition decompiledTypeDefinition, IMethod method)
{
if (method == null || !(method.HasGeneratedName() || method.Name.Contains("$")))
return false;
if (!(method.IsCompilerGenerated() || IsPotentialClosure(context, method.DeclaringType)))
if (!(method.IsCompilerGenerated() || IsPotentialClosure(decompiledTypeDefinition, method.DeclaringTypeDefinition)))
return false;
return true;
}
bool HandleAnonymousMethod(ObjectCreateExpression objectCreateExpression, Expression target, MethodReference methodRef)
bool HandleAnonymousMethod(ObjectCreateExpression objectCreateExpression, Expression target, IMethod method)
{
if (!context.Settings.AnonymousMethods)
return false; // anonymous method decompilation is disabled
if (target != null && !(target is IdentifierExpression || target is ThisReferenceExpression || target is NullReferenceExpression))
if (target != null && !(target is TypeReferenceExpression || target is IdentifierExpression || target is ThisReferenceExpression || target is NullReferenceExpression))
return false; // don't copy arbitrary expressions, deal with identifiers only
// Anonymous methods are defined in the same assembly
MethodDefinition method = methodRef.ResolveWithinSameModule();
if (!IsAnonymousMethod(context, method))
if (!IsAnonymousMethod(context.DecompiledTypeDefinition, method))
return false;
// Create AnonymousMethodExpression and prepare parameters
@ -135,7 +99,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -135,7 +99,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
ame.CopyAnnotationsFrom(objectCreateExpression); // copy ILRanges etc.
ame.RemoveAnnotations<MethodReference>(); // remove reference to delegate ctor
ame.AddAnnotation(method); // add reference to anonymous method
ame.Parameters.AddRange(AstBuilder.MakeParameters(method, isLambda: true));
ame.Parameters.AddRange(MakeParameters(method));
ame.HasParameterList = true;
// rename variables so that they don't conflict with the parameters:
@ -144,15 +108,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -144,15 +108,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
// Decompile the anonymous method:
DecompilerContext subContext = context.Clone();
subContext.CurrentMethod = method;
subContext.CurrentMethodIsAsync = false;
subContext.ReservedVariableNames.AddRange(currentlyUsedVariableNames);
BlockStatement body = AstMethodBodyBuilder.CreateMethodBody(method, subContext, ame.Parameters);
TransformationPipeline.RunTransformationsUntil(body, v => v is DelegateConstruction, subContext);
body.AcceptVisitor(this, null);
var body = DecompileBody(method);
bool isLambda = false;
if (ame.Parameters.All(p => p.ParameterModifier == ParameterModifier.None)) {
@ -164,7 +120,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -164,7 +120,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
var parameterReferencingIdentifiers =
from ident in body.Descendants.OfType<IdentifierExpression>()
let v = ident.Annotation<ILVariable>()
where v != null && v.IsParameter && method.Parameters.Contains(v.OriginalParameter)
where v != null && v.Kind == VariableKind.Parameter
select ident;
if (!parameterReferencingIdentifiers.Any()) {
ame.Parameters.Clear();
@ -190,8 +146,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -190,8 +146,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
ame.Body = body;
replacement = ame;
}
var expectedType = objectCreateExpression.Annotation<TypeInformation>()?.ExpectedType?.Resolve();
if (expectedType != null && !expectedType.IsDelegate()) {
var expectedType = objectCreateExpression.GetResolveResult()?.Type.GetDefinition();
if (expectedType != null && expectedType.Kind != TypeKind.Delegate) {
var simplifiedDelegateCreation = (ObjectCreateExpression)objectCreateExpression.Clone();
simplifiedDelegateCreation.Arguments.Clear();
simplifiedDelegateCreation.Arguments.Add(replacement);
@ -201,98 +157,118 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -201,98 +157,118 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return true;
}
internal static bool IsPotentialClosure(DecompilerContext context, TypeDefinition potentialDisplayClass)
IEnumerable<ParameterDeclaration> MakeParameters(IMethod method)
{
foreach (var parameter in method.Parameters) {
var pd = context.TypeSystemAstBuilder.ConvertParameter(parameter);
if (parameter.Type.ContainsAnonymousType())
pd.Type = null;
yield return pd;
}
}
BlockStatement DecompileBody(IMethod method)
{
// subContext.ReservedVariableNames.AddRange(currentlyUsedVariableNames);
return new CSharpDecompiler(context.TypeSystem, context.Settings).DecompileLambdaBody(method);
}
internal static bool IsPotentialClosure(ITypeDefinition decompiledTypeDefinition, ITypeDefinition potentialDisplayClass)
{
if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass())
return false;
// check that methodContainingType is within containingType
while (potentialDisplayClass != context.CurrentType) {
potentialDisplayClass = potentialDisplayClass.DeclaringType;
while (potentialDisplayClass != decompiledTypeDefinition) {
potentialDisplayClass = potentialDisplayClass.DeclaringTypeDefinition;
if (potentialDisplayClass == null)
return false;
}
return true;
}
public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data)
/*
public override object VisitInvocationExpression(InvocationExpression invocationExpression)
{
if (context.Settings.ExpressionTrees && ExpressionTreeConverter.CouldBeExpressionTree(invocationExpression)) {
Expression converted = ExpressionTreeConverter.TryConvert(context, invocationExpression);
if (converted != null) {
invocationExpression.ReplaceWith(converted);
return converted.AcceptVisitor(this, data);
return converted.AcceptVisitor(this);
}
}
return base.VisitInvocationExpression(invocationExpression, data);
return base.VisitInvocationExpression(invocationExpression);
}
*/
#region Track current variables
public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data)
public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration)
{
Debug.Assert(currentlyUsedVariableNames.Count == 0);
try {
nextLocalVariableIndex = methodDeclaration.Body.Annotation<ICollection<ILVariable>>()?.Where(v => v.Kind == VariableKind.Local).MaxOrDefault(v => v.Index) + 1 ?? 0;
currentlyUsedVariableNames.AddRange(methodDeclaration.Parameters.Select(p => p.Name));
return base.VisitMethodDeclaration(methodDeclaration, data);
return base.VisitMethodDeclaration(methodDeclaration);
} finally {
currentlyUsedVariableNames.Clear();
}
}
public override object VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, object data)
public override object VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration)
{
Debug.Assert(currentlyUsedVariableNames.Count == 0);
try {
nextLocalVariableIndex = operatorDeclaration.Body.Annotation<ICollection<ILVariable>>()?.Where(v => v.Kind == VariableKind.Local).MaxOrDefault(v => v.Index) + 1 ?? 0;
currentlyUsedVariableNames.AddRange(operatorDeclaration.Parameters.Select(p => p.Name));
return base.VisitOperatorDeclaration(operatorDeclaration, data);
return base.VisitOperatorDeclaration(operatorDeclaration);
} finally {
currentlyUsedVariableNames.Clear();
}
}
public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data)
public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration)
{
Debug.Assert(currentlyUsedVariableNames.Count == 0);
try {
nextLocalVariableIndex = constructorDeclaration.Body.Annotation<ICollection<ILVariable>>()?.Where(v => v.Kind == VariableKind.Local).MaxOrDefault(v => v.Index) + 1 ?? 0;
currentlyUsedVariableNames.AddRange(constructorDeclaration.Parameters.Select(p => p.Name));
return base.VisitConstructorDeclaration(constructorDeclaration, data);
return base.VisitConstructorDeclaration(constructorDeclaration);
} finally {
currentlyUsedVariableNames.Clear();
}
}
public override object VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration, object data)
public override object VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration)
{
Debug.Assert(currentlyUsedVariableNames.Count == 0);
try {
currentlyUsedVariableNames.AddRange(indexerDeclaration.Parameters.Select(p => p.Name));
return base.VisitIndexerDeclaration(indexerDeclaration, data);
return base.VisitIndexerDeclaration(indexerDeclaration);
} finally {
currentlyUsedVariableNames.Clear();
}
}
public override object VisitAccessor(Accessor accessor, object data)
public override object VisitAccessor(Accessor accessor)
{
try {
nextLocalVariableIndex = accessor.Body.Annotation<ICollection<ILVariable>>()?.Where(v => v.Kind == VariableKind.Local).MaxOrDefault(v => v.Index) + 1 ?? 0;
currentlyUsedVariableNames.Add("value");
return base.VisitAccessor(accessor, data);
return base.VisitAccessor(accessor);
} finally {
currentlyUsedVariableNames.RemoveAt(currentlyUsedVariableNames.Count - 1);
}
}
public override object VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, object data)
public override object VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement)
{
foreach (VariableInitializer v in variableDeclarationStatement.Variables)
currentlyUsedVariableNames.Add(v.Name);
return base.VisitVariableDeclarationStatement(variableDeclarationStatement, data);
return base.VisitVariableDeclarationStatement(variableDeclarationStatement);
}
public override object VisitFixedStatement(FixedStatement fixedStatement, object data)
public override object VisitFixedStatement(FixedStatement fixedStatement)
{
foreach (VariableInitializer v in fixedStatement.Variables)
currentlyUsedVariableNames.Add(v.Name);
return base.VisitFixedStatement(fixedStatement, data);
return base.VisitFixedStatement(fixedStatement);
}
#endregion
@ -302,35 +278,35 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -302,35 +278,35 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
new ObjectCreateExpression { Type = new AnyNode("type") }
));
public override object VisitBlockStatement(BlockStatement blockStatement, object data)
public override object VisitBlockStatement(BlockStatement blockStatement)
{
int numberOfVariablesOutsideBlock = currentlyUsedVariableNames.Count;
base.VisitBlockStatement(blockStatement, data);
base.VisitBlockStatement(blockStatement);
foreach (ExpressionStatement stmt in blockStatement.Statements.OfType<ExpressionStatement>().ToArray()) {
Match displayClassAssignmentMatch = displayClassAssignmentPattern.Match(stmt);
if (!displayClassAssignmentMatch.Success)
continue;
ILVariable variable = displayClassAssignmentMatch.Get<AstNode>("variable").Single().Annotation<ILVariable>();
ILVariable variable = displayClassAssignmentMatch.Get<AstNode>("variable").Single().Annotation<ILVariableResolveResult>()?.Variable;
if (variable == null)
continue;
TypeDefinition type = variable.Type.ResolveWithinSameModule();
if (!IsPotentialClosure(context, type))
var type = variable.Type.GetDefinition();
if (!IsPotentialClosure(context.DecompiledTypeDefinition, type))
continue;
if (displayClassAssignmentMatch.Get<AstType>("type").Single().Annotation<TypeReference>().ResolveWithinSameModule() != type)
if (!(displayClassAssignmentMatch.Get<AstType>("type").Single().GetSymbol() as IType).GetDefinition().Equals(type))
continue;
// Looks like we found a display class creation. Now let's verify that the variable is used only for field accesses:
bool ok = true;
foreach (var identExpr in blockStatement.Descendants.OfType<IdentifierExpression>()) {
if (identExpr.Identifier == variable.Name && identExpr != displayClassAssignmentMatch.Get("variable").Single()) {
if (!(identExpr.Parent is MemberReferenceExpression && identExpr.Parent.Annotation<FieldReference>() != null))
if (!(identExpr.Parent is MemberReferenceExpression && identExpr.Parent.GetSymbol() is IField))
ok = false;
}
}
if (!ok)
continue;
Dictionary<FieldReference, AstNode> dict = new Dictionary<FieldReference, AstNode>();
Dictionary<IField, AstNode> dict = new Dictionary<IField, AstNode>();
// Delete the variable declaration statement:
VariableDeclarationStatement displayClassVarDecl = PatternStatementTransform.FindVariableDeclaration(stmt, variable.Name);
@ -344,7 +320,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -344,7 +320,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
// Delete any following statements as long as they assign parameters to the display class
BlockStatement rootBlock = blockStatement.Ancestors.OfType<BlockStatement>().LastOrDefault() ?? blockStatement;
List<ILVariable> parameterOccurrances = rootBlock.Descendants.OfType<IdentifierExpression>()
.Select(n => n.Annotation<ILVariable>()).Where(p => p != null && p.IsParameter).ToList();
.Select(n => n.Annotation<ILVariable>()).Where(p => p != null && p.Kind == VariableKind.Parameter).ToList();
AstNode next;
for (; cur != null; cur = next) {
next = cur.NextSibling;
@ -362,7 +338,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -362,7 +338,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
);
Match m = closureFieldAssignmentPattern.Match(cur);
if (m.Success) {
FieldDefinition fieldDef = m.Get<MemberReferenceExpression>("left").Single().Annotation<FieldReference>().ResolveWithinSameModule();
AstNode right = m.Get<AstNode>("right").Single();
bool isParameter = false;
bool isDisplayClassParentPointerAssignment = false;
@ -371,8 +346,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -371,8 +346,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} else if (right is IdentifierExpression) {
// handle parameters only if the whole method contains no other occurrence except for 'right'
ILVariable v = right.Annotation<ILVariable>();
isParameter = v.IsParameter && parameterOccurrances.Count(c => c == v) == 1;
if (!isParameter && IsPotentialClosure(context, v.Type.ResolveWithinSameModule())) {
isParameter = v.Kind == VariableKind.Parameter && parameterOccurrances.Count(c => c == v) == 1;
if (!isParameter && IsPotentialClosure(context.DecompiledTypeDefinition, v.Type.GetDefinition())) {
// parent display class within the same method
// (closure2.localsX = closure1;)
isDisplayClassParentPointerAssignment = true;
@ -383,8 +358,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -383,8 +358,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
MemberReferenceExpression mre = m.Get<MemberReferenceExpression>("right").Single();
do {
// descend into the targets of the mre as long as the field types are closures
FieldDefinition fieldDef2 = mre.Annotation<FieldReference>().ResolveWithinSameModule();
if (fieldDef2 == null || !IsPotentialClosure(context, fieldDef2.FieldType.ResolveWithinSameModule())) {
var fieldDef2 = mre.GetSymbol() as IField;
if (fieldDef2 == null || !IsPotentialClosure(context.DecompiledTypeDefinition, fieldDef2.Type.GetDefinition())) {
break;
}
// if we finally get to a this reference, it's copying a display class parent pointer
@ -394,8 +369,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -394,8 +369,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
mre = mre.Target as MemberReferenceExpression;
} while (mre != null);
}
if (isParameter || isDisplayClassParentPointerAssignment) {
dict[fieldDef] = right;
var field = m.Get<MemberReferenceExpression>("left").Single().GetSymbol() as IField;
if (field != null && (isParameter || isDisplayClassParentPointerAssignment)) {
dict[field] = right;
cur.Remove();
} else {
break;
@ -407,7 +383,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -407,7 +383,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
// Now create variables for all fields of the display class (except for those that we already handled as parameters)
List<Tuple<AstType, ILVariable>> variablesToDeclare = new List<Tuple<AstType, ILVariable>>();
foreach (FieldDefinition field in type.Fields) {
foreach (var field in type.Fields) {
if (field.IsStatic)
continue; // skip static fields
if (dict.ContainsKey(field)) // skip field if it already was handled as parameter
@ -417,13 +393,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -417,13 +393,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
capturedVariableName = capturedVariableName.Substring(10);
EnsureVariableNameIsAvailable(blockStatement, capturedVariableName);
currentlyUsedVariableNames.Add(capturedVariableName);
ILVariable ilVar = new ILVariable
ILVariable ilVar = new ILVariable(VariableKind.Local, field.Type, nextLocalVariableIndex++)
{
IsGenerated = true,
Name = capturedVariableName,
Type = field.FieldType,
Name = capturedVariableName
};
variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), ilVar));
variablesToDeclare.Add(Tuple.Create(context.TypeSystemAstBuilder.ConvertType(field.Type), ilVar));
dict[field] = new IdentifierExpression(capturedVariableName).WithAnnotation(ilVar);
}
@ -432,7 +406,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -432,7 +406,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (identExpr.Identifier == variable.Name) {
MemberReferenceExpression mre = (MemberReferenceExpression)identExpr.Parent;
AstNode replacement;
if (dict.TryGetValue(mre.Annotation<FieldReference>().ResolveWithinSameModule(), out replacement)) {
if (dict.TryGetValue((IField)mre.GetSymbol(), out replacement)) {
mre.ReplaceWith(replacement.Clone());
}
}
@ -457,6 +431,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -457,6 +431,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
// name is still available
return;
}
throw new NotImplementedException("naming conflict: " + name);
}/*
// Naming conflict. Let's rename the existing variable so that the field keeps the name from metadata.
NameVariables nv = new NameVariables();
// Add currently used variable and parameter names
@ -493,6 +469,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -493,6 +469,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
v.Name = newName;
}
}
}
}*/
}
}

3
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -68,6 +68,7 @@ @@ -68,6 +68,7 @@
<Compile Include="CSharp\Transforms\ContextTrackingVisitor.cs" />
<Compile Include="CSharp\Transforms\DecimalConstantTransform.cs" />
<Compile Include="CSharp\Transforms\DeclareVariables.cs" />
<Compile Include="CSharp\Transforms\DelegateConstruction.cs" />
<Compile Include="CSharp\Transforms\EscapeInvalidIdentifiers.cs" />
<Compile Include="CSharp\Transforms\FixNameCollisions.cs" />
<Compile Include="CSharp\Transforms\IntroduceUsingDeclarations.cs" />
@ -131,6 +132,7 @@ @@ -131,6 +132,7 @@
<Compile Include="IL\Transforms\ExpressionTransforms.cs" />
<Compile Include="IL\Transforms\InlineCompilerGeneratedVariables.cs" />
<Compile Include="IL\Transforms\LoopingTransform.cs" />
<Compile Include="IL\Transforms\RemoveCachedDelegateInitialization.cs" />
<Compile Include="IL\Transforms\RemoveDeadVariableInit.cs" />
<Compile Include="IL\Transforms\SplitVariables.cs" />
<Compile Include="IL\Transforms\TransformArrayInitializers.cs" />
@ -153,6 +155,7 @@ @@ -153,6 +155,7 @@
<Compile Include="IL\InstructionOutputExtensions.cs" />
<Compile Include="IL\PrimitiveType.cs" />
<Compile Include="IL\StackType.cs" />
<Compile Include="NRExtensions.cs" />
<Compile Include="Output\ITextOutput.cs" />
<Compile Include="Output\PlainTextOutput.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

95
ICSharpCode.Decompiler/IL/Transforms/RemoveCachedDelegateInitialization.cs

@ -0,0 +1,95 @@ @@ -0,0 +1,95 @@
// Copyright (c) 2011-2016 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Linq;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.Transforms;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
{
public class RemoveCachedDelegateInitialization : IILTransform
{
ILTransformContext context;
ITypeResolveContext decompilationContext;
void IILTransform.Run(ILFunction function, ILTransformContext context)
{
if (!new DecompilerSettings().AnonymousMethods)
return;
this.context = context;
this.decompilationContext = new SimpleTypeResolveContext(context.TypeSystem.Resolve(function.Method));
foreach (var block in function.Descendants.OfType<Block>()) {
for (int i = block.Instructions.Count - 1; i >= 0; i--) {
var inst = block.Instructions[i] as IfInstruction;
if (inst != null) {
if (CachedDelegateInitializationWithField(inst)) {
block.Instructions.RemoveAt(i);
continue;
}
if (CachedDelegateInitializationWithLocal(inst)) {
block.Instructions.RemoveAt(i);
continue;
}
}
}
}
}
bool CachedDelegateInitializationWithField(IfInstruction inst)
{
// if (comp(ldsfld CachedAnonMethodDelegate == ldnull) {
// stsfld CachedAnonMethodDelegate(DelegateConstruction)
// }
// ... one usage of CachedAnonMethodDelegate ...
// =>
// ... one usage of DelegateConstruction ...
Block trueInst = inst.TrueInst as Block;
var condition = inst.Condition as Comp;
if (condition == null || trueInst == null || trueInst.Instructions.Count != 1 || !inst.FalseInst.MatchNop())
return false;
IField field, field2;
ILInstruction value;
var storeInst = trueInst.Instructions[0];
if (!condition.Left.MatchLdsFld(out field) || !condition.Right.MatchLdNull())
return false;
if (!storeInst.MatchStsFld(out value, out field2) || !field.Equals(field2) || !field.IsCompilerGeneratedOrIsInCompilerGeneratedClass())
return false;
if (value.OpCode != OpCode.NewObj || !ExpressionBuilder.IsDelegateConstruction((CallInstruction)value))
return false;
var targetMethod = ((IInstructionWithMethodOperand)((CallInstruction)value).Arguments[1]).Method;
if (!DelegateConstruction.IsAnonymousMethod(decompilationContext.CurrentTypeDefinition, targetMethod))
return false;
var nextInstruction = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex + 1);
if (nextInstruction == null)
return false;
var usages = nextInstruction.Descendants.OfType<LdsFld>().Where(i => i.Field.Equals(field)).ToArray();
if (usages.Length > 1)
return false;
usages[0].ReplaceWith(value);
return true;
}
bool CachedDelegateInitializationWithLocal(IfInstruction inst)
{
return false;
}
}
}

85
ICSharpCode.Decompiler/NRExtensions.cs

@ -0,0 +1,85 @@ @@ -0,0 +1,85 @@
// Copyright (c) 2015 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.Decompiler
{
public static class NRExtensions
{
public static bool IsCompilerGenerated(this IEntity entity)
{
if (entity != null) {
foreach (IAttribute a in entity.Attributes) {
if (a.AttributeType.FullName == "System.Runtime.CompilerServices.CompilerGeneratedAttribute")
return true;
}
}
return false;
}
public static bool IsCompilerGeneratedOrIsInCompilerGeneratedClass(this IEntity entity)
{
if (entity == null)
return false;
if (entity.IsCompilerGenerated())
return true;
return IsCompilerGeneratedOrIsInCompilerGeneratedClass(entity.DeclaringTypeDefinition);
}
public static bool HasGeneratedName(this IMember member)
{
return member.Name.StartsWith("<", StringComparison.Ordinal);
}
public static bool HasGeneratedName(this IType type)
{
return type.Name.StartsWith("<", StringComparison.Ordinal);
}
public static bool IsAnonymousType(this IType type)
{
if (type == null)
return false;
if (string.IsNullOrEmpty(type.Namespace) && type.HasGeneratedName() && (type.Name.Contains("AnonType") || type.Name.Contains("AnonymousType"))) {
ITypeDefinition td = type.GetDefinition();
return td != null && td.IsCompilerGenerated();
}
return false;
}
public static bool ContainsAnonymousType(this IType type)
{
var visitor = new ContainsAnonTypeVisitor();
type.AcceptVisitor(visitor);
return visitor.ContainsAnonType;
}
class ContainsAnonTypeVisitor : TypeVisitor
{
public bool ContainsAnonType;
public override IType VisitOtherType(IType type)
{
if (IsAnonymousType(type))
ContainsAnonType = true;
return base.VisitOtherType(type);
}
}
}
}

11
ICSharpCode.Decompiler/Util/CollectionExtensions.cs

@ -27,6 +27,17 @@ namespace ICSharpCode.Decompiler @@ -27,6 +27,17 @@ namespace ICSharpCode.Decompiler
return stack.Peek();
}
public static int MaxOrDefault<T>(this IEnumerable<T> input, Func<T, int> selector)
{
int max = 0;
foreach (var element in input) {
int value = selector(element);
if (value > max)
max = value;
}
return max;
}
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> input)
{
foreach (T item in input)

Loading…
Cancel
Save