Browse Source

Move DelegateConstruction transform to ILAst

pull/734/head
Siegfried Pammer 9 years ago
parent
commit
81e8df3f96
  1. 97
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 117
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 6
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  4. 2
      ICSharpCode.Decompiler/CSharp/Transforms/DelegateConstruction.cs
  5. 3
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  6. 19
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  7. 181
      ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
  8. 95
      ICSharpCode.Decompiler/IL/Transforms/RemoveCachedDelegateInitialization.cs
  9. 18
      ICSharpCode.Decompiler/NRExtensions.cs

97
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -46,30 +46,34 @@ namespace ICSharpCode.Decompiler.CSharp
readonly DecompilerTypeSystem typeSystem; readonly DecompilerTypeSystem typeSystem;
readonly DecompilerSettings settings; readonly DecompilerSettings settings;
List<IILTransform> ilTransforms = new List<IILTransform> { List<IILTransform> ilTransforms = GetILTransforms();
new SplitVariables(),
new ControlFlowSimplification(), public static List<IILTransform> GetILTransforms()
new ILInlining(), {
new DetectPinRegions(), return new List<IILTransform> {
new LoopDetection(), new SplitVariables(),
new IntroduceExitPoints(), new ControlFlowSimplification(),
new ConditionDetection(), new ILInlining(),
new ILInlining(), new DetectPinRegions(),
new CopyPropagation(), new LoopDetection(),
new InlineCompilerGeneratedVariables(), new IntroduceExitPoints(),
new ExpressionTransforms(), // must run once before "the loop" to allow RemoveDeadVariablesInit new ConditionDetection(),
new RemoveDeadVariableInit(), // must run after ExpressionTransforms because it does not handle stobj(ldloca V, ...) new ILInlining(),
new RemoveCachedDelegateInitialization(), new CopyPropagation(),
new LoopingTransform( new InlineCompilerGeneratedVariables(),
new ExpressionTransforms(), new ExpressionTransforms(), // must run once before "the loop" to allow RemoveDeadVariablesInit
new TransformArrayInitializers(), new RemoveDeadVariableInit(), // must run after ExpressionTransforms because it does not handle stobj(ldloca V, ...)
new ILInlining() new DelegateConstruction(),
) new LoopingTransform(
}; new ExpressionTransforms(),
new TransformArrayInitializers(),
new ILInlining()
)
};
}
List<IAstTransform> astTransforms = new List<IAstTransform> { List<IAstTransform> astTransforms = new List<IAstTransform> {
//new PushNegation(), //new PushNegation(),
new DelegateConstruction(),
new PatternStatementTransform(), new PatternStatementTransform(),
new ReplaceMethodCallsWithOperators(), new ReplaceMethodCallsWithOperators(),
new IntroduceUnsafeModifier(), new IntroduceUnsafeModifier(),
@ -605,29 +609,10 @@ namespace ICSharpCode.Decompiler.CSharp
return methodDecl; return methodDecl;
} }
IDecompilerTypeSystem GetSpecializingTypeSystem(ITypeResolveContext decompilationContext) void DecompileBody(MethodDefinition methodDefinition, IMethod method, EntityDeclaration entityDecl, ITypeResolveContext decompilationContext)
{
IList<IType> classTypeParameters = null;
IList<IType> methodTypeParameters = null;
if (decompilationContext.CurrentTypeDefinition != null)
classTypeParameters = decompilationContext.CurrentTypeDefinition.TypeArguments;
IMethod method = decompilationContext.CurrentMember as IMethod;
if (method != null)
methodTypeParameters = method.TypeArguments;
if ((classTypeParameters != null && classTypeParameters.Count > 0) || (methodTypeParameters != null && methodTypeParameters.Count > 0))
return new SpecializingDecompilerTypeSystem(typeSystem, new TypeParameterSubstitution(classTypeParameters, methodTypeParameters));
else
return typeSystem;
}
BlockStatement DecompileBodyInternal(MethodDefinition methodDefinition, IMethod method, EntityDeclaration entityDecl, ITypeResolveContext decompilationContext, string variablesPrefix = null)
{ {
var specializingTypeSystem = GetSpecializingTypeSystem(decompilationContext); var specializingTypeSystem = typeSystem.GetSpecializingTypeSystem(decompilationContext);
var ilReader = new ILReader(specializingTypeSystem); ILFunction function = ILFunction.Read(specializingTypeSystem, methodDefinition, CancellationToken);
var function = ilReader.ReadIL(methodDefinition.Body, CancellationToken);
function.CheckInvariant(ILPhase.Normal);
if (entityDecl != null) { if (entityDecl != null) {
int i = 0; int i = 0;
@ -640,13 +625,6 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
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 }; var context = new ILTransformContext { TypeSystem = specializingTypeSystem, CancellationToken = CancellationToken };
foreach (var transform in ilTransforms) { foreach (var transform in ilTransforms) {
CancellationToken.ThrowIfCancellationRequested(); CancellationToken.ThrowIfCancellationRequested();
@ -654,23 +632,8 @@ namespace ICSharpCode.Decompiler.CSharp
function.CheckInvariant(ILPhase.Normal); function.CheckInvariant(ILPhase.Normal);
} }
var statementBuilder = new StatementBuilder(decompilationContext, method); var statementBuilder = new StatementBuilder(specializingTypeSystem, decompilationContext, method);
var body = statementBuilder.ConvertAsBlock(function.Body); entityDecl.AddChild(statementBuilder.ConvertAsBlock(function.Body), Roles.Body);
body.AddAnnotation(function.Variables);
return 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) EntityDeclaration DoDecompile(FieldDefinition fieldDefinition, IField field, ITypeResolveContext decompilationContext)

117
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -60,13 +60,17 @@ namespace ICSharpCode.Decompiler.CSharp
/// </remarks> /// </remarks>
class ExpressionBuilder : ILVisitor<TranslatedExpression> class ExpressionBuilder : ILVisitor<TranslatedExpression>
{ {
readonly IDecompilerTypeSystem typeSystem;
readonly ITypeResolveContext decompilationContext;
internal readonly ICompilation compilation; internal readonly ICompilation compilation;
internal readonly CSharpResolver resolver; internal readonly CSharpResolver resolver;
readonly TypeSystemAstBuilder astBuilder; readonly TypeSystemAstBuilder astBuilder;
public ExpressionBuilder(ITypeResolveContext decompilationContext) public ExpressionBuilder(IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext)
{ {
Debug.Assert(decompilationContext != null); Debug.Assert(decompilationContext != null);
this.typeSystem = typeSystem;
this.decompilationContext = decompilationContext;
this.compilation = decompilationContext.Compilation; this.compilation = decompilationContext.Compilation;
this.resolver = new CSharpResolver(new CSharpTypeResolveContext(compilation.MainAssembly, null, decompilationContext.CurrentTypeDefinition, decompilationContext.CurrentMember)); this.resolver = new CSharpResolver(new CSharpTypeResolveContext(compilation.MainAssembly, null, decompilationContext.CurrentTypeDefinition, decompilationContext.CurrentMember));
this.astBuilder = new TypeSystemAstBuilder(resolver); this.astBuilder = new TypeSystemAstBuilder(resolver);
@ -730,22 +734,20 @@ namespace ICSharpCode.Decompiler.CSharp
return HandleCallInstruction(inst); return HandleCallInstruction(inst);
} }
internal static bool IsDelegateConstruction(CallInstruction inst)
{
return inst.Arguments.Count == 2
&& (inst.Arguments[1].OpCode == OpCode.LdFtn
|| inst.Arguments[1].OpCode == OpCode.LdVirtFtn)
&& inst.Method.DeclaringType.Kind == TypeKind.Delegate;
}
TranslatedExpression HandleDelegateConstruction(CallInstruction inst) TranslatedExpression HandleDelegateConstruction(CallInstruction inst)
{ {
ILInstruction func = inst.Arguments[1]; ILInstruction func = inst.Arguments[1];
IMethod method; IMethod method;
if (func.OpCode == OpCode.LdFtn) { switch (func.OpCode) {
method = ((LdFtn)func).Method; case OpCode.LdFtn:
} else { method = ((LdFtn)func).Method;
method = ((LdVirtFtn)func).Method; break;
case OpCode.LdVirtFtn:
method = ((LdVirtFtn)func).Method;
break;
default:
method = (IMethod)typeSystem.Resolve(((ILFunction)func).Method);
break;
} }
var target = TranslateTarget(method, inst.Arguments[0], func.OpCode == OpCode.LdFtn); var target = TranslateTarget(method, inst.Arguments[0], func.OpCode == OpCode.LdFtn);
@ -763,14 +765,97 @@ namespace ICSharpCode.Decompiler.CSharp
var mre = new MemberReferenceExpression(target, method.Name); var mre = new MemberReferenceExpression(target, method.Name);
mre.TypeArguments.AddRange(method.TypeArguments.Select(a => ConvertType(a))); mre.TypeArguments.AddRange(method.TypeArguments.Select(a => ConvertType(a)));
return new ObjectCreateExpression(ConvertType(inst.Method.DeclaringType), mre) var oce = new ObjectCreateExpression(ConvertType(inst.Method.DeclaringType), mre)
.WithAnnotation(new DelegateConstruction.Annotation(func.OpCode == OpCode.LdVirtFtn, target, method.Name)) // .WithAnnotation(new DelegateConstruction.Annotation(func.OpCode == OpCode.LdVirtFtn, target, method.Name))
.WithILInstruction(inst) .WithILInstruction(inst)
.WithRR(new ConversionResolveResult( .WithRR(new ConversionResolveResult(
inst.Method.DeclaringType, inst.Method.DeclaringType,
new MemberResolveResult(target.ResolveResult, method), new MemberResolveResult(target.ResolveResult, method),
// TODO handle extension methods capturing the first argument // TODO handle extension methods capturing the first argument
Conversion.MethodGroupConversion(method, func.OpCode == OpCode.LdVirtFtn, false))); Conversion.MethodGroupConversion(method, func.OpCode == OpCode.LdVirtFtn, false)));
if (func is ILFunction) {
return TranslateFunction(oce, target, (ILFunction)func);
} else {
return oce;
}
}
TranslatedExpression TranslateFunction(TranslatedExpression objectCreateExpression, TranslatedExpression target, ILFunction function)
{
var method = typeSystem.Resolve(function.Method) as IMethod;
Debug.Assert(method != null);
// Create AnonymousMethodExpression and prepare parameters
AnonymousMethodExpression ame = new AnonymousMethodExpression();
ame.Parameters.AddRange(MakeParameters(method, function));
ame.HasParameterList = true;
StatementBuilder builder = new StatementBuilder(typeSystem, decompilationContext, method);
var body = builder.ConvertAsBlock(function.Body);
bool isLambda = false;
if (ame.Parameters.All(p => p.ParameterModifier == ParameterModifier.None)) {
isLambda = (body.Statements.Count == 1 && body.Statements.Single() is ReturnStatement);
}
// Remove the parameter list from an AnonymousMethodExpression if the original method had no names,
// and the parameters are not used in the method body
if (!isLambda && method.Parameters.All(p => string.IsNullOrEmpty(p.Name))) {
var parameterReferencingIdentifiers =
from ident in body.Descendants.OfType<IdentifierExpression>()
let v = ident.Annotation<ILVariable>()
where v != null && v.Kind == VariableKind.Parameter
select ident;
if (!parameterReferencingIdentifiers.Any()) {
ame.Parameters.Clear();
ame.HasParameterList = false;
}
}
// Replace all occurrences of 'this' in the method body with the delegate's target:
foreach (AstNode node in body.Descendants) {
if (node is ThisReferenceExpression)
node.ReplaceWith(target.Expression.Clone());
}
Expression replacement;
if (isLambda) {
LambdaExpression lambda = new LambdaExpression();
lambda.CopyAnnotationsFrom(ame);
ame.Parameters.MoveTo(lambda.Parameters);
Expression returnExpr = ((ReturnStatement)body.Statements.Single()).Expression;
returnExpr.Remove();
lambda.Body = returnExpr;
replacement = lambda;
} else {
ame.Body = body;
replacement = ame;
}
var expectedType = objectCreateExpression.ResolveResult.Type.GetDefinition();
if (expectedType != null && expectedType.Kind != TypeKind.Delegate) {
var simplifiedDelegateCreation = (ObjectCreateExpression)objectCreateExpression.Expression.Clone();
simplifiedDelegateCreation.Arguments.Clear();
simplifiedDelegateCreation.Arguments.Add(replacement);
replacement = simplifiedDelegateCreation;
}
return replacement
.WithILInstruction(function)
.WithRR(objectCreateExpression.ResolveResult);
}
IEnumerable<ParameterDeclaration> MakeParameters(IMethod method, ILFunction function)
{
var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index);
int i = 0;
foreach (var parameter in method.Parameters) {
var pd = astBuilder.ConvertParameter(parameter);
if (parameter.Type.ContainsAnonymousType())
pd.Type = null;
ILVariable v;
if (variables.TryGetValue(i, out v))
pd.AddAnnotation(new ILVariableResolveResult(v, method.Parameters[i].Type));
yield return pd;
i++;
}
} }
TranslatedExpression TranslateTarget(IMember member, ILInstruction target, bool nonVirtualInvocation) TranslatedExpression TranslateTarget(IMember member, ILInstruction target, bool nonVirtualInvocation)
@ -800,7 +885,7 @@ namespace ICSharpCode.Decompiler.CSharp
// Used for Call, CallVirt and NewObj // Used for Call, CallVirt and NewObj
TranslatedExpression target; TranslatedExpression target;
if (inst.OpCode == OpCode.NewObj) { if (inst.OpCode == OpCode.NewObj) {
if (IsDelegateConstruction(inst)) { if (IL.Transforms.DelegateConstruction.IsDelegateConstruction((NewObj)inst, true)) {
return HandleDelegateConstruction(inst); return HandleDelegateConstruction(inst);
} }
target = default(TranslatedExpression); // no target target = default(TranslatedExpression); // no target

6
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -33,10 +33,10 @@ namespace ICSharpCode.Decompiler.CSharp
internal readonly ExpressionBuilder exprBuilder; internal readonly ExpressionBuilder exprBuilder;
readonly IMethod currentMethod; readonly IMethod currentMethod;
public StatementBuilder(ITypeResolveContext decompilationContext, IMethod currentMethod) public StatementBuilder(IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext, IMethod currentMethod)
{ {
Debug.Assert(decompilationContext != null && currentMethod != null); Debug.Assert(typeSystem != null && decompilationContext != null && currentMethod != null);
this.exprBuilder = new ExpressionBuilder(decompilationContext); this.exprBuilder = new ExpressionBuilder(typeSystem, decompilationContext);
this.currentMethod = currentMethod; this.currentMethod = currentMethod;
} }

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

@ -34,7 +34,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// For anonymous methods, creates an AnonymousMethodExpression. /// For anonymous methods, creates an AnonymousMethodExpression.
/// Also gets rid of any "Display Classes" left over after inlining an anonymous method. /// Also gets rid of any "Display Classes" left over after inlining an anonymous method.
/// </summary> /// </summary>
public class DelegateConstruction : ContextTrackingVisitor<object>, IAstTransform public class DelegateConstructionOld : ContextTrackingVisitor<object>, IAstTransform
{ {
internal sealed class Annotation internal sealed class Annotation
{ {

3
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -68,7 +68,6 @@
<Compile Include="CSharp\Transforms\ContextTrackingVisitor.cs" /> <Compile Include="CSharp\Transforms\ContextTrackingVisitor.cs" />
<Compile Include="CSharp\Transforms\DecimalConstantTransform.cs" /> <Compile Include="CSharp\Transforms\DecimalConstantTransform.cs" />
<Compile Include="CSharp\Transforms\DeclareVariables.cs" /> <Compile Include="CSharp\Transforms\DeclareVariables.cs" />
<Compile Include="CSharp\Transforms\DelegateConstruction.cs" />
<Compile Include="CSharp\Transforms\EscapeInvalidIdentifiers.cs" /> <Compile Include="CSharp\Transforms\EscapeInvalidIdentifiers.cs" />
<Compile Include="CSharp\Transforms\FixNameCollisions.cs" /> <Compile Include="CSharp\Transforms\FixNameCollisions.cs" />
<Compile Include="CSharp\Transforms\IntroduceUsingDeclarations.cs" /> <Compile Include="CSharp\Transforms\IntroduceUsingDeclarations.cs" />
@ -132,7 +131,7 @@
<Compile Include="IL\Transforms\ExpressionTransforms.cs" /> <Compile Include="IL\Transforms\ExpressionTransforms.cs" />
<Compile Include="IL\Transforms\InlineCompilerGeneratedVariables.cs" /> <Compile Include="IL\Transforms\InlineCompilerGeneratedVariables.cs" />
<Compile Include="IL\Transforms\LoopingTransform.cs" /> <Compile Include="IL\Transforms\LoopingTransform.cs" />
<Compile Include="IL\Transforms\RemoveCachedDelegateInitialization.cs" /> <Compile Include="IL\Transforms\DelegateConstruction.cs" />
<Compile Include="IL\Transforms\RemoveDeadVariableInit.cs" /> <Compile Include="IL\Transforms\RemoveDeadVariableInit.cs" />
<Compile Include="IL\Transforms\SplitVariables.cs" /> <Compile Include="IL\Transforms\SplitVariables.cs" />
<Compile Include="IL\Transforms\TransformArrayInitializers.cs" /> <Compile Include="IL\Transforms\TransformArrayInitializers.cs" />

19
ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs

@ -18,6 +18,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using ICSharpCode.NRefactory.TypeSystem;
using Mono.Cecil; using Mono.Cecil;
using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Disassembler;
@ -73,13 +75,28 @@ namespace ICSharpCode.Decompiler.IL
/// <summary> /// <summary>
/// Apply a list of transforms to this function. /// Apply a list of transforms to this function.
/// </summary> /// </summary>
public void RunTransforms(IEnumerable<IILTransform> transforms, ILTransformContext context) public void RunTransforms(IEnumerable<IILTransform> transforms, ILTransformContext context, Func<IILTransform, bool> stopTransform = null)
{ {
foreach (var transform in transforms) { foreach (var transform in transforms) {
context.CancellationToken.ThrowIfCancellationRequested(); context.CancellationToken.ThrowIfCancellationRequested();
if (stopTransform != null && stopTransform(transform))
break;
transform.Run(this, context); transform.Run(this, context);
this.CheckInvariant(ILPhase.Normal); this.CheckInvariant(ILPhase.Normal);
} }
} }
public static ILFunction Read(IDecompilerTypeSystem context, IMethod method, CancellationToken cancellationToken = default(CancellationToken))
{
return Read(context, (MethodDefinition)context.GetCecil(method), cancellationToken);
}
public static ILFunction Read(IDecompilerTypeSystem context, MethodDefinition methodDefinition, CancellationToken cancellationToken = default(CancellationToken))
{
var ilReader = new ILReader(context);
var function = ilReader.ReadIL(methodDefinition.Body, cancellationToken);
function.CheckInvariant(ILPhase.Normal);
return function;
}
} }
} }

181
ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs

@ -0,0 +1,181 @@
// 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.IL;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
{
public class DelegateConstruction : 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--) {
foreach (var call in block.Instructions[i].Descendants.OfType<NewObj>()) {
ILFunction f = TransformDelegateConstruction(call);
if (f != null)
call.Arguments[1].ReplaceWith(f);
}
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;
}
}
}
}
}
#region TransformDelegateConstruction
internal static bool IsDelegateConstruction(NewObj inst, bool allowTransformed = false)
{
if (inst == null || inst.Arguments.Count != 2 || inst.Method.DeclaringType.Kind != TypeKind.Delegate)
return false;
var opCode = inst.Arguments[1].OpCode;
return opCode == OpCode.LdFtn || opCode == OpCode.LdVirtFtn || (allowTransformed && opCode == OpCode.ILFunction);
}
static bool IsAnonymousMethod(ITypeDefinition decompiledTypeDefinition, IMethod method)
{
if (method == null || !(method.HasGeneratedName() || method.Name.Contains("$")))
return false;
if (!(method.IsCompilerGenerated() || IsPotentialClosure(decompiledTypeDefinition, method.DeclaringTypeDefinition)))
return false;
return true;
}
static bool IsPotentialClosure(ITypeDefinition decompiledTypeDefinition, ITypeDefinition potentialDisplayClass)
{
if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass())
return false;
// check that methodContainingType is within containingType
while (potentialDisplayClass != decompiledTypeDefinition) {
potentialDisplayClass = potentialDisplayClass.DeclaringTypeDefinition;
if (potentialDisplayClass == null)
return false;
}
return true;
}
ILFunction TransformDelegateConstruction(NewObj value)
{
if (!IsDelegateConstruction(value))
return null;
var targetMethod = ((IInstructionWithMethodOperand)value.Arguments[1]).Method;
if (IsAnonymousMethod(decompilationContext.CurrentTypeDefinition, targetMethod)) {
var target = value.Arguments[0];
var localTypeSystem = context.TypeSystem.GetSpecializingTypeSystem(decompilationContext);
var function = ILFunction.Read(localTypeSystem, targetMethod, context.CancellationToken);
var contextPrefix = targetMethod.Name;
foreach (ILVariable v in function.Variables.Where(v => v.Kind != VariableKind.Parameter)) {
v.Name = contextPrefix + v.Name;
}
function.RunTransforms(CSharpDecompiler.GetILTransforms(), context, t => t is DelegateConstruction);
function.AcceptVisitor(new ReplaceDelegateTargetVisitor(target, function.Variables.SingleOrDefault(v => v.Index == -1 && v.Kind == VariableKind.Parameter)));
((IILTransform)this).Run(function, new ILTransformContext { CancellationToken = context.CancellationToken, TypeSystem = localTypeSystem });
return function;
}
return null;
}
class ReplaceDelegateTargetVisitor : ILVisitor
{
readonly ILVariable thisVariable;
readonly ILInstruction target;
public ReplaceDelegateTargetVisitor(ILInstruction target, ILVariable thisVariable)
{
this.target = target;
this.thisVariable = thisVariable;
}
protected override void Default(ILInstruction inst)
{
foreach (var child in inst.Children) {
child.AcceptVisitor(this);
}
}
protected internal override void VisitLdLoc(LdLoc inst)
{
if (inst.MatchLdLoc(thisVariable)) {
inst.ReplaceWith(target.Clone());
return;
}
base.VisitLdLoc(inst);
}
}
#endregion
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 (!IsDelegateConstruction(value as NewObj, true))
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;
}
}
}

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

@ -1,95 +0,0 @@
// 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;
}
}
}

18
ICSharpCode.Decompiler/NRExtensions.cs

@ -16,12 +16,30 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic;
using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.Decompiler namespace ICSharpCode.Decompiler
{ {
public static class NRExtensions public static class NRExtensions
{ {
public static IDecompilerTypeSystem GetSpecializingTypeSystem(this IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext)
{
IList<IType> classTypeParameters = null;
IList<IType> methodTypeParameters = null;
if (decompilationContext.CurrentTypeDefinition != null)
classTypeParameters = decompilationContext.CurrentTypeDefinition.TypeArguments;
IMethod method = decompilationContext.CurrentMember as IMethod;
if (method != null)
methodTypeParameters = method.TypeArguments;
if ((classTypeParameters != null && classTypeParameters.Count > 0) || (methodTypeParameters != null && methodTypeParameters.Count > 0))
return new SpecializingDecompilerTypeSystem(typeSystem, new TypeParameterSubstitution(classTypeParameters, methodTypeParameters));
else
return typeSystem;
}
public static bool IsCompilerGenerated(this IEntity entity) public static bool IsCompilerGenerated(this IEntity entity)
{ {
if (entity != null) { if (entity != null) {

Loading…
Cancel
Save