|
|
|
@ -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; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}*/ |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|