Browse Source

Merge branch 'master' of git://github.com/icsharpcode/ILSpy into Debugger

pull/191/merge
Eusebiu Marcu 15 years ago
parent
commit
ef94d3888c
  1. 33
      ICSharpCode.Decompiler/Ast/AstBuilder.cs
  2. 9
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  3. 8
      ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs
  4. 4
      ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs
  5. 2
      ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
  6. 18
      ICSharpCode.Decompiler/CecilExtensions.cs
  7. 4
      ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs
  8. 92
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  9. 9
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  10. 113
      ICSharpCode.Decompiler/ILAst/ILInlining.cs
  11. 2
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  12. 25
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  13. 10
      ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs
  14. 2
      ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs
  15. 2
      ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributes.cs
  16. 1
      ICSharpCode.Decompiler/Tests/Types/TypeTests.cs

33
ICSharpCode.Decompiler/Ast/AstBuilder.cs

@ -123,7 +123,7 @@ namespace ICSharpCode.Decompiler.Ast
public void AddType(TypeDefinition typeDef) public void AddType(TypeDefinition typeDef)
{ {
TypeDeclaration astType = CreateType(typeDef); var astType = CreateType(typeDef);
NamespaceDeclaration astNS = GetCodeNamespace(typeDef.Namespace); NamespaceDeclaration astNS = GetCodeNamespace(typeDef.Namespace);
if (astNS != null) { if (astNS != null) {
astNS.AddChild(astType, NamespaceDeclaration.MemberRole); astNS.AddChild(astType, NamespaceDeclaration.MemberRole);
@ -153,7 +153,12 @@ namespace ICSharpCode.Decompiler.Ast
astCompileUnit.AddChild(CreateEvent(ev), CompilationUnit.MemberRole); astCompileUnit.AddChild(CreateEvent(ev), CompilationUnit.MemberRole);
} }
public TypeDeclaration CreateType(TypeDefinition typeDef) /// <summary>
/// Creates the AST for a type definition.
/// </summary>
/// <param name="typeDef"></param>
/// <returns>TypeDeclaration or DelegateDeclaration.</returns>
public AttributedNode CreateType(TypeDefinition typeDef)
{ {
// create CSharp code mappings - used for debugger // create CSharp code mappings - used for debugger
if (!CSharpCodeMapping.SourceCodeMappings.ContainsKey(typeDef.FullName)) { if (!CSharpCodeMapping.SourceCodeMappings.ContainsKey(typeDef.FullName)) {
@ -191,13 +196,13 @@ namespace ICSharpCode.Decompiler.Ast
astType.Constraints.AddRange(MakeConstraints(genericParameters)); astType.Constraints.AddRange(MakeConstraints(genericParameters));
// Nested types // Nested types
foreach(TypeDefinition nestedTypeDef in typeDef.NestedTypes) { foreach (TypeDefinition nestedTypeDef in typeDef.NestedTypes) {
if (MemberIsHidden(nestedTypeDef, context.Settings)) if (MemberIsHidden(nestedTypeDef, context.Settings))
continue; continue;
astType.AddChild(CreateType(nestedTypeDef), TypeDeclaration.MemberRole); astType.AddChild(CreateType(nestedTypeDef), TypeDeclaration.MemberRole);
} }
AttributedNode result = astType;
if (typeDef.IsEnum) { if (typeDef.IsEnum) {
long expectedEnumMemberValue = 0; long expectedEnumMemberValue = 0;
bool forcePrintingInitializers = IsFlagsEnum(typeDef); bool forcePrintingInitializers = IsFlagsEnum(typeDef);
@ -218,6 +223,21 @@ namespace ICSharpCode.Decompiler.Ast
astType.AddChild(enumMember, TypeDeclaration.MemberRole); astType.AddChild(enumMember, TypeDeclaration.MemberRole);
} }
} }
} else if (typeDef.BaseType != null && typeDef.BaseType.FullName == "System.MulticastDelegate") {
DelegateDeclaration dd = new DelegateDeclaration();
dd.Modifiers = astType.Modifiers & ~Modifiers.Sealed;
dd.Name = astType.Name;
dd.AddAnnotation(typeDef);
astType.Attributes.MoveTo(dd.Attributes);
astType.TypeParameters.MoveTo(dd.TypeParameters);
astType.Constraints.MoveTo(dd.Constraints);
foreach (var m in typeDef.Methods) {
if (m.Name == "Invoke") {
dd.ReturnType = ConvertType(m.ReturnType, m.MethodReturnType);
dd.Parameters.AddRange(MakeParameters(m.Parameters));
}
}
result = dd;
} else { } else {
// Base type // Base type
if (typeDef.BaseType != null && !typeDef.IsValueType && typeDef.BaseType.FullName != "System.Object") { if (typeDef.BaseType != null && !typeDef.IsValueType && typeDef.BaseType.FullName != "System.Object") {
@ -231,7 +251,7 @@ namespace ICSharpCode.Decompiler.Ast
} }
context.CurrentType = oldCurrentType; context.CurrentType = oldCurrentType;
return astType; return result;
} }
public void Transform(IAstTransform transform) public void Transform(IAstTransform transform)
@ -736,6 +756,7 @@ namespace ICSharpCode.Decompiler.Ast
CustomEventDeclaration CreateEvent(EventDefinition eventDef) CustomEventDeclaration CreateEvent(EventDefinition eventDef)
{ {
CustomEventDeclaration astEvent = new CustomEventDeclaration(); CustomEventDeclaration astEvent = new CustomEventDeclaration();
ConvertCustomAttributes(astEvent, eventDef);
astEvent.AddAnnotation(eventDef); astEvent.AddAnnotation(eventDef);
astEvent.Name = CleanName(eventDef.Name); astEvent.Name = CleanName(eventDef.Name);
astEvent.ReturnType = ConvertType(eventDef.EventType, eventDef); astEvent.ReturnType = ConvertType(eventDef.EventType, eventDef);
@ -1148,7 +1169,7 @@ namespace ICSharpCode.Decompiler.Ast
} }
} }
TypeCode code = TypeAnalysis.GetTypeCode(type); TypeCode code = TypeAnalysis.GetTypeCode(type);
if (code == TypeCode.Object) if (code == TypeCode.Object || code == TypeCode.Empty)
return new Ast.PrimitiveExpression((int)val); return new Ast.PrimitiveExpression((int)val);
else else
return new Ast.PrimitiveExpression(CSharpPrimitiveCast.Cast(code, val, false)); return new Ast.PrimitiveExpression(CSharpPrimitiveCast.Cast(code, val, false));

9
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -22,6 +22,7 @@ namespace ICSharpCode.Decompiler.Ast
TypeSystem typeSystem; TypeSystem typeSystem;
DecompilerContext context; DecompilerContext context;
HashSet<ILVariable> localVariablesToDefine = new HashSet<ILVariable>(); // local variables that are missing a definition HashSet<ILVariable> localVariablesToDefine = new HashSet<ILVariable>(); // local variables that are missing a definition
HashSet<ILVariable> implicitlyDefinedVariables = new HashSet<ILVariable>(); // local variables that are implicitly defined (e.g. catch handler)
public static BlockStatement CreateMethodBody(MethodDefinition methodDef, DecompilerContext context) public static BlockStatement CreateMethodBody(MethodDefinition methodDef, DecompilerContext context)
{ {
@ -69,7 +70,7 @@ namespace ICSharpCode.Decompiler.Ast
context.CancellationToken.ThrowIfCancellationRequested(); context.CancellationToken.ThrowIfCancellationRequested();
Ast.BlockStatement astBlock = TransformBlock(ilMethod); Ast.BlockStatement astBlock = TransformBlock(ilMethod);
CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments
foreach (ILVariable v in localVariablesToDefine) { foreach (ILVariable v in localVariablesToDefine.Except(implicitlyDefinedVariables)) {
DeclareVariableInSmallestScope.DeclareVariable(astBlock, AstBuilder.ConvertType(v.Type), v.Name); DeclareVariableInSmallestScope.DeclareVariable(astBlock, AstBuilder.ConvertType(v.Type), v.Name);
} }
@ -143,6 +144,8 @@ namespace ICSharpCode.Decompiler.Ast
var tryCatchStmt = new Ast.TryCatchStatement(); var tryCatchStmt = new Ast.TryCatchStatement();
tryCatchStmt.TryBlock = TransformBlock(tryCatchNode.TryBlock); tryCatchStmt.TryBlock = TransformBlock(tryCatchNode.TryBlock);
foreach (var catchClause in tryCatchNode.CatchBlocks) { foreach (var catchClause in tryCatchNode.CatchBlocks) {
if (catchClause.ExceptionVariable != null)
implicitlyDefinedVariables.Add(catchClause.ExceptionVariable);
tryCatchStmt.CatchClauses.Add( tryCatchStmt.CatchClauses.Add(
new Ast.CatchClause { new Ast.CatchClause {
Type = AstBuilder.ConvertType(catchClause.ExceptionType), Type = AstBuilder.ConvertType(catchClause.ExceptionType),
@ -610,7 +613,7 @@ namespace ICSharpCode.Decompiler.Ast
}; };
} }
} }
} else if (cecilMethodDef.Name == "Invoke" && cecilMethodDef.DeclaringType.BaseType.FullName == "System.MulticastDelegate") { } else if (cecilMethodDef.Name == "Invoke" && cecilMethodDef.DeclaringType.BaseType != null && cecilMethodDef.DeclaringType.BaseType.FullName == "System.MulticastDelegate") {
AdjustArgumentsForMethodCall(cecilMethod, methodArgs); AdjustArgumentsForMethodCall(cecilMethod, methodArgs);
return target.Invoke(methodArgs); return target.Invoke(methodArgs);
} }
@ -754,6 +757,8 @@ namespace ICSharpCode.Decompiler.Ast
} }
if (actualIsIntegerOrEnum && requiredIsIntegerOrEnum) { if (actualIsIntegerOrEnum && requiredIsIntegerOrEnum) {
if (actualType.FullName == reqType.FullName)
return expr;
return expr.CastTo(AstBuilder.ConvertType(reqType)); return expr.CastTo(AstBuilder.ConvertType(reqType));
} }
return expr; return expr;

8
ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs

@ -47,13 +47,13 @@ namespace ICSharpCode.Decompiler.Ast
static AstNode FindInsertPos(AstNode node, string name, bool allowPassIntoLoops) static AstNode FindInsertPos(AstNode node, string name, bool allowPassIntoLoops)
{ {
IdentifierExpression ident = node as IdentifierExpression;
if (ident != null && ident.Identifier == name && ident.TypeArguments.Count == 0)
return node;
AstNode pos = null; AstNode pos = null;
AstNode withinPos = null; AstNode withinPos = null;
while (node != null) { while (node != null) {
IdentifierExpression ident = node as IdentifierExpression;
if (ident != null && ident.Identifier == name && ident.TypeArguments.Count == 0)
return node;
AstNode withinCurrent = FindInsertPos(node.FirstChild, name, allowPassIntoLoops); AstNode withinCurrent = FindInsertPos(node.FirstChild, name, allowPassIntoLoops);
if (withinCurrent != null) { if (withinCurrent != null) {
if (pos == null) { if (pos == null) {

4
ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs

@ -62,7 +62,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
if (m == null) if (m == null)
break; break;
FieldDefinition fieldDef = m.Get("fieldAccess").Single().Annotation<FieldDefinition>(); FieldDefinition fieldDef = m.Get("fieldAccess").Single().Annotation<FieldReference>().ResolveWithinSameModule();
if (fieldDef == null) if (fieldDef == null)
break; break;
AttributedNode fieldOrEventDecl = typeDeclaration.Members.FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef); AttributedNode fieldOrEventDecl = typeDeclaration.Members.FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
@ -106,7 +106,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
AssignmentExpression assignment = es.Expression as AssignmentExpression; AssignmentExpression assignment = es.Expression as AssignmentExpression;
if (assignment == null || assignment.Operator != AssignmentOperatorType.Assign) if (assignment == null || assignment.Operator != AssignmentOperatorType.Assign)
break; break;
FieldDefinition fieldDef = assignment.Left.Annotation<FieldDefinition>(); FieldDefinition fieldDef = assignment.Left.Annotation<FieldReference>().ResolveWithinSameModule();
if (fieldDef == null || !fieldDef.IsStatic) if (fieldDef == null || !fieldDef.IsStatic)
break; break;
FieldDeclaration fieldDecl = typeDeclaration.Members.OfType<FieldDeclaration>().FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef); FieldDeclaration fieldDecl = typeDeclaration.Members.OfType<FieldDeclaration>().FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);

2
ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs

@ -334,7 +334,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
continue; continue;
Match m = automaticPropertyPattern.Match(property); Match m = automaticPropertyPattern.Match(property);
if (m != null) { if (m != null) {
FieldDefinition field = m.Get("fieldReference").Single().Annotation<FieldDefinition>(); FieldDefinition field = m.Get("fieldReference").Single().Annotation<FieldReference>().ResolveWithinSameModule();
if (field.IsCompilerGenerated()) { if (field.IsCompilerGenerated()) {
RemoveCompilerGeneratedAttribute(property.Getter.Attributes); RemoveCompilerGeneratedAttribute(property.Getter.Attributes);
RemoveCompilerGeneratedAttribute(property.Setter.Attributes); RemoveCompilerGeneratedAttribute(property.Setter.Attributes);

18
ICSharpCode.Decompiler/CecilExtensions.cs

@ -159,9 +159,25 @@ namespace ICSharpCode.Decompiler
return accessorMethods; return accessorMethods;
} }
public static FieldDefinition ResolveWithinSameModule(this FieldReference field)
{
if (field != null && field.DeclaringType.GetElementType().Module == field.Module)
return field.Resolve();
else
return null;
}
public static MethodDefinition ResolveWithinSameModule(this MethodReference method)
{
if (method != null && method.DeclaringType.GetElementType().Module == method.Module)
return method.Resolve();
else
return null;
}
public static bool IsCompilerGenerated(this ICustomAttributeProvider provider) public static bool IsCompilerGenerated(this ICustomAttributeProvider provider)
{ {
if (provider.HasCustomAttributes) { if (provider != null && provider.HasCustomAttributes) {
foreach (CustomAttribute a in provider.CustomAttributes) { foreach (CustomAttribute a in provider.CustomAttributes) {
if (a.AttributeType.FullName == "System.Runtime.CompilerServices.CompilerGeneratedAttribute") if (a.AttributeType.FullName == "System.Runtime.CompilerServices.CompilerGeneratedAttribute")
return true; return true;

4
ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs

@ -33,7 +33,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (arg1.Match(block.Body.ElementAtOrDefault(i + 1)) && arg2.Match(block.Body.ElementAtOrDefault(i + 2))) { if (arg1.Match(block.Body.ElementAtOrDefault(i + 1)) && arg2.Match(block.Body.ElementAtOrDefault(i + 2))) {
if (initializeArrayPattern.Match(block.Body.ElementAtOrDefault(i + 3))) { if (initializeArrayPattern.Match(block.Body.ElementAtOrDefault(i + 3))) {
if (HandleStaticallyInitializedArray(arg2, block, i, newArrInst, arrayLength)) { if (HandleStaticallyInitializedArray(arg2, block, i, newArrInst, arrayLength)) {
i -= ILInlining.InlineInto(block, i + 1, method) - 1; i -= new ILInlining(method).InlineInto(block, i + 1) - 1;
} }
return; return;
} }
@ -55,7 +55,7 @@ namespace ICSharpCode.Decompiler.ILAst
((ILExpression)block.Body[i]).Arguments[0] = new ILExpression( ((ILExpression)block.Body[i]).Arguments[0] = new ILExpression(
ILCode.InitArray, newArrInst.Operand, operands.ToArray()); ILCode.InitArray, newArrInst.Operand, operands.ToArray());
block.Body.RemoveRange(i + 1, arrayLength); block.Body.RemoveRange(i + 1, arrayLength);
i -= ILInlining.InlineInto(block, i + 1, method) - 1; i -= new ILInlining(method).InlineInto(block, i + 1) - 1;
} }
}; };
} }

92
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -23,8 +23,8 @@ namespace ICSharpCode.Decompiler.ILAst
GotoRemoval, GotoRemoval,
DuplicateReturns, DuplicateReturns,
FlattenIfStatements, FlattenIfStatements,
PeepholeTransforms,
InlineVariables2, InlineVariables2,
PeepholeTransforms,
TypeInference, TypeInference,
None None
} }
@ -42,7 +42,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables) return; if (abortBeforeStep == ILAstOptimizationStep.InlineVariables) return;
// Works better after simple goto removal because of the following debug pattern: stloc X; br Next; Next:; ldloc X // Works better after simple goto removal because of the following debug pattern: stloc X; br Next; Next:; ldloc X
InlineVariables(method); ILInlining.InlineAllVariables(method);
if (abortBeforeStep == ILAstOptimizationStep.ReduceBranchInstructionSet) return; if (abortBeforeStep == ILAstOptimizationStep.ReduceBranchInstructionSet) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) { foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
@ -97,12 +97,12 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.FlattenIfStatements) return; if (abortBeforeStep == ILAstOptimizationStep.FlattenIfStatements) return;
FlattenIfStatements(method); FlattenIfStatements(method);
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return;
ILInlining.InlineAllVariables(method);
if (abortBeforeStep == ILAstOptimizationStep.PeepholeTransforms) return; if (abortBeforeStep == ILAstOptimizationStep.PeepholeTransforms) return;
PeepholeTransforms.Run(context, method); PeepholeTransforms.Run(context, method);
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return;
InlineVariables(method);
if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return; if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return;
TypeAnalysis.Run(context, method); TypeAnalysis.Run(context, method);
@ -135,88 +135,6 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} }
void InlineVariables(ILBlock method)
{
// Analyse the whole method
Dictionary<ILVariable, int> numStloc = new Dictionary<ILVariable, int>();
Dictionary<ILVariable, int> numLdloc = new Dictionary<ILVariable, int>();
Dictionary<ILVariable, int> numLdloca = new Dictionary<ILVariable, int>();
foreach(ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
ILVariable locVar = expr.Operand as ILVariable;
if (locVar != null) {
if (expr.Code == ILCode.Stloc) {
numStloc[locVar] = numStloc.GetOrDefault(locVar) + 1;
} else if (expr.Code == ILCode.Ldloc) {
numLdloc[locVar] = numLdloc.GetOrDefault(locVar) + 1;
} else if (expr.Code == ILCode.Ldloca) {
numLdloca[locVar] = numLdloca.GetOrDefault(locVar) + 1;
} else {
throw new NotSupportedException(expr.Code.ToString());
}
}
}
// Inline all blocks
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
List<ILNode> body = block.Body;
for(int i = 0; i < body.Count - 1;) {
ILExpression nextExpr = body[i + 1] as ILExpression;
ILVariable locVar;
ILExpression expr;
ILExpression ldParent;
int ldPos;
if (body[i].Match(ILCode.Stloc, out locVar, out expr) &&
numStloc.GetOrDefault(locVar) == 1 &&
numLdloc.GetOrDefault(locVar) == 1 &&
numLdloca.GetOrDefault(locVar) == 0 &&
nextExpr != null &&
FindLdloc(nextExpr, locVar, out ldParent, out ldPos) == true &&
ldParent != null)
{
// Assign the ranges of the optimized instrustions
expr.ILRanges.AddRange(((ILExpression)body[i]).ILRanges);
expr.ILRanges.AddRange(ldParent.Arguments[ldPos].ILRanges);
// We are moving the expression evaluation past the other aguments.
// It is ok to pass ldloc because the expression can not contain stloc and thus the ldloc will still return the same value
body.RemoveAt(i);
ldParent.Arguments[ldPos] = expr; // Inline the stloc body
i = Math.Max(0, i - 1); // Go back one step
} else {
i++;
}
}
}
}
/// <summary>
/// Finds the position to inline to.
/// </summary>
/// <returns>true = found; false = cannot continue search; null = not found</returns>
static bool? FindLdloc(ILExpression expr, ILVariable v, out ILExpression parent, out int pos)
{
parent = null;
pos = 0;
for (int i = 0; i < expr.Arguments.Count; i++) {
// Stop when seeing an opcode that does not guarantee that its operands will be evaluated
// Inlining in that case migth result in the inlined expresion not being evaluted
if (i == 1 && (expr.Code == ILCode.LogicAnd || expr.Code == ILCode.LogicOr || expr.Code == ILCode.TernaryOp))
return false;
ILExpression arg = expr.Arguments[i];
if (arg.Code == ILCode.Ldloc && arg.Operand == v) {
parent = expr;
pos = i;
return true;
}
bool? r = FindLdloc(arg, v, out parent, out pos);
if (r != null)
return r;
}
return expr.Code == ILCode.Ldloc ? (bool?)null : false;
}
/// <summary> /// <summary>
/// Reduces the branch codes to just br and brtrue. /// Reduces the branch codes to just br and brtrue.
/// Moves ILRanges to the branch argument /// Moves ILRanges to the branch argument

9
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -465,9 +465,12 @@ namespace ICSharpCode.Decompiler.ILAst
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
Debug.Assert(Values.Count > 0); if (this.Values != null) {
foreach (int i in this.Values) { foreach (int i in this.Values) {
output.WriteLine("case {0}:", i); output.WriteLine("case {0}:", i);
}
} else {
output.WriteLine("default:");
} }
output.Indent(); output.Indent();
base.WriteTo(output); base.WriteTo(output);

113
ICSharpCode.Decompiler/ILAst/ILInlining.cs

@ -2,6 +2,7 @@
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) // This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
@ -12,18 +13,70 @@ namespace ICSharpCode.Decompiler.ILAst
/// </summary> /// </summary>
public class ILInlining public class ILInlining
{ {
public static void InlineAllVariables(ILBlock method)
{
ILInlining i = new ILInlining(method);
foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>())
i.InlineAllInBlock(block);
}
Dictionary<ILVariable, int> numStloc = new Dictionary<ILVariable, int>();
Dictionary<ILVariable, int> numLdloc = new Dictionary<ILVariable, int>();
Dictionary<ILVariable, int> numLdloca = new Dictionary<ILVariable, int>();
public ILInlining(ILBlock method)
{
// Analyse the whole method
foreach(ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
ILVariable locVar = expr.Operand as ILVariable;
if (locVar != null) {
if (expr.Code == ILCode.Stloc) {
numStloc[locVar] = numStloc.GetOrDefault(locVar) + 1;
} else if (expr.Code == ILCode.Ldloc) {
numLdloc[locVar] = numLdloc.GetOrDefault(locVar) + 1;
} else if (expr.Code == ILCode.Ldloca) {
numLdloca[locVar] = numLdloca.GetOrDefault(locVar) + 1;
} else {
throw new NotSupportedException(expr.Code.ToString());
}
}
}
}
public void InlineAllInBlock(ILBlock block)
{
List<ILNode> body = block.Body;
for(int i = 0; i < body.Count - 1;) {
ILExpression nextExpr = body[i + 1] as ILExpression;
ILVariable locVar;
ILExpression expr;
ILExpression ldParent;
int ldPos;
if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineIfPossible(block, i)) {
// We are moving the expression evaluation past the other aguments.
// It is ok to pass ldloc because the expression can not contain stloc and thus the ldloc will still return the same value
i = Math.Max(0, i - 1); // Go back one step
} else {
i++;
}
}
}
/// <summary> /// <summary>
/// Inlines instructions before pos into block.Body[pos]. /// Inlines instructions before pos into block.Body[pos].
/// </summary> /// </summary>
/// <returns>The number of instructions that were inlined.</returns> /// <returns>The number of instructions that were inlined.</returns>
public static int InlineInto(ILBlock block, int pos, ILBlock method) public int InlineInto(ILBlock block, int pos)
{ {
int count = 0; int count = 0;
while (--pos >= 0) { while (--pos >= 0) {
ILExpression expr = block.Body[pos] as ILExpression; ILExpression expr = block.Body[pos] as ILExpression;
if (expr == null || expr.Code != ILCode.Stloc) if (expr == null || expr.Code != ILCode.Stloc)
break; break;
if (InlineIfPossible(block, pos, method)) if (InlineIfPossible(block, pos))
count++; count++;
else else
break; break;
@ -34,9 +87,16 @@ namespace ICSharpCode.Decompiler.ILAst
/// <summary> /// <summary>
/// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible. /// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible.
/// </summary> /// </summary>
public static bool InlineIfPossible(ILBlock block, int pos, ILBlock method) public bool InlineIfPossible(ILBlock block, int pos)
{ {
if (InlineIfPossible((ILExpression)block.Body[pos], block.Body.ElementAtOrDefault(pos+1), method)) { ILVariable v;
ILExpression inlinedExpression;
if (block.Body[pos].Match(ILCode.Stloc, out v, out inlinedExpression)
&& InlineIfPossible(v, inlinedExpression, block.Body.ElementAtOrDefault(pos+1)))
{
// Assign the ranges of the stloc instruction:
inlinedExpression.ILRanges.AddRange(((ILExpression)block.Body[pos]).ILRanges);
// Remove the stloc instruction:
block.Body.RemoveAt(pos); block.Body.RemoveAt(pos);
return true; return true;
} }
@ -46,17 +106,24 @@ namespace ICSharpCode.Decompiler.ILAst
/// <summary> /// <summary>
/// Inlines 'expr' into 'next', if possible. /// Inlines 'expr' into 'next', if possible.
/// </summary> /// </summary>
public static bool InlineIfPossible(ILExpression expr, ILNode next, ILBlock method) bool InlineIfPossible(ILVariable v, ILExpression inlinedExpression, ILNode next)
{ {
if (expr.Code != ILCode.Stloc)
throw new ArgumentException("expr must be stloc");
// ensure the variable is accessed only a single time // ensure the variable is accessed only a single time
if (method.GetSelfAndChildrenRecursive<ILExpression>().Count(e => e != expr && e.Operand == expr.Operand) != 1) if (!(numStloc.GetOrDefault(v) == 1 && numLdloc.GetOrDefault(v) == 1 && numLdloca.GetOrDefault(v) == 0))
return false; return false;
HashSet<ILVariable> forbiddenVariables = new HashSet<ILVariable>();
foreach (ILExpression potentialStore in inlinedExpression.GetSelfAndChildrenRecursive<ILExpression>()) {
if (potentialStore.Code == ILCode.Stloc)
forbiddenVariables.Add((ILVariable)potentialStore.Operand);
}
ILExpression parent; ILExpression parent;
int pos; int pos;
if (FindLoadInNext(next as ILExpression, (ILVariable)expr.Operand, out parent, out pos) == true) { if (FindLoadInNext(next as ILExpression, v, forbiddenVariables, out parent, out pos) == true) {
parent.Arguments[pos] = expr.Arguments[0]; // Assign the ranges of the ldloc instruction:
inlinedExpression.ILRanges.AddRange(parent.Arguments[pos].ILRanges);
parent.Arguments[pos] = inlinedExpression;
return true; return true;
} }
return false; return false;
@ -66,29 +133,39 @@ namespace ICSharpCode.Decompiler.ILAst
/// Finds the position to inline to. /// Finds the position to inline to.
/// </summary> /// </summary>
/// <returns>true = found; false = cannot continue search; null = not found</returns> /// <returns>true = found; false = cannot continue search; null = not found</returns>
static bool? FindLoadInNext(ILExpression expr, ILVariable v, out ILExpression parent, out int pos) bool? FindLoadInNext(ILExpression expr, ILVariable v, HashSet<ILVariable> forbiddenVariables, out ILExpression parent, out int pos)
{ {
parent = null; parent = null;
pos = 0; pos = 0;
if (expr == null) if (expr == null)
return false; return false;
for (int i = 0; i < expr.Arguments.Count; i++) { for (int i = 0; i < expr.Arguments.Count; i++) {
// Stop when seeing an opcode that does not guarantee that its operands will be evaluated.
// Inlining in that case might result in the inlined expresion not being evaluted.
if (i == 1 && (expr.Code == ILCode.LogicAnd || expr.Code == ILCode.LogicOr || expr.Code == ILCode.TernaryOp))
return false;
ILExpression arg = expr.Arguments[i]; ILExpression arg = expr.Arguments[i];
if (arg.Code == ILCode.Ldloc && arg.Operand == v) { if (arg.Code == ILCode.Ldloc && arg.Operand == v) {
parent = expr; parent = expr;
pos = i; pos = i;
return true; return true;
} }
bool? r = FindLoadInNext(arg, v, out parent, out pos); bool? r = FindLoadInNext(arg, v, forbiddenVariables, out parent, out pos);
if (r != null) if (r != null)
return r; return r;
} }
return IsWithoutSideEffects(expr.Code) ? (bool?)null : false; if (expr.Code == ILCode.Ldloc) {
} ILVariable loadedVar = (ILVariable)expr.Operand;
if (!forbiddenVariables.Contains(loadedVar) && numLdloca.GetOrDefault(loadedVar) == 0) {
static bool IsWithoutSideEffects(ILCode code) // the expression is loading a non-forbidden variable:
{ // we're allowed to continue searching
return code == ILCode.Ldloc; return null;
}
}
// otherwise: abort, inlining is not possible
return false;
} }
} }
} }

2
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -163,7 +163,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (parent.Arguments[j].Code == ILCode.Ldsfld && parent.Arguments[j].Operand == field) { if (parent.Arguments[j].Code == ILCode.Ldsfld && parent.Arguments[j].Operand == field) {
parent.Arguments[j] = newObj; parent.Arguments[j] = newObj;
block.Body.RemoveAt(i); block.Body.RemoveAt(i);
i -= ILInlining.InlineInto(block, i, method); i -= new ILInlining(method).InlineInto(block, i);
return; return;
} }
} }

25
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -28,6 +28,10 @@ namespace ICSharpCode.Decompiler.ILAst
ta.method = method; ta.method = method;
ta.InferTypes(method); ta.InferTypes(method);
ta.InferRemainingStores(); ta.InferRemainingStores();
// Now that stores were inferred, we can infer the remaining instructions that depended on those stored
// (but which didn't provide an expected type for the store)
// For example, this is necessary to make a switch() over a generated variable work correctly.
ta.InferTypes(method);
} }
DecompilerContext context; DecompilerContext context;
@ -257,7 +261,15 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Initobj: case ILCode.Initobj:
return null; return null;
case ILCode.Localloc: case ILCode.Localloc:
return typeSystem.IntPtr; if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], typeSystem.Int32);
}
if (expectedType is PointerType)
return expectedType;
else
return typeSystem.IntPtr;
case ILCode.Sizeof:
return typeSystem.Int32;
#endregion #endregion
#region Arithmetic instructions #region Arithmetic instructions
case ILCode.Not: // bitwise complement case ILCode.Not: // bitwise complement
@ -348,7 +360,6 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType; ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType;
if (forceInferChildren) { if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], new ArrayType(typeSystem.Byte));
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
} }
return arrayType != null ? arrayType.ElementType : null; return arrayType != null ? arrayType.ElementType : null;
@ -386,33 +397,43 @@ namespace ICSharpCode.Decompiler.ILAst
#region Conversion instructions #region Conversion instructions
case ILCode.Conv_I1: case ILCode.Conv_I1:
case ILCode.Conv_Ovf_I1: case ILCode.Conv_Ovf_I1:
case ILCode.Conv_Ovf_I1_Un:
return (GetInformationAmount(expectedType) == 8 && IsSigned(expectedType) == true) ? expectedType : typeSystem.SByte; return (GetInformationAmount(expectedType) == 8 && IsSigned(expectedType) == true) ? expectedType : typeSystem.SByte;
case ILCode.Conv_I2: case ILCode.Conv_I2:
case ILCode.Conv_Ovf_I2: case ILCode.Conv_Ovf_I2:
case ILCode.Conv_Ovf_I2_Un:
return (GetInformationAmount(expectedType) == 16 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int16; return (GetInformationAmount(expectedType) == 16 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int16;
case ILCode.Conv_I4: case ILCode.Conv_I4:
case ILCode.Conv_Ovf_I4: case ILCode.Conv_Ovf_I4:
case ILCode.Conv_Ovf_I4_Un:
return (GetInformationAmount(expectedType) == 32 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int32; return (GetInformationAmount(expectedType) == 32 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int32;
case ILCode.Conv_I8: case ILCode.Conv_I8:
case ILCode.Conv_Ovf_I8: case ILCode.Conv_Ovf_I8:
case ILCode.Conv_Ovf_I8_Un:
return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int64; return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int64;
case ILCode.Conv_U1: case ILCode.Conv_U1:
case ILCode.Conv_Ovf_U1: case ILCode.Conv_Ovf_U1:
case ILCode.Conv_Ovf_U1_Un:
return (GetInformationAmount(expectedType) == 8 && IsSigned(expectedType) == false) ? expectedType : typeSystem.Byte; return (GetInformationAmount(expectedType) == 8 && IsSigned(expectedType) == false) ? expectedType : typeSystem.Byte;
case ILCode.Conv_U2: case ILCode.Conv_U2:
case ILCode.Conv_Ovf_U2: case ILCode.Conv_Ovf_U2:
case ILCode.Conv_Ovf_U2_Un:
return (GetInformationAmount(expectedType) == 16 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt16; return (GetInformationAmount(expectedType) == 16 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt16;
case ILCode.Conv_U4: case ILCode.Conv_U4:
case ILCode.Conv_Ovf_U4: case ILCode.Conv_Ovf_U4:
case ILCode.Conv_Ovf_U4_Un:
return (GetInformationAmount(expectedType) == 32 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt32; return (GetInformationAmount(expectedType) == 32 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt32;
case ILCode.Conv_U8: case ILCode.Conv_U8:
case ILCode.Conv_Ovf_U8: case ILCode.Conv_Ovf_U8:
case ILCode.Conv_Ovf_U8_Un:
return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt64; return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt64;
case ILCode.Conv_I: case ILCode.Conv_I:
case ILCode.Conv_Ovf_I: case ILCode.Conv_Ovf_I:
case ILCode.Conv_Ovf_I_Un:
return (GetInformationAmount(expectedType) == nativeInt && IsSigned(expectedType) == true) ? expectedType : typeSystem.IntPtr; return (GetInformationAmount(expectedType) == nativeInt && IsSigned(expectedType) == true) ? expectedType : typeSystem.IntPtr;
case ILCode.Conv_U: case ILCode.Conv_U:
case ILCode.Conv_Ovf_U: case ILCode.Conv_Ovf_U:
case ILCode.Conv_Ovf_U_Un:
return (GetInformationAmount(expectedType) == nativeInt && IsSigned(expectedType) == false) ? expectedType : typeSystem.UIntPtr; return (GetInformationAmount(expectedType) == nativeInt && IsSigned(expectedType) == false) ? expectedType : typeSystem.UIntPtr;
case ILCode.Conv_R4: case ILCode.Conv_R4:
return typeSystem.Single; return typeSystem.Single;

10
ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs

@ -130,18 +130,12 @@ namespace ICSharpCode.Decompiler.ILAst
static FieldDefinition GetFieldDefinition(FieldReference field) static FieldDefinition GetFieldDefinition(FieldReference field)
{ {
if (field != null && field.DeclaringType.IsGenericInstance) return CecilExtensions.ResolveWithinSameModule(field);
return field.Resolve();
else
return field as FieldDefinition;
} }
static MethodDefinition GetMethodDefinition(MethodReference method) static MethodDefinition GetMethodDefinition(MethodReference method)
{ {
if (method != null && method.DeclaringType.IsGenericInstance) return CecilExtensions.ResolveWithinSameModule(method);
return method.Resolve();
else
return method as MethodDefinition;
} }
bool MatchEnumeratorCreationNewObj(ILExpression expr, out MethodDefinition ctor) bool MatchEnumeratorCreationNewObj(ILExpression expr, out MethodDefinition ctor)

2
ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs

@ -46,7 +46,7 @@ namespace AttributeWithTypeArgument
[AttributeUsage(AttributeTargets.All)] [AttributeUsage(AttributeTargets.All)]
public class MyTypeAttribute : Attribute public class MyTypeAttribute : Attribute
{ {
public MyTypeAttribute(Type t) : base() public MyTypeAttribute(Type t)
{ {
} }
} }

2
ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributes.cs

@ -20,7 +20,7 @@ namespace aa
[AttributeUsage(AttributeTargets.All)] [AttributeUsage(AttributeTargets.All)]
public class MyAttribute : Attribute public class MyAttribute : Attribute
{ {
public MyAttribute(CustomAtributes.EnumWithFlag en) : base() public MyAttribute(CustomAtributes.EnumWithFlag en)
{ {
} }
} }

1
ICSharpCode.Decompiler/Tests/Types/TypeTests.cs

@ -6,6 +6,7 @@ using NUnit.Framework;
namespace ICSharpCode.Decompiler.Tests.Types namespace ICSharpCode.Decompiler.Tests.Types
{ {
[TestFixture]
public class TypeTests : DecompilerTestBase public class TypeTests : DecompilerTestBase
{ {
[Test] [Test]

Loading…
Cancel
Save