Browse Source

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

pull/191/merge
Eusebiu Marcu 15 years ago
parent
commit
42000d4d77
  1. 91
      ICSharpCode.Decompiler/Ast/AstBuilder.cs
  2. 2
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  3. 148
      ICSharpCode.Decompiler/Ast/CecilTypeResolveContext.cs
  4. 32
      ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs
  5. 4
      ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
  6. 87
      ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs
  7. 594
      ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
  8. 1
      ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs
  9. 3
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  10. 5
      ICSharpCode.Decompiler/ILAst/GotoRemoval.cs
  11. 7
      ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs
  12. 364
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  13. 28
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  14. 40
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  15. 2
      ICSharpCode.Decompiler/ILAst/ILInlining.cs
  16. 2
      ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs
  17. 40
      ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs
  18. 37
      ICSharpCode.Decompiler/ILAst/PatternMatching.cs
  19. 7
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  20. 259
      ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs
  21. 5
      ICSharpCode.Decompiler/Tests/UnsafeCode.cs
  22. 54
      ILSpy/CSharpLanguage.cs
  23. 10
      ILSpy/DecompilationOptions.cs
  24. 8
      ILSpy/ILSpy.csproj
  25. 45
      ILSpy/MainWindow.xaml.cs
  26. 18
      ILSpy/NavigationHistory.cs
  27. 43
      ILSpy/TextView/DecompilerTextView.cs
  28. 2
      ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs
  29. 203
      ILSpy/TreeNodes/Analyzer/AnalyzerMethodOverridesTreeNode.cs
  30. 2
      ILSpy/TreeNodes/BaseTypesTreeNode.cs
  31. 15
      ILSpy/TreeNodes/DerivedTypesTreeNode.cs
  32. 2
      ILSpy/TreeNodes/TypeTreeNode.cs
  33. 26
      NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs
  34. 28
      NRefactory/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs
  35. 101
      NRefactory/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs
  36. 29
      NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs
  37. 25
      NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs
  38. 1
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs
  39. 1
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs
  40. 4
      NRefactory/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs
  41. 2
      NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs
  42. 12
      NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs
  43. 15
      NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs
  44. 258
      NRefactory/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs
  45. 5
      NRefactory/ICSharpCode.NRefactory/TypeSystem/IAccessor.cs
  46. 6
      NRefactory/ICSharpCode.NRefactory/TypeSystem/IAttribute.cs
  47. 2
      NRefactory/ICSharpCode.NRefactory/TypeSystem/IMethod.cs
  48. 6
      NRefactory/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs
  49. 17
      NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultAccessor.cs
  50. 60
      NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultAttribute.cs
  51. 9
      NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs

91
ICSharpCode.Decompiler/Ast/AstBuilder.cs

@ -19,7 +19,15 @@ namespace ICSharpCode.Decompiler.Ast
using Ast = ICSharpCode.NRefactory.CSharp; using Ast = ICSharpCode.NRefactory.CSharp;
using ClassType = ICSharpCode.NRefactory.TypeSystem.ClassType; using ClassType = ICSharpCode.NRefactory.TypeSystem.ClassType;
using VarianceModifier = ICSharpCode.NRefactory.TypeSystem.VarianceModifier; using VarianceModifier = ICSharpCode.NRefactory.TypeSystem.VarianceModifier;
[Flags]
public enum ConvertTypeOptions
{
None = 0,
IncludeNamespace = 1,
IncludeTypeParameterDefinitions = 2
}
public class AstBuilder public class AstBuilder
{ {
DecompilerContext context = new DecompilerContext(); DecompilerContext context = new DecompilerContext();
@ -89,21 +97,16 @@ namespace ICSharpCode.Decompiler.Ast
public void AddAssembly(AssemblyDefinition assemblyDefinition, bool onlyAssemblyLevel = false) public void AddAssembly(AssemblyDefinition assemblyDefinition, bool onlyAssemblyLevel = false)
{ {
astCompileUnit.AddChild(
new UsingDeclaration {
Import = new SimpleType("System")
}, CompilationUnit.MemberRole);
ConvertCustomAttributes(astCompileUnit, assemblyDefinition, AttributeTarget.Assembly); ConvertCustomAttributes(astCompileUnit, assemblyDefinition, AttributeTarget.Assembly);
ConvertCustomAttributes(astCompileUnit, assemblyDefinition.MainModule, AttributeTarget.Module); ConvertCustomAttributes(astCompileUnit, assemblyDefinition.MainModule, AttributeTarget.Module);
if (!onlyAssemblyLevel) { if (!onlyAssemblyLevel) {
foreach (TypeDefinition typeDef in assemblyDefinition.MainModule.Types) foreach (TypeDefinition typeDef in assemblyDefinition.MainModule.Types) {
{
// Skip nested types - they will be added by the parent type
if (typeDef.DeclaringType != null) continue;
// Skip the <Module> class // Skip the <Module> class
if (typeDef.Name == "<Module>") continue; if (typeDef.Name == "<Module>") continue;
// Skip any hidden types
if (AstBuilder.MemberIsHidden(typeDef, context.Settings))
continue;
AddType(typeDef); AddType(typeDef);
} }
@ -264,7 +267,7 @@ namespace ICSharpCode.Decompiler.Ast
transform.Run(astCompileUnit); transform.Run(astCompileUnit);
} }
string CleanName(string name) internal static string CleanName(string name)
{ {
int pos = name.LastIndexOf('`'); int pos = name.LastIndexOf('`');
if (pos >= 0) if (pos >= 0)
@ -283,13 +286,13 @@ namespace ICSharpCode.Decompiler.Ast
/// a type system type reference.</param> /// a type system type reference.</param>
/// <param name="typeAttributes">Attributes associated with the Cecil type reference. /// <param name="typeAttributes">Attributes associated with the Cecil type reference.
/// This is used to support the 'dynamic' type.</param> /// This is used to support the 'dynamic' type.</param>
public static AstType ConvertType(TypeReference type, ICustomAttributeProvider typeAttributes = null) public static AstType ConvertType(TypeReference type, ICustomAttributeProvider typeAttributes = null, ConvertTypeOptions options = ConvertTypeOptions.None)
{ {
int typeIndex = 0; int typeIndex = 0;
return ConvertType(type, typeAttributes, ref typeIndex); return ConvertType(type, typeAttributes, ref typeIndex, options);
} }
static AstType ConvertType(TypeReference type, ICustomAttributeProvider typeAttributes, ref int typeIndex) static AstType ConvertType(TypeReference type, ICustomAttributeProvider typeAttributes, ref int typeIndex, ConvertTypeOptions options)
{ {
while (type is OptionalModifierType || type is RequiredModifierType) { while (type is OptionalModifierType || type is RequiredModifierType) {
type = ((TypeSpecification)type).ElementType; type = ((TypeSpecification)type).ElementType;
@ -301,39 +304,44 @@ namespace ICSharpCode.Decompiler.Ast
if (type is Mono.Cecil.ByReferenceType) { if (type is Mono.Cecil.ByReferenceType) {
typeIndex++; typeIndex++;
// by reference type cannot be represented in C#; so we'll represent it as a pointer instead // by reference type cannot be represented in C#; so we'll represent it as a pointer instead
return ConvertType((type as Mono.Cecil.ByReferenceType).ElementType, typeAttributes, ref typeIndex) return ConvertType((type as Mono.Cecil.ByReferenceType).ElementType, typeAttributes, ref typeIndex, options)
.MakePointerType(); .MakePointerType();
} else if (type is Mono.Cecil.PointerType) { } else if (type is Mono.Cecil.PointerType) {
typeIndex++; typeIndex++;
return ConvertType((type as Mono.Cecil.PointerType).ElementType, typeAttributes, ref typeIndex) return ConvertType((type as Mono.Cecil.PointerType).ElementType, typeAttributes, ref typeIndex, options)
.MakePointerType(); .MakePointerType();
} else if (type is Mono.Cecil.ArrayType) { } else if (type is Mono.Cecil.ArrayType) {
typeIndex++; typeIndex++;
return ConvertType((type as Mono.Cecil.ArrayType).ElementType, typeAttributes, ref typeIndex) return ConvertType((type as Mono.Cecil.ArrayType).ElementType, typeAttributes, ref typeIndex, options)
.MakeArrayType((type as Mono.Cecil.ArrayType).Rank); .MakeArrayType((type as Mono.Cecil.ArrayType).Rank);
} else if (type is GenericInstanceType) { } else if (type is GenericInstanceType) {
GenericInstanceType gType = (GenericInstanceType)type; GenericInstanceType gType = (GenericInstanceType)type;
if (gType.ElementType.Namespace == "System" && gType.ElementType.Name == "Nullable`1" && gType.GenericArguments.Count == 1) { if (gType.ElementType.Namespace == "System" && gType.ElementType.Name == "Nullable`1" && gType.GenericArguments.Count == 1) {
typeIndex++; typeIndex++;
return new ComposedType { return new ComposedType {
BaseType = ConvertType(gType.GenericArguments[0], typeAttributes, ref typeIndex), BaseType = ConvertType(gType.GenericArguments[0], typeAttributes, ref typeIndex, options),
HasNullableSpecifier = true HasNullableSpecifier = true
}; };
} }
AstType baseType = ConvertType(gType.ElementType, typeAttributes, ref typeIndex); AstType baseType = ConvertType(gType.ElementType, typeAttributes, ref typeIndex, options & ~ConvertTypeOptions.IncludeTypeParameterDefinitions);
List<AstType> typeArguments = new List<AstType>(); List<AstType> typeArguments = new List<AstType>();
foreach (var typeArgument in gType.GenericArguments) { foreach (var typeArgument in gType.GenericArguments) {
typeIndex++; typeIndex++;
typeArguments.Add(ConvertType(typeArgument, typeAttributes, ref typeIndex)); typeArguments.Add(ConvertType(typeArgument, typeAttributes, ref typeIndex, options));
} }
ApplyTypeArgumentsTo(baseType, typeArguments); ApplyTypeArgumentsTo(baseType, typeArguments);
return baseType; return baseType;
} else if (type is GenericParameter) { } else if (type is GenericParameter) {
return new SimpleType(type.Name); return new SimpleType(type.Name);
} else if (type.IsNested) { } else if (type.IsNested) {
AstType typeRef = ConvertType(type.DeclaringType, typeAttributes, ref typeIndex); AstType typeRef = ConvertType(type.DeclaringType, typeAttributes, ref typeIndex, options & ~ConvertTypeOptions.IncludeTypeParameterDefinitions);
string namepart = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name); string namepart = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name);
return new MemberType { Target = typeRef, MemberName = namepart }.WithAnnotation(type); MemberType memberType = new MemberType { Target = typeRef, MemberName = namepart };
memberType.AddAnnotation(type);
if ((options & ConvertTypeOptions.IncludeTypeParameterDefinitions) == ConvertTypeOptions.IncludeTypeParameterDefinitions) {
AddTypeParameterDefininitionsTo(type, memberType);
}
return memberType;
} else { } else {
string ns = type.Namespace ?? string.Empty; string ns = type.Namespace ?? string.Empty;
string name = type.Name; string name = type.Name;
@ -382,18 +390,35 @@ namespace ICSharpCode.Decompiler.Ast
name = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(name); name = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(name);
// TODO: Until we can simplify type with 'using', use just the name without namesapce AstType astType;
return new SimpleType(name).WithAnnotation(type); if ((options & ConvertTypeOptions.IncludeNamespace) == ConvertTypeOptions.IncludeNamespace && ns.Length > 0) {
string[] parts = ns.Split('.');
AstType nsType = new SimpleType(parts[0]);
for (int i = 1; i < parts.Length; i++) {
nsType = new MemberType { Target = nsType, MemberName = parts[i] };
}
astType = new MemberType { Target = nsType, MemberName = name };
} else {
astType = new SimpleType(name);
}
astType.AddAnnotation(type);
// if (ns.Length == 0) if ((options & ConvertTypeOptions.IncludeTypeParameterDefinitions) == ConvertTypeOptions.IncludeTypeParameterDefinitions) {
// return new SimpleType(name).WithAnnotation(type); AddTypeParameterDefininitionsTo(type, astType);
// string[] parts = ns.Split('.'); }
// AstType nsType = new SimpleType(parts[0]); return astType;
// for (int i = 1; i < parts.Length; i++) { }
// nsType = new MemberType { Target = nsType, MemberName = parts[i] }; }
// } }
// return new MemberType { Target = nsType, MemberName = name }.WithAnnotation(type);
static void AddTypeParameterDefininitionsTo(TypeReference type, AstType astType)
{
if (type.HasGenericParameters) {
List<AstType> typeArguments = new List<AstType>();
foreach (GenericParameter gp in type.GenericParameters) {
typeArguments.Add(new SimpleType(gp.Name));
} }
ApplyTypeArgumentsTo(astType, typeArguments);
} }
} }

2
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -198,8 +198,6 @@ namespace ICSharpCode.Decompiler.Ast
yield return fixedStatement; yield return fixedStatement;
} else if (node is ILBlock) { } else if (node is ILBlock) {
yield return TransformBlock((ILBlock)node); yield return TransformBlock((ILBlock)node);
} else if (node is ILComment) {
yield return new CommentStatement(((ILComment)node).Text).WithAnnotation(((ILComment)node).ILRanges);
} else { } else {
throw new Exception("Unknown node type"); throw new Exception("Unknown node type");
} }

148
ICSharpCode.Decompiler/Ast/CecilTypeResolveContext.cs

@ -0,0 +1,148 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast
{
/// <summary>
/// ITypeResolveContext implementation that lazily loads types from Cecil.
/// </summary>
public class CecilTypeResolveContext : ISynchronizedTypeResolveContext, IProjectContent
{
readonly ModuleDefinition module;
readonly string[] namespaces;
readonly CecilLoader loader;
Dictionary<TypeDefinition, WeakReference> dict = new Dictionary<TypeDefinition, WeakReference>();
int countUntilNextCleanup = 4;
public CecilTypeResolveContext(ModuleDefinition module)
{
this.loader = new CecilLoader();
this.loader.IncludeInternalMembers = true;
this.module = module;
this.namespaces = module.Types.Select(t => t.Namespace).Distinct().ToArray();
List<IAttribute> assemblyAttributes = new List<IAttribute>();
foreach (var attr in module.Assembly.CustomAttributes) {
assemblyAttributes.Add(loader.ReadAttribute(attr));
}
this.AssemblyAttributes = assemblyAttributes.AsReadOnly();
}
ITypeDefinition GetClass(TypeDefinition cecilType)
{
lock (dict) {
WeakReference wr;
ITypeDefinition type;
if (dict.TryGetValue(cecilType, out wr)) {
type = (ITypeDefinition)wr.Target;
} else {
wr = null;
type = null;
}
if (type == null) {
type = loader.LoadType(cecilType, this);
}
if (wr == null) {
if (--countUntilNextCleanup <= 0)
CleanupDict();
wr = new WeakReference(type);
dict.Add(cecilType, wr);
} else {
wr.Target = type;
}
return type;
}
}
void CleanupDict()
{
List<TypeDefinition> deletedKeys = new List<TypeDefinition>();
foreach (var pair in dict) {
if (!pair.Value.IsAlive) {
deletedKeys.Add(pair.Key);
}
}
foreach (var key in deletedKeys) {
dict.Remove(key);
}
countUntilNextCleanup = dict.Count + 4;
}
public IList<IAttribute> AssemblyAttributes { get; private set; }
public ITypeDefinition GetClass(string nameSpace, string name, int typeParameterCount, StringComparer nameComparer)
{
if (typeParameterCount > 0)
name = name + "`" + typeParameterCount.ToString();
if (nameComparer == StringComparer.Ordinal) {
TypeDefinition cecilType = module.GetType(nameSpace, name);
if (cecilType != null)
return GetClass(cecilType);
else
return null;
}
foreach (TypeDefinition cecilType in module.Types) {
if (nameComparer.Equals(name, cecilType.Name)
&& nameComparer.Equals(nameSpace, cecilType.Namespace)
&& cecilType.GenericParameters.Count == typeParameterCount)
{
return GetClass(cecilType);
}
}
return null;
}
public IEnumerable<ITypeDefinition> GetClasses()
{
foreach (TypeDefinition cecilType in module.Types) {
yield return GetClass(cecilType);
}
}
public IEnumerable<ITypeDefinition> GetClasses(string nameSpace, StringComparer nameComparer)
{
foreach (TypeDefinition cecilType in module.Types) {
if (nameComparer.Equals(nameSpace, cecilType.Namespace))
yield return GetClass(cecilType);
}
}
public IEnumerable<string> GetNamespaces()
{
return namespaces;
}
public string GetNamespace(string nameSpace, StringComparer nameComparer)
{
foreach (string ns in namespaces) {
if (nameComparer.Equals(ns, nameSpace))
return ns;
}
return null;
}
ICSharpCode.NRefactory.Utils.CacheManager ITypeResolveContext.CacheManager {
get {
// We don't support caching
return null;
}
}
ISynchronizedTypeResolveContext ITypeResolveContext.Synchronize()
{
// This class is logically immutable
return this;
}
void IDisposable.Dispose()
{
// exit from Synchronize() block
}
}
}

32
ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs

@ -11,7 +11,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
/// <summary> /// <summary>
/// Base class for AST visitors that need the current type/method context info. /// Base class for AST visitors that need the current type/method context info.
/// </summary> /// </summary>
public abstract class ContextTrackingVisitor : DepthFirstAstVisitor<object, object>, IAstTransform public abstract class ContextTrackingVisitor<TResult> : DepthFirstAstVisitor<object, TResult>, IAstTransform
{ {
protected readonly DecompilerContext context; protected readonly DecompilerContext context;
@ -22,7 +22,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
this.context = context; this.context = context;
} }
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) public override TResult VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
{ {
TypeDefinition oldType = context.CurrentType; TypeDefinition oldType = context.CurrentType;
try { try {
@ -33,7 +33,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
} }
public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) public override TResult VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data)
{ {
Debug.Assert(context.CurrentMethod == null); Debug.Assert(context.CurrentMethod == null);
try { try {
@ -44,7 +44,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
} }
public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) public override TResult VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data)
{ {
Debug.Assert(context.CurrentMethod == null); Debug.Assert(context.CurrentMethod == null);
try { try {
@ -55,7 +55,29 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
} }
public override object VisitAccessor(Accessor accessor, object data) public override TResult VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration, object data)
{
Debug.Assert(context.CurrentMethod == null);
try {
context.CurrentMethod = destructorDeclaration.Annotation<MethodDefinition>();
return base.VisitDestructorDeclaration(destructorDeclaration, data);
} finally {
context.CurrentMethod = null;
}
}
public override TResult VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, object data)
{
Debug.Assert(context.CurrentMethod == null);
try {
context.CurrentMethod = operatorDeclaration.Annotation<MethodDefinition>();
return base.VisitOperatorDeclaration(operatorDeclaration, data);
} finally {
context.CurrentMethod = null;
}
}
public override TResult VisitAccessor(Accessor accessor, object data)
{ {
Debug.Assert(context.CurrentMethod == null); Debug.Assert(context.CurrentMethod == null);
try { try {

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

@ -18,7 +18,7 @@ namespace ICSharpCode.Decompiler.Ast.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 public class DelegateConstruction : ContextTrackingVisitor<object>
{ {
internal sealed class Annotation internal sealed class Annotation
{ {
@ -182,7 +182,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public override object VisitBlockStatement(BlockStatement blockStatement, object data) public override object VisitBlockStatement(BlockStatement blockStatement, object data)
{ {
base.VisitBlockStatement(blockStatement, data); base.VisitBlockStatement(blockStatement, data);
foreach (VariableDeclarationStatement stmt in blockStatement.Statements.OfType<VariableDeclarationStatement>()) { foreach (VariableDeclarationStatement stmt in blockStatement.Statements.OfType<VariableDeclarationStatement>().ToArray()) {
if (stmt.Variables.Count() != 1) if (stmt.Variables.Count() != 1)
continue; continue;
var variable = stmt.Variables.Single(); var variable = stmt.Variables.Single();

87
ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs

@ -0,0 +1,87 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Introduces using declarations.
/// </summary>
public class IntroduceUsingDeclarations : DepthFirstAstVisitor<object, object>, IAstTransform
{
DecompilerContext context;
public IntroduceUsingDeclarations(DecompilerContext context)
{
this.context = context;
currentNamespace = context.CurrentType != null ? context.CurrentType.Namespace : string.Empty;
}
public void Run(AstNode compilationUnit)
{
// Don't show using when decompiling a single method or nested types:
if (context.CurrentMethod != null || (context.CurrentType != null && context.CurrentType.IsNested))
return;
// First determine all the namespaces that need to be imported:
compilationUnit.AcceptVisitor(this, null);
importedNamespaces.Add("System"); // always import System, even when not necessary
// Now add using declarations for those namespaces:
foreach (string ns in importedNamespaces.OrderByDescending(n => n)) {
// we go backwards (OrderByDescending) through the list of namespaces because we insert them backwards
// (always inserting at the start of the list)
string[] parts = ns.Split('.');
AstType nsType = new SimpleType(parts[0]);
for (int i = 1; i < parts.Length; i++) {
nsType = new MemberType { Target = nsType, MemberName = parts[i] };
}
compilationUnit.InsertChildAfter(null, new UsingDeclaration { Import = nsType }, CompilationUnit.MemberRole);
}
// TODO: verify that the SimpleTypes refer to the correct type (no ambiguities)
}
readonly HashSet<string> importedNamespaces = new HashSet<string>();
string currentNamespace;
bool IsParentOfCurrentNamespace(string ns)
{
if (ns.Length == 0)
return true;
if (currentNamespace.StartsWith(ns, StringComparison.Ordinal)) {
if (currentNamespace.Length == ns.Length)
return true;
if (currentNamespace[ns.Length] == '.')
return true;
}
return false;
}
public override object VisitSimpleType(SimpleType simpleType, object data)
{
TypeReference tr = simpleType.Annotation<TypeReference>();
if (tr != null && !IsParentOfCurrentNamespace(tr.Namespace)) {
importedNamespaces.Add(tr.Namespace);
}
return base.VisitSimpleType(simpleType, data); // also visit type arguments
}
public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data)
{
string oldNamespace = currentNamespace;
foreach (Identifier ident in namespaceDeclaration.Identifiers) {
currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident.Name);
}
base.VisitNamespaceDeclaration(namespaceDeclaration, data);
currentNamespace = oldNamespace;
return null;
}
}
}

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

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.PatternMatching; using ICSharpCode.NRefactory.CSharp.PatternMatching;
@ -13,37 +14,106 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
/// <summary> /// <summary>
/// Finds the expanded form of using statements using pattern matching and replaces it with a UsingStatement. /// Finds the expanded form of using statements using pattern matching and replaces it with a UsingStatement.
/// </summary> /// </summary>
public class PatternStatementTransform : IAstTransform public sealed class PatternStatementTransform : ContextTrackingVisitor<AstNode>, IAstTransform
{ {
DecompilerContext context; public PatternStatementTransform(DecompilerContext context) : base(context)
{
}
public PatternStatementTransform(DecompilerContext context) #region Visitor Overrides
protected override AstNode VisitChildren(AstNode node, object data)
{ {
if (context == null) // Go through the children, and keep visiting a node as long as it changes.
throw new ArgumentNullException("context"); // Because some transforms delete/replace nodes before and after the node being transformed, we rely
this.context = context; // on the transform's return value to know where we need to keep iterating.
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
AstNode oldChild;
do {
oldChild = child;
child = child.AcceptVisitor(this, data);
Debug.Assert(child != null && child.Parent == node);
} while (child != oldChild);
}
return node;
} }
public void Run(AstNode compilationUnit) public override AstNode VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, object data)
{ {
if (context.Settings.UsingStatement) AstNode result;
TransformUsings(compilationUnit); if (context.Settings.UsingStatement) {
if (context.Settings.ForEachStatement) result = TransformUsings(variableDeclarationStatement);
TransformForeach(compilationUnit); if (result != null)
TransformFor(compilationUnit); return result;
TransformDoWhile(compilationUnit); }
if (context.Settings.LockStatement) result = TransformFor(variableDeclarationStatement);
TransformLock(compilationUnit); if (result != null)
if (context.Settings.SwitchStatementOnString) return result;
TransformSwitchOnString(compilationUnit); if (context.Settings.LockStatement) {
if (context.Settings.AutomaticProperties) result = TransformLock(variableDeclarationStatement);
TransformAutomaticProperties(compilationUnit); if (result != null)
if (context.Settings.AutomaticEvents) return result;
TransformAutomaticEvents(compilationUnit); }
return base.VisitVariableDeclarationStatement(variableDeclarationStatement, data);
TransformTryCatchFinally(compilationUnit); }
public override AstNode VisitUsingStatement(UsingStatement usingStatement, object data)
{
if (context.Settings.ForEachStatement) {
AstNode result = TransformForeach(usingStatement);
if (result != null)
return result;
}
return base.VisitUsingStatement(usingStatement, data);
} }
public override AstNode VisitWhileStatement(WhileStatement whileStatement, object data)
{
return TransformDoWhile(whileStatement) ?? base.VisitWhileStatement(whileStatement, data);
}
public override AstNode VisitIfElseStatement(IfElseStatement ifElseStatement, object data)
{
if (context.Settings.SwitchStatementOnString) {
AstNode result = TransformSwitchOnString(ifElseStatement);
if (result != null)
return result;
}
return base.VisitIfElseStatement(ifElseStatement, data);
}
public override AstNode VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, object data)
{
if (context.Settings.AutomaticProperties) {
AstNode result = TransformAutomaticProperties(propertyDeclaration);
if (result != null)
return result;
}
return base.VisitPropertyDeclaration(propertyDeclaration, data);
}
public override AstNode VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration, object data)
{
// first apply transforms to the accessor bodies
base.VisitCustomEventDeclaration(eventDeclaration, data);
if (context.Settings.AutomaticEvents) {
AstNode result = TransformAutomaticEvents(eventDeclaration);
if (result != null)
return result;
}
return eventDeclaration;
}
public override AstNode VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data)
{
return TransformDestructor(methodDeclaration) ?? base.VisitMethodDeclaration(methodDeclaration, data);
}
public override AstNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement, object data)
{
return TransformTryCatchFinally(tryCatchStatement) ?? base.VisitTryCatchStatement(tryCatchStatement, data);
}
#endregion
/// <summary> /// <summary>
/// $type $variable = $initializer; /// $type $variable = $initializer;
/// </summary> /// </summary>
@ -93,31 +163,30 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
}; };
public void TransformUsings(AstNode compilationUnit) public UsingStatement TransformUsings(VariableDeclarationStatement node)
{ {
foreach (AstNode node in compilationUnit.Descendants.OfType<VariableDeclarationStatement>().ToArray()) { Match m1 = variableDeclPattern.Match(node);
Match m1 = variableDeclPattern.Match(node); if (m1 == null) return null;
if (m1 == null) continue; AstNode tryCatch = node.NextSibling;
AstNode tryCatch = node.NextSibling; while (simpleVariableDefinition.Match(tryCatch) != null)
while (simpleVariableDefinition.Match(tryCatch) != null) tryCatch = tryCatch.NextSibling;
tryCatch = tryCatch.NextSibling; Match m2 = usingTryCatchPattern.Match(tryCatch);
Match m2 = usingTryCatchPattern.Match(tryCatch); if (m2 == null) return null;
if (m2 == null) continue; if (m1.Get<VariableInitializer>("variable").Single().Name == m2.Get<IdentifierExpression>("ident").Single().Identifier) {
if (m1.Get<VariableInitializer>("variable").Single().Name == m2.Get<IdentifierExpression>("ident").Single().Identifier) { if (m2.Has("valueType")) {
if (m2.Has("valueType")) { // if there's no if(x!=null), then it must be a value type
// if there's no if(x!=null), then it must be a value type TypeReference tr = m1.Get<AstType>("type").Single().Annotation<TypeReference>();
TypeReference tr = m1.Get<AstType>("type").Single().Annotation<TypeReference>(); if (tr == null || !tr.IsValueType)
if (tr == null || !tr.IsValueType) return null;
continue;
}
BlockStatement body = m2.Get<BlockStatement>("body").Single();
tryCatch.ReplaceWith(
new UsingStatement {
ResourceAcquisition = node.Detach(),
EmbeddedStatement = body.Detach()
});
} }
BlockStatement body = m2.Get<BlockStatement>("body").Single();
UsingStatement usingStatement = new UsingStatement();
usingStatement.ResourceAcquisition = node.Detach();
usingStatement.EmbeddedStatement = body.Detach();
tryCatch.ReplaceWith(usingStatement);
return usingStatement;
} }
return null;
} }
#endregion #endregion
@ -184,29 +253,28 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}.ToStatement() }.ToStatement()
}; };
public void TransformForeach(AstNode compilationUnit) public ForeachStatement TransformForeach(UsingStatement node)
{ {
foreach (AstNode node in compilationUnit.Descendants.OfType<UsingStatement>().ToArray()) { Match m = foreachPattern.Match(node);
Match m = foreachPattern.Match(node); if (m == null)
if (m == null) return null;
continue; VariableInitializer enumeratorVar = m.Get<VariableInitializer>("enumeratorVariable").Single();
VariableInitializer enumeratorVar = m.Get<VariableInitializer>("enumeratorVariable").Single(); VariableInitializer itemVar = m.Get<VariableInitializer>("itemVariable").Single();
VariableInitializer itemVar = m.Get<VariableInitializer>("itemVariable").Single(); if (m.Has("itemVariableInsideLoop") && itemVar.Annotation<DelegateConstruction.CapturedVariableAnnotation>() != null) {
if (m.Has("itemVariableInsideLoop") && itemVar.Annotation<DelegateConstruction.CapturedVariableAnnotation>() != null) { // cannot move captured variables out of loops
// cannot move captured variables out of loops return null;
continue;
}
BlockStatement newBody = new BlockStatement();
foreach (Statement stmt in m.Get<Statement>("statement"))
newBody.Add(stmt.Detach());
node.ReplaceWith(
new ForeachStatement {
VariableType = m.Get<AstType>("itemType").Single().Detach(),
VariableName = itemVar.Name,
InExpression = m.Get<Expression>("collection").Single().Detach(),
EmbeddedStatement = newBody
});
} }
BlockStatement newBody = new BlockStatement();
foreach (Statement stmt in m.Get<Statement>("statement"))
newBody.Add(stmt.Detach());
ForeachStatement foreachStatement = new ForeachStatement {
VariableType = m.Get<AstType>("itemType").Single().Detach(),
VariableName = itemVar.Name,
InExpression = m.Get<Expression>("collection").Single().Detach(),
EmbeddedStatement = newBody
};
node.ReplaceWith(foreachStatement);
return foreachStatement;
} }
#endregion #endregion
@ -231,32 +299,30 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
}}; }};
public void TransformFor(AstNode compilationUnit) public ForStatement TransformFor(VariableDeclarationStatement node)
{ {
foreach (AstNode node in compilationUnit.Descendants.OfType<VariableDeclarationStatement>().ToArray()) { Match m1 = variableDeclPattern.Match(node);
Match m1 = variableDeclPattern.Match(node); if (m1 == null) return null;
if (m1 == null) continue; AstNode next = node.NextSibling;
AstNode next = node.NextSibling; while (simpleVariableDefinition.Match(next) != null)
while (simpleVariableDefinition.Match(next) != null) next = next.NextSibling;
next = next.NextSibling; Match m2 = forPattern.Match(next);
Match m2 = forPattern.Match(next); if (m2 == null) return null;
if (m2 == null) continue; // ensure the variable in the for pattern is the same as in the declaration
// ensure the variable in the for pattern is the same as in the declaration if (m1.Get<VariableInitializer>("variable").Single().Name != m2.Get<IdentifierExpression>("ident").Single().Identifier)
if (m1.Get<VariableInitializer>("variable").Single().Name != m2.Get<IdentifierExpression>("ident").Single().Identifier) return null;
continue; WhileStatement loop = (WhileStatement)next;
WhileStatement loop = (WhileStatement)next; node.Remove();
node.Remove(); BlockStatement newBody = new BlockStatement();
BlockStatement newBody = new BlockStatement(); foreach (Statement stmt in m2.Get<Statement>("statement"))
foreach (Statement stmt in m2.Get<Statement>("statement")) newBody.Add(stmt.Detach());
newBody.Add(stmt.Detach()); ForStatement forStatement = new ForStatement();
loop.ReplaceWith( forStatement.Initializers.Add(node);
new ForStatement { forStatement.Condition = loop.Condition.Detach();
Initializers = { (VariableDeclarationStatement)node }, forStatement.Iterators.Add(m2.Get<Statement>("increment").Single().Detach());
Condition = loop.Condition.Detach(), forStatement.EmbeddedStatement = newBody;
Iterators = { m2.Get<Statement>("increment").Single().Detach() }, loop.ReplaceWith(forStatement);
EmbeddedStatement = newBody return forStatement;
});
}
} }
#endregion #endregion
@ -273,37 +339,37 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
}}; }};
public void TransformDoWhile(AstNode compilationUnit) public DoWhileStatement TransformDoWhile(WhileStatement whileLoop)
{ {
foreach (WhileStatement whileLoop in compilationUnit.Descendants.OfType<WhileStatement>().ToArray()) { Match m = doWhilePattern.Match(whileLoop);
Match m = doWhilePattern.Match(whileLoop); if (m != null) {
if (m != null) { DoWhileStatement doLoop = new DoWhileStatement();
DoWhileStatement doLoop = new DoWhileStatement(); doLoop.Condition = new UnaryOperatorExpression(UnaryOperatorType.Not, m.Get<Expression>("condition").Single().Detach());
doLoop.Condition = new UnaryOperatorExpression(UnaryOperatorType.Not, m.Get<Expression>("condition").Single().Detach()); doLoop.Condition.AcceptVisitor(new PushNegation(), null);
doLoop.Condition.AcceptVisitor(new PushNegation(), null); BlockStatement block = (BlockStatement)whileLoop.EmbeddedStatement;
BlockStatement block = (BlockStatement)whileLoop.EmbeddedStatement; block.Statements.Last().Remove(); // remove if statement
block.Statements.Last().Remove(); // remove if statement doLoop.EmbeddedStatement = block.Detach();
doLoop.EmbeddedStatement = block.Detach(); whileLoop.ReplaceWith(doLoop);
whileLoop.ReplaceWith(doLoop);
// we may have to extract variable definitions out of the loop if they were used in the condition:
// we may have to extract variable definitions out of the loop if they were used in the condition: foreach (var varDecl in block.Statements.OfType<VariableDeclarationStatement>()) {
foreach (var varDecl in block.Statements.OfType<VariableDeclarationStatement>()) { VariableInitializer v = varDecl.Variables.Single();
VariableInitializer v = varDecl.Variables.Single(); if (doLoop.Condition.DescendantsAndSelf.OfType<IdentifierExpression>().Any(i => i.Identifier == v.Name)) {
if (doLoop.Condition.DescendantsAndSelf.OfType<IdentifierExpression>().Any(i => i.Identifier == v.Name)) { AssignmentExpression assign = new AssignmentExpression(new IdentifierExpression(v.Name), v.Initializer.Detach());
AssignmentExpression assign = new AssignmentExpression(new IdentifierExpression(v.Name), v.Initializer.Detach()); // move annotations from v to assign:
// move annotations from v to assign: assign.CopyAnnotationsFrom(v);
assign.CopyAnnotationsFrom(v); v.RemoveAnnotations<object>();
v.RemoveAnnotations<object>(); // remove varDecl with assignment; and move annotations from varDecl to the ExpressionStatement:
// remove varDecl with assignment; and move annotations from varDecl to the ExpressionStatement: varDecl.ReplaceWith(new ExpressionStatement(assign).CopyAnnotationsFrom(varDecl));
varDecl.ReplaceWith(new ExpressionStatement(assign).CopyAnnotationsFrom(varDecl)); varDecl.RemoveAnnotations<object>();
varDecl.RemoveAnnotations<object>();
// insert the varDecl above the do-while loop:
// insert the varDecl above the do-while loop: doLoop.Parent.InsertChildBefore(doLoop, varDecl, BlockStatement.StatementRole);
doLoop.Parent.InsertChildBefore(doLoop, varDecl, BlockStatement.StatementRole);
}
} }
} }
return doLoop;
} }
return null;
} }
#endregion #endregion
@ -338,49 +404,49 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
}}; }};
public void TransformLock(AstNode compilationUnit) public LockStatement TransformLock(VariableDeclarationStatement node)
{ {
foreach (AstNode node in compilationUnit.Descendants.OfType<VariableDeclarationStatement>().ToArray()) { Match m1 = lockFlagInitPattern.Match(node);
Match m1 = lockFlagInitPattern.Match(node); if (m1 == null) return null;
if (m1 == null) continue; AstNode tryCatch = node.NextSibling;
AstNode tryCatch = node.NextSibling; while (simpleVariableDefinition.Match(tryCatch) != null)
while (simpleVariableDefinition.Match(tryCatch) != null) tryCatch = tryCatch.NextSibling;
tryCatch = tryCatch.NextSibling; Match m2 = lockTryCatchPattern.Match(tryCatch);
Match m2 = lockTryCatchPattern.Match(tryCatch); if (m2 == null) return null;
if (m2 == null) continue; if (m1.Get<VariableInitializer>("variable").Single().Name == m2.Get<IdentifierExpression>("flag").Single().Identifier) {
if (m1.Get<VariableInitializer>("variable").Single().Name == m2.Get<IdentifierExpression>("flag").Single().Identifier) { Expression enter = m2.Get<Expression>("enter").Single();
Expression enter = m2.Get<Expression>("enter").Single(); IdentifierExpression exit = m2.Get<IdentifierExpression>("exit").Single();
IdentifierExpression exit = m2.Get<IdentifierExpression>("exit").Single(); if (exit.Match(enter) == null) {
if (exit.Match(enter) == null) { // If exit and enter are not the same, then enter must be "exit = ..."
// If exit and enter are not the same, then enter must be "exit = ..." AssignmentExpression assign = enter as AssignmentExpression;
AssignmentExpression assign = enter as AssignmentExpression; if (assign == null)
if (assign == null) return null;
continue; if (exit.Match(assign.Left) == null)
if (exit.Match(assign.Left) == null) return null;
continue; enter = assign.Right;
enter = assign.Right; // Remove 'exit' variable:
// Remove 'exit' variable: bool ok = false;
bool ok = false; for (AstNode tmp = node.NextSibling; tmp != tryCatch; tmp = tmp.NextSibling) {
for (AstNode tmp = node.NextSibling; tmp != tryCatch; tmp = tmp.NextSibling) { VariableDeclarationStatement v = (VariableDeclarationStatement)tmp;
VariableDeclarationStatement v = (VariableDeclarationStatement)tmp; if (v.Variables.Single().Name == exit.Identifier) {
if (v.Variables.Single().Name == exit.Identifier) { ok = true;
ok = true; v.Remove();
v.Remove(); break;
break;
}
} }
if (!ok)
continue;
} }
// transform the code into a lock statement: if (!ok)
LockStatement l = new LockStatement(); return null;
l.Expression = enter.Detach();
l.EmbeddedStatement = ((TryCatchStatement)tryCatch).TryBlock.Detach();
((BlockStatement)l.EmbeddedStatement).Statements.First().Remove(); // Remove 'Enter()' call
tryCatch.ReplaceWith(l);
node.Remove(); // remove flag variable
} }
// transform the code into a lock statement:
LockStatement l = new LockStatement();
l.Expression = enter.Detach();
l.EmbeddedStatement = ((TryCatchStatement)tryCatch).TryBlock.Detach();
((BlockStatement)l.EmbeddedStatement).Statements.First().Remove(); // Remove 'Enter()' call
tryCatch.ReplaceWith(l);
node.Remove(); // remove flag variable
return l;
} }
return null;
} }
#endregion #endregion
@ -427,60 +493,59 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
FalseStatement = new OptionalNode("nullStmt", new BlockStatement { Statements = { new Repeat(new AnyNode()) } }) FalseStatement = new OptionalNode("nullStmt", new BlockStatement { Statements = { new Repeat(new AnyNode()) } })
}; };
public void TransformSwitchOnString(AstNode compilationUnit) public SwitchStatement TransformSwitchOnString(IfElseStatement node)
{ {
foreach (AstNode node in compilationUnit.Descendants.OfType<IfElseStatement>().ToArray()) { Match m = switchOnStringPattern.Match(node);
Match m = switchOnStringPattern.Match(node); if (m == null)
if (m == null) return null;
continue; if (m.Has("nonNullDefaultStmt") && !m.Has("nullStmt"))
if (m.Has("nonNullDefaultStmt") && !m.Has("nullStmt")) return null;
continue; // switchVar must be the same as switchExpr; or switchExpr must be an assignment and switchVar the left side of that assignment
// switchVar must be the same as switchExpr; or switchExpr must be an assignment and switchVar the left side of that assignment if (m.Get("switchVar").Single().Match(m.Get("switchExpr").Single()) == null) {
if (m.Get("switchVar").Single().Match(m.Get("switchExpr").Single()) == null) { AssignmentExpression assign = m.Get("switchExpr").Single() as AssignmentExpression;
AssignmentExpression assign = m.Get("switchExpr").Single() as AssignmentExpression; if (m.Get("switchVar").Single().Match(assign.Left) == null)
if (m.Get("switchVar").Single().Match(assign.Left) == null) return null;
}
FieldReference cachedDictField = m.Get("cachedDict").Single().Annotation<FieldReference>();
if (cachedDictField == null || !cachedDictField.DeclaringType.Name.StartsWith("<PrivateImplementationDetails>", StringComparison.Ordinal))
return null;
List<Statement> dictCreation = m.Get<BlockStatement>("dictCreation").Single().Statements.ToList();
List<KeyValuePair<string, int>> dict = BuildDictionary(dictCreation);
SwitchStatement sw = m.Get<SwitchStatement>("switch").Single();
sw.Expression = m.Get<Expression>("switchExpr").Single().Detach();
foreach (SwitchSection section in sw.SwitchSections) {
List<CaseLabel> labels = section.CaseLabels.ToList();
section.CaseLabels.Clear();
foreach (CaseLabel label in labels) {
PrimitiveExpression expr = label.Expression as PrimitiveExpression;
if (expr == null || !(expr.Value is int))
continue; continue;
} int val = (int)expr.Value;
FieldReference cachedDictField = m.Get("cachedDict").Single().Annotation<FieldReference>(); foreach (var pair in dict) {
if (cachedDictField == null || !cachedDictField.DeclaringType.Name.StartsWith("<PrivateImplementationDetails>", StringComparison.Ordinal)) if (pair.Value == val)
continue; section.CaseLabels.Add(new CaseLabel { Expression = new PrimitiveExpression(pair.Key) });
List<Statement> dictCreation = m.Get<BlockStatement>("dictCreation").Single().Statements.ToList();
List<KeyValuePair<string, int>> dict = BuildDictionary(dictCreation);
SwitchStatement sw = m.Get<SwitchStatement>("switch").Single();
sw.Expression = m.Get<Expression>("switchExpr").Single().Detach();
foreach (SwitchSection section in sw.SwitchSections) {
List<CaseLabel> labels = section.CaseLabels.ToList();
section.CaseLabels.Clear();
foreach (CaseLabel label in labels) {
PrimitiveExpression expr = label.Expression as PrimitiveExpression;
if (expr == null || !(expr.Value is int))
continue;
int val = (int)expr.Value;
foreach (var pair in dict) {
if (pair.Value == val)
section.CaseLabels.Add(new CaseLabel { Expression = new PrimitiveExpression(pair.Key) });
}
} }
} }
if (m.Has("nullStmt")) { }
SwitchSection section = new SwitchSection(); if (m.Has("nullStmt")) {
section.CaseLabels.Add(new CaseLabel { Expression = new NullReferenceExpression() }); SwitchSection section = new SwitchSection();
BlockStatement block = m.Get<BlockStatement>("nullStmt").Single(); section.CaseLabels.Add(new CaseLabel { Expression = new NullReferenceExpression() });
block.Statements.Add(new BreakStatement()); BlockStatement block = m.Get<BlockStatement>("nullStmt").Single();
section.Statements.Add(block.Detach()); block.Statements.Add(new BreakStatement());
section.Statements.Add(block.Detach());
sw.SwitchSections.Add(section);
if (m.Has("nonNullDefaultStmt")) {
section = new SwitchSection();
section.CaseLabels.Add(new CaseLabel());
block = new BlockStatement();
block.Statements.AddRange(m.Get<Statement>("nonNullDefaultStmt").Select(s => s.Detach()));
block.Add(new BreakStatement());
section.Statements.Add(block);
sw.SwitchSections.Add(section); sw.SwitchSections.Add(section);
if (m.Has("nonNullDefaultStmt")) {
section = new SwitchSection();
section.CaseLabels.Add(new CaseLabel());
block = new BlockStatement();
block.Statements.AddRange(m.Get<Statement>("nonNullDefaultStmt").Select(s => s.Detach()));
block.Add(new BreakStatement());
section.Statements.Add(block);
sw.SwitchSections.Add(section);
}
} }
node.ReplaceWith(sw);
} }
node.ReplaceWith(sw);
return sw;
} }
List<KeyValuePair<string, int>> BuildDictionary(List<Statement> dictCreation) List<KeyValuePair<string, int>> BuildDictionary(List<Statement> dictCreation)
@ -526,25 +591,25 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
}}}; }}};
void TransformAutomaticProperties(AstNode compilationUnit) PropertyDeclaration TransformAutomaticProperties(PropertyDeclaration property)
{ {
foreach (var property in compilationUnit.Descendants.OfType<PropertyDeclaration>()) { PropertyDefinition cecilProperty = property.Annotation<PropertyDefinition>();
PropertyDefinition cecilProperty = property.Annotation<PropertyDefinition>(); if (cecilProperty == null || cecilProperty.GetMethod == null || cecilProperty.SetMethod == null)
if (cecilProperty == null || cecilProperty.GetMethod == null || cecilProperty.SetMethod == null) return null;
continue; if (!(cecilProperty.GetMethod.IsCompilerGenerated() && cecilProperty.SetMethod.IsCompilerGenerated()))
if (!(cecilProperty.GetMethod.IsCompilerGenerated() && cecilProperty.SetMethod.IsCompilerGenerated())) return null;
continue; Match m = automaticPropertyPattern.Match(property);
Match m = automaticPropertyPattern.Match(property); if (m != null) {
if (m != null) { FieldDefinition field = m.Get("fieldReference").Single().Annotation<FieldReference>().ResolveWithinSameModule();
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); property.Getter.Body = null;
property.Getter.Body = null; property.Setter.Body = null;
property.Setter.Body = null;
}
} }
} }
// Since the event instance is not changed, we can continue in the visitor as usual, so return null
return null;
} }
void RemoveCompilerGeneratedAttribute(AstNodeCollection<AttributeSection> attributeSections) void RemoveCompilerGeneratedAttribute(AstNodeCollection<AttributeSection> attributeSections)
@ -624,33 +689,64 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return combineMethod.DeclaringType.FullName == "System.Delegate"; return combineMethod.DeclaringType.FullName == "System.Delegate";
} }
void TransformAutomaticEvents(AstNode compilationUnit) EventDeclaration TransformAutomaticEvents(CustomEventDeclaration ev)
{ {
foreach (var ev in compilationUnit.Descendants.OfType<CustomEventDeclaration>().ToArray()) { Match m1 = automaticEventPatternV4.Match(ev.AddAccessor);
Match m1 = automaticEventPatternV4.Match(ev.AddAccessor); if (!CheckAutomaticEventV4Match(m1, ev, true))
if (!CheckAutomaticEventV4Match(m1, ev, true)) return null;
continue; Match m2 = automaticEventPatternV4.Match(ev.RemoveAccessor);
Match m2 = automaticEventPatternV4.Match(ev.RemoveAccessor); if (!CheckAutomaticEventV4Match(m2, ev, false))
if (!CheckAutomaticEventV4Match(m2, ev, false)) return null;
continue; EventDeclaration ed = new EventDeclaration();
EventDeclaration ed = new EventDeclaration(); ev.Attributes.MoveTo(ed.Attributes);
ev.Attributes.MoveTo(ed.Attributes); ed.ReturnType = ev.ReturnType.Detach();
ed.ReturnType = ev.ReturnType.Detach(); ed.Modifiers = ev.Modifiers;
ed.Modifiers = ev.Modifiers; ed.Variables.Add(new VariableInitializer(ev.Name));
ed.Variables.Add(new VariableInitializer(ev.Name)); ed.CopyAnnotationsFrom(ev);
ed.CopyAnnotationsFrom(ev);
EventDefinition eventDef = ev.Annotation<EventDefinition>();
EventDefinition eventDef = ev.Annotation<EventDefinition>(); if (eventDef != null) {
if (eventDef != null) { FieldDefinition field = eventDef.DeclaringType.Fields.FirstOrDefault(f => f.Name == ev.Name);
FieldDefinition field = eventDef.DeclaringType.Fields.FirstOrDefault(f => f.Name == ev.Name); if (field != null) {
if (field != null) { ed.AddAnnotation(field);
ed.AddAnnotation(field); AstBuilder.ConvertAttributes(ed, field, AttributeTarget.Field);
AstBuilder.ConvertAttributes(ed, field, AttributeTarget.Field); }
}
ev.ReplaceWith(ed);
return ed;
}
#endregion
#region Destructor
static readonly MethodDeclaration destructorPattern = new MethodDeclaration {
Attributes = { new Repeat(new AnyNode()) },
Modifiers = Modifiers.Any,
ReturnType = new PrimitiveType("void"),
Name = "Finalize",
Body = new BlockStatement {
new TryCatchStatement {
TryBlock = new AnyNode("body"),
FinallyBlock = new BlockStatement {
new BaseReferenceExpression().Invoke("Finalize")
} }
} }
ev.ReplaceWith(ed);
} }
};
DestructorDeclaration TransformDestructor(MethodDeclaration methodDef)
{
Match m = destructorPattern.Match(methodDef);
if (m != null) {
DestructorDeclaration dd = new DestructorDeclaration();
methodDef.Attributes.MoveTo(dd.Attributes);
dd.Modifiers = methodDef.Modifiers & ~(Modifiers.Protected | Modifiers.Override);
dd.Body = m.Get<BlockStatement>("body").Single().Detach();
dd.Name = AstBuilder.CleanName(context.CurrentType.Name);
methodDef.ReplaceWith(dd);
return dd;
}
return null;
} }
#endregion #endregion
@ -669,15 +765,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
/// Simplify nested 'try { try {} catch {} } finally {}'. /// Simplify nested 'try { try {} catch {} } finally {}'.
/// This transformation must run after the using/lock tranformations. /// This transformation must run after the using/lock tranformations.
/// </summary> /// </summary>
void TransformTryCatchFinally(AstNode compilationUnit) TryCatchStatement TransformTryCatchFinally(TryCatchStatement tryFinally)
{ {
foreach (var tryFinally in compilationUnit.Descendants.OfType<TryCatchStatement>()) { if (tryCatchFinallyPattern.Match(tryFinally) != null) {
if (tryCatchFinallyPattern.Match(tryFinally) != null) { TryCatchStatement tryCatch = (TryCatchStatement)tryFinally.TryBlock.Statements.Single();
TryCatchStatement tryCatch = (TryCatchStatement)tryFinally.TryBlock.Statements.Single(); tryFinally.TryBlock = tryCatch.TryBlock.Detach();
tryFinally.TryBlock = tryCatch.TryBlock.Detach(); tryCatch.CatchClauses.MoveTo(tryFinally.CatchClauses);
tryCatch.CatchClauses.MoveTo(tryFinally.CatchClauses);
}
} }
// Since the tryFinally instance is not changed, we can continue in the visitor as usual, so return null
return null;
} }
#endregion #endregion

1
ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs

@ -23,6 +23,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
new ConvertConstructorCallIntoInitializer(), new ConvertConstructorCallIntoInitializer(),
new ReplaceMethodCallsWithOperators(), new ReplaceMethodCallsWithOperators(),
new IntroduceUnsafeModifier(), new IntroduceUnsafeModifier(),
new IntroduceUsingDeclarations(context)
}; };
} }

3
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -51,6 +51,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="Ast\AstBuilder.cs" /> <Compile Include="Ast\AstBuilder.cs" />
<Compile Include="Ast\AstMethodBodyBuilder.cs" /> <Compile Include="Ast\AstMethodBodyBuilder.cs" />
<Compile Include="Ast\CecilTypeResolveContext.cs" />
<Compile Include="Ast\CommentStatement.cs" /> <Compile Include="Ast\CommentStatement.cs" />
<Compile Include="Ast\DeclareVariableInSmallestScope.cs" /> <Compile Include="Ast\DeclareVariableInSmallestScope.cs" />
<Compile Include="Ast\CSharpCodeMapping.cs" /> <Compile Include="Ast\CSharpCodeMapping.cs" />
@ -62,6 +63,7 @@
<Compile Include="Ast\Transforms\ConvertConstructorCallIntoInitializer.cs" /> <Compile Include="Ast\Transforms\ConvertConstructorCallIntoInitializer.cs" />
<Compile Include="Ast\Transforms\DelegateConstruction.cs" /> <Compile Include="Ast\Transforms\DelegateConstruction.cs" />
<Compile Include="Ast\Transforms\IntroduceUnsafeModifier.cs" /> <Compile Include="Ast\Transforms\IntroduceUnsafeModifier.cs" />
<Compile Include="Ast\Transforms\IntroduceUsingDeclarations.cs" />
<Compile Include="Ast\Transforms\ReplaceMethodCallsWithOperators.cs" /> <Compile Include="Ast\Transforms\ReplaceMethodCallsWithOperators.cs" />
<Compile Include="Ast\Transforms\PushNegation.cs" /> <Compile Include="Ast\Transforms\PushNegation.cs" />
<Compile Include="Ast\Transforms\TransformationPipeline.cs" /> <Compile Include="Ast\Transforms\TransformationPipeline.cs" />
@ -100,6 +102,7 @@
<Compile Include="ILAst\LoopsAndConditions.cs" /> <Compile Include="ILAst\LoopsAndConditions.cs" />
<Compile Include="ILAst\PatternMatching.cs" /> <Compile Include="ILAst\PatternMatching.cs" />
<Compile Include="ILAst\PeepholeTransform.cs" /> <Compile Include="ILAst\PeepholeTransform.cs" />
<Compile Include="ILAst\SimpleControlFlow.cs" />
<Compile Include="ILAst\TypeAnalysis.cs" /> <Compile Include="ILAst\TypeAnalysis.cs" />
<Compile Include="ILAst\YieldReturnDecompiler.cs" /> <Compile Include="ILAst\YieldReturnDecompiler.cs" />
<Compile Include="ITextOutput.cs" /> <Compile Include="ITextOutput.cs" />

5
ICSharpCode.Decompiler/ILAst/GotoRemoval.cs

@ -65,8 +65,9 @@ namespace ICSharpCode.Decompiler.ILAst
int count = ilCase.Body.Count; int count = ilCase.Body.Count;
if (count >= 2) { if (count >= 2) {
if (!ilCase.Body[count - 2].CanFallThough() && if (ilCase.Body[count - 2].IsUnconditionalControlFlow() &&
ilCase.Body[count - 1].Match(ILCode.LoopOrSwitchBreak)) { ilCase.Body[count - 1].Match(ILCode.LoopOrSwitchBreak))
{
ilCase.Body.RemoveAt(count - 1); ilCase.Body.RemoveAt(count - 1);
} }
} }

7
ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs

@ -316,7 +316,7 @@ namespace ICSharpCode.Decompiler.ILAst
// Find all successors // Find all successors
List<ByteCode> branchTargets = new List<ByteCode>(); List<ByteCode> branchTargets = new List<ByteCode>();
if (byteCode.Code.CanFallThough()) { if (!byteCode.Code.IsUnconditionalControlFlow()) {
branchTargets.Add(byteCode.Next); branchTargets.Add(byteCode.Next);
} }
if (byteCode.Operand is Instruction[]) { if (byteCode.Operand is Instruction[]) {
@ -695,10 +695,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILRange ilRange = new ILRange() { From = byteCode.Offset, To = byteCode.EndOffset }; ILRange ilRange = new ILRange() { From = byteCode.Offset, To = byteCode.EndOffset };
if (byteCode.StackBefore == null) { if (byteCode.StackBefore == null) {
ast.Add(new ILComment() { // Unreachable code
Text = "Unreachable code: " + byteCode.Code.GetName(),
ILRanges = new List<ILRange>(new[] { ilRange })
});
continue; continue;
} }

364
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -21,6 +21,7 @@ namespace ICSharpCode.Decompiler.ILAst
SimplifyShortCircuit, SimplifyShortCircuit,
SimplifyTernaryOperator, SimplifyTernaryOperator,
SimplifyNullCoalescing, SimplifyNullCoalescing,
JoinBasicBlocks,
TransformDecimalCtorToConstant, TransformDecimalCtorToConstant,
SimplifyLdObjAndStObj, SimplifyLdObjAndStObj,
TransformArrayInitializers, TransformArrayInitializers,
@ -86,55 +87,45 @@ namespace ICSharpCode.Decompiler.ILAst
// Types are needed for the ternary operator optimization // Types are needed for the ternary operator optimization
TypeAnalysis.Run(context, method); TypeAnalysis.Run(context, method);
AnalyseLabels(method);
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
bool modified; bool modified;
do { do {
modified = false; modified = false;
if (abortBeforeStep == ILAstOptimizationStep.SimplifyShortCircuit) return; if (abortBeforeStep == ILAstOptimizationStep.SimplifyShortCircuit) return;
modified |= block.RunOptimization(SimplifyShortCircuit); modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyShortCircuit);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyTernaryOperator) return; if (abortBeforeStep == ILAstOptimizationStep.SimplifyTernaryOperator) return;
modified |= block.RunOptimization(SimplifyTernaryOperator); modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyTernaryOperator);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyNullCoalescing) return; if (abortBeforeStep == ILAstOptimizationStep.SimplifyNullCoalescing) return;
modified |= block.RunOptimization(SimplifyNullCoalescing); modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyNullCoalescing);
if (abortBeforeStep == ILAstOptimizationStep.JoinBasicBlocks) return;
modified |= block.RunOptimization(new SimpleControlFlow(context, method).JoinBasicBlocks);
if (abortBeforeStep == ILAstOptimizationStep.TransformDecimalCtorToConstant) return;
modified |= block.RunOptimization(TransformDecimalCtorToConstant);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyLdObjAndStObj) return;
modified |= block.RunOptimization(SimplifyLdObjAndStObj);
if (abortBeforeStep == ILAstOptimizationStep.TransformArrayInitializers) return;
modified |= block.RunOptimization(Initializers.TransformArrayInitializers);
if (abortBeforeStep == ILAstOptimizationStep.TransformCollectionInitializers) return;
modified |= block.RunOptimization(Initializers.TransformCollectionInitializers);
if (abortBeforeStep == ILAstOptimizationStep.MakeAssignmentExpression) return;
modified |= block.RunOptimization(MakeAssignmentExpression);
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return;
modified |= new ILInlining(method).InlineAllInBlock(block);
new ILInlining(method).CopyPropagation();
} while(modified); } while(modified);
} }
ILInlining inlining2 = new ILInlining(method);
inlining2.InlineAllVariables();
inlining2.CopyPropagation();
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
// Intentionaly outside the while(modifed) loop,
// I will put it there later after more testing
bool modified = false;
if (abortBeforeStep == ILAstOptimizationStep.TransformDecimalCtorToConstant) return;
modified |= block.RunOptimization(TransformDecimalCtorToConstant);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyLdObjAndStObj) return;
modified |= block.RunOptimization(SimplifyLdObjAndStObj);
if (abortBeforeStep == ILAstOptimizationStep.TransformArrayInitializers) return;
modified |= block.RunOptimization(Initializers.TransformArrayInitializers);
modified |= block.RunOptimization(Initializers.TransformArrayInitializers);
if (abortBeforeStep == ILAstOptimizationStep.TransformCollectionInitializers) return;
modified |= block.RunOptimization(Initializers.TransformCollectionInitializers);
if (abortBeforeStep == ILAstOptimizationStep.MakeAssignmentExpression) return;
modified |= block.RunOptimization(MakeAssignmentExpression);
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return;
modified |= new ILInlining(method).InlineAllInBlock(block);
}
if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return; if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
new LoopsAndConditions(context).FindLoops(block); new LoopsAndConditions(context).FindLoops(block);
@ -175,9 +166,17 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.IntroduceFixedStatements) return; if (abortBeforeStep == ILAstOptimizationStep.IntroduceFixedStatements) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count; i++) { for (int i = block.Body.Count - 1; i >= 0; i--) {
// TODO: Move before loops
if (i < block.Body.Count)
IntroduceFixedStatements(block.Body, i);
}
}
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = block.Body.Count - 1; i >= 0; i--) {
// TODO: Move before loops // TODO: Move before loops
IntroduceFixedStatements(block.Body, i); if (i < block.Body.Count)
IntroduceFixedStatements(block.Body, i);
} }
} }
@ -286,14 +285,14 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
List<ILNode> basicBlocks = new List<ILNode>(); List<ILNode> basicBlocks = new List<ILNode>();
ILBasicBlock basicBlock = new ILBasicBlock() { ILLabel entryLabel = block.Body.FirstOrDefault() as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++) };
EntryLabel = block.Body.FirstOrDefault() as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++) } ILBasicBlock basicBlock = new ILBasicBlock();
};
basicBlocks.Add(basicBlock); basicBlocks.Add(basicBlock);
block.EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel); basicBlock.Body.Add(entryLabel);
block.EntryGoto = new ILExpression(ILCode.Br, entryLabel);
if (block.Body.Count > 0) { if (block.Body.Count > 0) {
if (block.Body[0] != basicBlock.EntryLabel) if (block.Body[0] != entryLabel)
basicBlock.Body.Add(block.Body[0]); basicBlock.Body.Add(block.Body[0]);
for (int i = 1; i < block.Body.Count; i++) { for (int i = 1; i < block.Body.Count; i++) {
@ -302,31 +301,28 @@ namespace ICSharpCode.Decompiler.ILAst
// Start a new basic block if necessary // Start a new basic block if necessary
if (currNode is ILLabel || if (currNode is ILLabel ||
lastNode is ILTryCatchBlock || currNode is ILTryCatchBlock || // Counts as label
currNode is ILTryCatchBlock || lastNode.IsConditionalControlFlow() ||
(lastNode is ILExpression && ((ILExpression)lastNode).IsBranch())) lastNode.IsUnconditionalControlFlow())
{ {
// Try to reuse the label // Try to reuse the label
ILLabel label = currNode is ILLabel ? ((ILLabel)currNode) : new ILLabel() { Name = "Block_" + (nextLabelIndex++) }; ILLabel label = currNode is ILLabel ? ((ILLabel)currNode) : new ILLabel() { Name = "Block_" + (nextLabelIndex++) };
// Terminate the last block // Terminate the last block
if (lastNode.CanFallThough()) { if (!lastNode.IsUnconditionalControlFlow()) {
// Explicit branch from one block to other // Explicit branch from one block to other
basicBlock.FallthoughGoto = new ILExpression(ILCode.Br, label); basicBlock.Body.Add(new ILExpression(ILCode.Br, label));
} else if (lastNode.Match(ILCode.Br)) {
// Reuse the existing goto as FallthoughGoto
basicBlock.FallthoughGoto = (ILExpression)lastNode;
basicBlock.Body.RemoveAt(basicBlock.Body.Count - 1);
} }
// Start the new block // Start the new block
basicBlock = new ILBasicBlock(); basicBlock = new ILBasicBlock();
basicBlocks.Add(basicBlock); basicBlocks.Add(basicBlock);
basicBlock.EntryLabel = label; basicBlock.Body.Add(label);
}
// Add the node to the basic block
// Add the node to the basic block if (currNode != label)
if (currNode != basicBlock.EntryLabel) { basicBlock.Body.Add(currNode);
} else {
basicBlock.Body.Add(currNode); basicBlock.Body.Add(currNode);
} }
} }
@ -336,208 +332,6 @@ namespace ICSharpCode.Decompiler.ILAst
return; return;
} }
Dictionary<ILLabel, int> labelGlobalRefCount;
Dictionary<ILLabel, ILBasicBlock> labelToBasicBlock;
void AnalyseLabels(ILBlock method)
{
labelGlobalRefCount = new Dictionary<ILLabel, int>();
foreach(ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) {
if (!labelGlobalRefCount.ContainsKey(target))
labelGlobalRefCount[target] = 0;
labelGlobalRefCount[target]++;
}
labelToBasicBlock = new Dictionary<ILLabel, ILBasicBlock>();
foreach(ILBasicBlock bb in method.GetSelfAndChildrenRecursive<ILBasicBlock>()) {
foreach(ILLabel label in bb.GetChildren().OfType<ILLabel>()) {
labelToBasicBlock[label] = bb;
}
}
}
bool SimplifyTernaryOperator(List<ILNode> body, ILBasicBlock head, int pos)
{
Debug.Assert(body.Contains(head));
ILExpression condExpr;
ILLabel trueLabel;
ILLabel falseLabel;
ILVariable trueLocVar = null;
ILExpression trueExpr;
ILLabel trueFall;
ILVariable falseLocVar = null;
ILExpression falseExpr;
ILLabel falseFall;
object unused;
if (head.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) &&
labelGlobalRefCount[trueLabel] == 1 &&
labelGlobalRefCount[falseLabel] == 1 &&
((labelToBasicBlock[trueLabel].MatchSingle(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) &&
labelToBasicBlock[falseLabel].MatchSingle(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall)) ||
(labelToBasicBlock[trueLabel].MatchSingle(ILCode.Ret, out unused, out trueExpr, out trueFall) &&
labelToBasicBlock[falseLabel].MatchSingle(ILCode.Ret, out unused, out falseExpr, out falseFall))) &&
trueLocVar == falseLocVar &&
trueFall == falseFall &&
body.Contains(labelToBasicBlock[trueLabel]) &&
body.Contains(labelToBasicBlock[falseLabel])
)
{
ILCode opCode = trueLocVar != null ? ILCode.Stloc : ILCode.Ret;
TypeReference retType = trueLocVar != null ? trueLocVar.Type : this.context.CurrentMethod.ReturnType;
int leftBoolVal;
int rightBoolVal;
ILExpression newExpr;
// a ? true:false is equivalent to a
// a ? false:true is equivalent to !a
// a ? true : b is equivalent to a || b
// a ? b : true is equivalent to !a || b
// a ? b : false is equivalent to a && b
// a ? false : b is equivalent to !a && b
if (retType == typeSystem.Boolean &&
trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) &&
falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) &&
((leftBoolVal != 0 && rightBoolVal == 0) || (leftBoolVal == 0 && rightBoolVal != 0))
)
{
// It can be expressed as trivilal expression
if (leftBoolVal != 0) {
newExpr = condExpr;
} else {
newExpr = new ILExpression(ILCode.LogicNot, null, condExpr);
}
} else if (retType == typeSystem.Boolean && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal)) {
// It can be expressed as logical expression
if (leftBoolVal != 0) {
newExpr = new ILExpression(ILCode.LogicOr, null, condExpr, falseExpr);
} else {
newExpr = new ILExpression(ILCode.LogicAnd, null, new ILExpression(ILCode.LogicNot, null, condExpr), falseExpr);
}
} else if (retType == typeSystem.Boolean && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal)) {
// It can be expressed as logical expression
if (rightBoolVal != 0) {
newExpr = new ILExpression(ILCode.LogicOr, null, new ILExpression(ILCode.LogicNot, null, condExpr), trueExpr);
} else {
newExpr = new ILExpression(ILCode.LogicAnd, null, condExpr, trueExpr);
}
} else {
// Ternary operator tends to create long complicated return statements
if (opCode == ILCode.Ret)
return false;
// Create ternary expression
newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr);
}
head.Body[head.Body.Count - 1] = new ILExpression(opCode, trueLocVar, newExpr);
head.FallthoughGoto = trueFall != null ? new ILExpression(ILCode.Br, trueFall) : null;
// Remove the old basic blocks
foreach(ILLabel deleteLabel in new [] { trueLabel, falseLabel }) {
body.RemoveOrThrow(labelToBasicBlock[deleteLabel]);
labelGlobalRefCount.RemoveOrThrow(deleteLabel);
labelToBasicBlock.RemoveOrThrow(deleteLabel);
}
return true;
}
return false;
}
bool SimplifyNullCoalescing(List<ILNode> body, ILBasicBlock head, int pos)
{
// ...
// v = ldloc(leftVar)
// brtrue(endBBLabel, ldloc(leftVar))
// br(rightBBLabel)
//
// rightBBLabel:
// v = rightExpr
// br(endBBLabel)
// ...
// =>
// ...
// v = NullCoalescing(ldloc(leftVar), rightExpr)
// br(endBBLabel)
ILVariable v, v2;
ILExpression leftExpr, leftExpr2;
ILVariable leftVar;
ILLabel endBBLabel, endBBLabel2;
ILLabel rightBBLabel;
ILBasicBlock rightBB;
ILExpression rightExpr;
if (head.Body.Count >= 2 &&
head.Body[head.Body.Count - 2].Match(ILCode.Stloc, out v, out leftExpr) &&
leftExpr.Match(ILCode.Ldloc, out leftVar) &&
head.MatchLast(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) &&
leftExpr2.Match(ILCode.Ldloc, leftVar) &&
labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) &&
rightBB.MatchSingle(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) &&
v == v2 &&
endBBLabel == endBBLabel2 &&
labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 &&
body.Contains(rightBB)
)
{
head.Body[head.Body.Count - 2] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr));
head.Body.RemoveAt(head.Body.Count - 1);
head.FallthoughGoto = new ILExpression(ILCode.Br, endBBLabel);
body.RemoveOrThrow(labelToBasicBlock[rightBBLabel]);
labelGlobalRefCount.RemoveOrThrow(rightBBLabel);
labelToBasicBlock.RemoveOrThrow(rightBBLabel);
return true;
}
return false;
}
bool SimplifyShortCircuit(List<ILNode> body, ILBasicBlock head, int pos)
{
Debug.Assert(body.Contains(head));
ILExpression condExpr;
ILLabel trueLabel;
ILLabel falseLabel;
if(head.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) {
for (int pass = 0; pass < 2; pass++) {
// On the second pass, swap labels and negate expression of the first branch
// It is slightly ugly, but much better then copy-pasting this whole block
ILLabel nextLabel = (pass == 0) ? trueLabel : falseLabel;
ILLabel otherLablel = (pass == 0) ? falseLabel : trueLabel;
bool negate = (pass == 1);
ILBasicBlock nextBasicBlock = labelToBasicBlock[nextLabel];
ILExpression nextCondExpr;
ILLabel nextTrueLablel;
ILLabel nextFalseLabel;
if (body.Contains(nextBasicBlock) &&
nextBasicBlock != head &&
labelGlobalRefCount[nextBasicBlock.EntryLabel] == 1 &&
nextBasicBlock.MatchSingle(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) &&
(otherLablel == nextFalseLabel || otherLablel == nextTrueLablel))
{
// Create short cicuit branch
if (otherLablel == nextFalseLabel) {
head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Brtrue, nextTrueLablel, new ILExpression(ILCode.LogicAnd, null, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr));
} else {
head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Brtrue, nextTrueLablel, new ILExpression(ILCode.LogicOr, null, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr));
}
head.FallthoughGoto = new ILExpression(ILCode.Br, nextFalseLabel);
// Remove the inlined branch from scope
labelGlobalRefCount.RemoveOrThrow(nextBasicBlock.EntryLabel);
labelToBasicBlock.RemoveOrThrow(nextBasicBlock.EntryLabel);
body.RemoveOrThrow(nextBasicBlock);
return true;
}
}
}
return false;
}
void DuplicateReturnStatements(ILBlock method) void DuplicateReturnStatements(ILBlock method)
{ {
Dictionary<ILLabel, ILNode> nextSibling = new Dictionary<ILLabel, ILNode>(); Dictionary<ILLabel, ILNode> nextSibling = new Dictionary<ILLabel, ILNode>();
@ -598,8 +392,13 @@ namespace ICSharpCode.Decompiler.ILAst
List<ILNode> flatBody = new List<ILNode>(); List<ILNode> flatBody = new List<ILNode>();
foreach (ILNode child in block.GetChildren()) { foreach (ILNode child in block.GetChildren()) {
FlattenBasicBlocks(child); FlattenBasicBlocks(child);
if (child is ILBasicBlock) { ILBasicBlock childAsBB = child as ILBasicBlock;
flatBody.AddRange(child.GetChildren()); if (childAsBB != null) {
if (!(childAsBB.Body.FirstOrDefault() is ILLabel))
throw new Exception("Basic block has to start with a label. \n" + childAsBB.ToString());
if (childAsBB.Body.LastOrDefault() is ILExpression && !childAsBB.Body.LastOrDefault().IsUnconditionalControlFlow())
throw new Exception("Basci block has to end with unconditional control flow. \n" + childAsBB.ToString());
flatBody.AddRange(childAsBB.GetChildren());
} else { } else {
flatBody.Add(child); flatBody.Add(child);
} }
@ -627,8 +426,8 @@ namespace ICSharpCode.Decompiler.ILAst
for (int i = 0; i < block.Body.Count; i++) { for (int i = 0; i < block.Body.Count; i++) {
ILCondition cond = block.Body[i] as ILCondition; ILCondition cond = block.Body[i] as ILCondition;
if (cond != null) { if (cond != null) {
bool trueExits = cond.TrueBlock.Body.Count > 0 && !cond.TrueBlock.Body.Last().CanFallThough(); bool trueExits = cond.TrueBlock.Body.LastOrDefault().IsUnconditionalControlFlow();
bool falseExits = cond.FalseBlock.Body.Count > 0 && !cond.FalseBlock.Body.Last().CanFallThough(); bool falseExits = cond.FalseBlock.Body.LastOrDefault().IsUnconditionalControlFlow();
if (trueExits) { if (trueExits) {
// Move the false block after the condition // Move the false block after the condition
@ -677,12 +476,9 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
bool modified = false; bool modified = false;
List<ILNode> body = block.Body; List<ILNode> body = block.Body;
for (int i = 0; i < body.Count;) { for (int i = body.Count - 1; i >= 0; i--) {
if (optimization(body, (ILBasicBlock)body[i], i)) { if (i < body.Count && optimization(body, (ILBasicBlock)body[i], i)) {
modified = true; modified = true;
i = Math.Max(0, i - 1); // Go back one step
} else {
i++;
} }
} }
return modified; return modified;
@ -692,26 +488,26 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
bool modified = false; bool modified = false;
foreach (ILBasicBlock bb in block.Body) { foreach (ILBasicBlock bb in block.Body) {
for (int j = 0; j < bb.Body.Count;) { for (int i = bb.Body.Count - 1; i >= 0; i--) {
ILExpression expr = bb.Body[j] as ILExpression; ILExpression expr = bb.Body.ElementAtOrDefault(i) as ILExpression;
if (expr != null && optimization(bb.Body, expr, j)) { if (expr != null && optimization(bb.Body, expr, i)) {
modified = true; modified = true;
j = Math.Max(0, j - 1); // Go back one step
} else {
j++;
} }
} }
} }
return modified; return modified;
} }
public static bool CanFallThough(this ILNode node) public static bool IsConditionalControlFlow(this ILNode node)
{ {
ILExpression expr = node as ILExpression; ILExpression expr = node as ILExpression;
if (expr != null) { return expr != null && expr.Code.IsConditionalControlFlow();
return expr.Code.CanFallThough(); }
}
return true; public static bool IsUnconditionalControlFlow(this ILNode node)
{
ILExpression expr = node as ILExpression;
return expr != null && expr.Code.IsUnconditionalControlFlow();
} }
/// <summary> /// <summary>
@ -768,12 +564,22 @@ namespace ICSharpCode.Decompiler.ILAst
return !mr.Name.StartsWith("get_", StringComparison.Ordinal); return !mr.Name.StartsWith("get_", StringComparison.Ordinal);
case ILCode.Newobj: case ILCode.Newobj:
case ILCode.Newarr: case ILCode.Newarr:
case ILCode.Stloc:
return true; return true;
default: default:
return false; return false;
} }
} }
public static void RemoveTail(this List<ILNode> body, params ILCode[] codes)
{
for (int i = 0; i < codes.Length; i++) {
if (((ILExpression)body[body.Count - codes.Length + i]).Code != codes[i])
throw new Exception("Tailing code does not match expected.");
}
body.RemoveRange(body.Count - codes.Length, codes.Length);
}
public static V GetOrDefault<K,V>(this Dictionary<K, V> dict, K key) public static V GetOrDefault<K,V>(this Dictionary<K, V> dict, K key)
{ {
V ret; V ret;

28
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -85,19 +85,12 @@ namespace ICSharpCode.Decompiler.ILAst
public class ILBasicBlock: ILNode public class ILBasicBlock: ILNode
{ {
public ILLabel EntryLabel; /// <remarks> Body has to start with a label and end with unconditional control flow </remarks>
public List<ILNode> Body = new List<ILNode>(); public List<ILNode> Body = new List<ILNode>();
public ILExpression FallthoughGoto;
public override IEnumerable<ILNode> GetChildren() public override IEnumerable<ILNode> GetChildren()
{ {
if (this.EntryLabel != null) return this.Body;
yield return this.EntryLabel;
foreach (ILNode child in this.Body) {
yield return child;
}
if (this.FallthoughGoto != null)
yield return this.FallthoughGoto;
} }
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
@ -119,17 +112,6 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} }
public class ILComment: ILNode
{
public string Text;
public List<ILRange> ILRanges { get; set; }
public override void WriteTo(ITextOutput output)
{
output.WriteLine("// " + this.Text);
}
}
public class ILTryCatchBlock: ILNode public class ILTryCatchBlock: ILNode
{ {
public class CatchBlock: ILBlock public class CatchBlock: ILBlock
@ -290,6 +272,9 @@ namespace ICSharpCode.Decompiler.ILAst
public ILExpression(ILCode code, object operand, List<ILExpression> args) public ILExpression(ILCode code, object operand, List<ILExpression> args)
{ {
if (operand is ILExpression)
throw new ArgumentException("operand");
this.Code = code; this.Code = code;
this.Operand = operand; this.Operand = operand;
this.Arguments = new List<ILExpression>(args); this.Arguments = new List<ILExpression>(args);
@ -298,6 +283,9 @@ namespace ICSharpCode.Decompiler.ILAst
public ILExpression(ILCode code, object operand, params ILExpression[] args) public ILExpression(ILCode code, object operand, params ILExpression[] args)
{ {
if (operand is ILExpression)
throw new ArgumentException("operand");
this.Code = code; this.Code = code;
this.Operand = operand; this.Operand = operand;
this.Arguments = new List<ILExpression>(args); this.Arguments = new List<ILExpression>(args);

40
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -280,7 +280,41 @@ namespace ICSharpCode.Decompiler.ILAst
return code.ToString().ToLowerInvariant().TrimStart('_').Replace('_','.'); return code.ToString().ToLowerInvariant().TrimStart('_').Replace('_','.');
} }
public static bool CanFallThough(this ILCode code) public static bool IsConditionalControlFlow(this ILCode code)
{
switch(code) {
case ILCode.__Brfalse_S:
case ILCode.__Brtrue_S:
case ILCode.__Beq_S:
case ILCode.__Bge_S:
case ILCode.__Bgt_S:
case ILCode.__Ble_S:
case ILCode.__Blt_S:
case ILCode.__Bne_Un_S:
case ILCode.__Bge_Un_S:
case ILCode.__Bgt_Un_S:
case ILCode.__Ble_Un_S:
case ILCode.__Blt_Un_S:
case ILCode.__Brfalse:
case ILCode.Brtrue:
case ILCode.__Beq:
case ILCode.__Bge:
case ILCode.__Bgt:
case ILCode.__Ble:
case ILCode.__Blt:
case ILCode.__Bne_Un:
case ILCode.__Bge_Un:
case ILCode.__Bgt_Un:
case ILCode.__Ble_Un:
case ILCode.__Blt_Un:
case ILCode.Switch:
return true;
default:
return false;
}
}
public static bool IsUnconditionalControlFlow(this ILCode code)
{ {
switch(code) { switch(code) {
case ILCode.Br: case ILCode.Br:
@ -295,9 +329,9 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.LoopContinue: case ILCode.LoopContinue:
case ILCode.LoopOrSwitchBreak: case ILCode.LoopOrSwitchBreak:
case ILCode.YieldBreak: case ILCode.YieldBreak:
return false;
default:
return true; return true;
default:
return false;
} }
} }

2
ICSharpCode.Decompiler/ILAst/ILInlining.cs

@ -81,7 +81,7 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
bool modified = false; bool modified = false;
List<ILNode> body = bb.Body; List<ILNode> body = bb.Body;
for(int i = 0; i < body.Count - 1;) { for(int i = 0; i < body.Count;) {
ILVariable locVar; ILVariable locVar;
ILExpression expr; ILExpression expr;
if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(bb.Body, i, aggressive: false)) { if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(bb.Body, i, aggressive: false)) {

2
ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs

@ -162,7 +162,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (nextExpr.Match(ILCode.Callvirt, out addMethod, out args) && if (nextExpr.Match(ILCode.Callvirt, out addMethod, out args) &&
addMethod.Name == "Add" && addMethod.Name == "Add" &&
addMethod.HasThis && addMethod.HasThis &&
args.Count == 2 && args.Count >= 2 &&
args[0].Match(ILCode.Ldloc, out v2) && args[0].Match(ILCode.Ldloc, out v2) &&
v == v2) v == v2)
{ {

40
ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs

@ -88,7 +88,7 @@ namespace ICSharpCode.Decompiler.ILAst
ControlFlowNode destination; ControlFlowNode destination;
// Labels which are out of out scope will not be int the collection // Labels which are out of out scope will not be int the collection
// Insert self edge only if we are sure we are a loop // Insert self edge only if we are sure we are a loop
if (labelToCfNode.TryGetValue(target, out destination) && (destination != source || target == node.EntryLabel)) { if (labelToCfNode.TryGetValue(target, out destination) && (destination != source || target == node.Body.FirstOrDefault())) {
ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal); ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal);
source.Outgoing.Add(edge); source.Outgoing.Add(edge);
destination.Incoming.Add(edge); destination.Incoming.Add(edge);
@ -123,7 +123,8 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression condExpr; ILExpression condExpr;
ILLabel trueLabel; ILLabel trueLabel;
ILLabel falseLabel; ILLabel falseLabel;
if(basicBlock.MatchSingle(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) // It has to be just brtrue - any preceding code would introduce goto
if(basicBlock.MatchSingleAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel))
{ {
ControlFlowNode trueTarget; ControlFlowNode trueTarget;
labelToCfNode.TryGetValue(trueLabel, out trueTarget); labelToCfNode.TryGetValue(trueLabel, out trueTarget);
@ -157,7 +158,7 @@ namespace ICSharpCode.Decompiler.ILAst
} }
// Use loop to implement the brtrue // Use loop to implement the brtrue
basicBlock.Body.RemoveAt(basicBlock.Body.Count - 1); basicBlock.Body.RemoveTail(ILCode.Brtrue, ILCode.Br);
basicBlock.Body.Add(new ILWhileLoop() { basicBlock.Body.Add(new ILWhileLoop() {
Condition = condExpr, Condition = condExpr,
BodyBlock = new ILBlock() { BodyBlock = new ILBlock() {
@ -165,7 +166,7 @@ namespace ICSharpCode.Decompiler.ILAst
Body = FindLoops(loopContents, node, false) Body = FindLoops(loopContents, node, false)
} }
}); });
basicBlock.FallthoughGoto = new ILExpression(ILCode.Br, falseLabel); basicBlock.Body.Add(new ILExpression(ILCode.Br, falseLabel));
result.Add(basicBlock); result.Add(basicBlock);
scope.ExceptWith(loopContents); scope.ExceptWith(loopContents);
@ -175,16 +176,15 @@ namespace ICSharpCode.Decompiler.ILAst
// Fallback method: while(true) // Fallback method: while(true)
if (scope.Contains(node)) { if (scope.Contains(node)) {
result.Add(new ILBasicBlock() { result.Add(new ILBasicBlock() {
EntryLabel = new ILLabel() { Name = "Loop_" + (nextLabelIndex++) },
Body = new List<ILNode>() { Body = new List<ILNode>() {
new ILLabel() { Name = "Loop_" + (nextLabelIndex++) },
new ILWhileLoop() { new ILWhileLoop() {
BodyBlock = new ILBlock() { BodyBlock = new ILBlock() {
EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel), EntryGoto = new ILExpression(ILCode.Br, (ILLabel)basicBlock.Body.First()),
Body = FindLoops(loopContents, node, true) Body = FindLoops(loopContents, node, true)
} }
}, },
}, },
FallthoughGoto = null
}); });
scope.ExceptWith(loopContents); scope.ExceptWith(loopContents);
@ -233,12 +233,13 @@ namespace ICSharpCode.Decompiler.ILAst
ILLabel[] caseLabels; ILLabel[] caseLabels;
ILExpression switchArg; ILExpression switchArg;
ILLabel fallLabel; ILLabel fallLabel;
if (block.MatchLast(ILCode.Switch, out caseLabels, out switchArg, out fallLabel)) { if (block.MatchLastAndBr(ILCode.Switch, out caseLabels, out switchArg, out fallLabel)) {
// Replace the switch code with ILSwitch // Replace the switch code with ILSwitch
ILSwitch ilSwitch = new ILSwitch() { Condition = switchArg }; ILSwitch ilSwitch = new ILSwitch() { Condition = switchArg };
block.Body.RemoveAt(block.Body.Count - 1); block.Body.RemoveTail(ILCode.Switch, ILCode.Br);
block.Body.Add(ilSwitch); block.Body.Add(ilSwitch);
block.Body.Add(new ILExpression(ILCode.Br, fallLabel));
result.Add(block); result.Add(block);
// Remove the item so that it is not picked up as content // Remove the item so that it is not picked up as content
@ -285,7 +286,12 @@ namespace ICSharpCode.Decompiler.ILAst
scope.ExceptWith(content); scope.ExceptWith(content);
caseBlock.Body.AddRange(FindConditions(content, condTarget)); caseBlock.Body.AddRange(FindConditions(content, condTarget));
// Add explicit break which should not be used by default, but the goto removal might decide to use it // Add explicit break which should not be used by default, but the goto removal might decide to use it
caseBlock.Body.Add(new ILBasicBlock() { Body = { new ILExpression(ILCode.LoopOrSwitchBreak, null) } }); caseBlock.Body.Add(new ILBasicBlock() {
Body = {
new ILLabel() { Name = "SwitchBreak_" + (nextLabelIndex++) },
new ILExpression(ILCode.LoopOrSwitchBreak, null)
}
});
} }
} }
caseBlock.Values.Add(i + addValue); caseBlock.Values.Add(i + addValue);
@ -297,12 +303,17 @@ namespace ICSharpCode.Decompiler.ILAst
if (content.Any()) { if (content.Any()) {
var caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, fallLabel) }; var caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, fallLabel) };
ilSwitch.CaseBlocks.Add(caseBlock); ilSwitch.CaseBlocks.Add(caseBlock);
block.FallthoughGoto = null; block.Body.RemoveTail(ILCode.Br);
scope.ExceptWith(content); scope.ExceptWith(content);
caseBlock.Body.AddRange(FindConditions(content, fallTarget)); caseBlock.Body.AddRange(FindConditions(content, fallTarget));
// Add explicit break which should not be used by default, but the goto removal might decide to use it // Add explicit break which should not be used by default, but the goto removal might decide to use it
caseBlock.Body.Add(new ILBasicBlock() { Body = { new ILExpression(ILCode.LoopOrSwitchBreak, null) } }); caseBlock.Body.Add(new ILBasicBlock() {
Body = {
new ILLabel() { Name = "SwitchBreak_" + (nextLabelIndex++) },
new ILExpression(ILCode.LoopOrSwitchBreak, null)
}
});
} }
} }
} }
@ -311,7 +322,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression condExpr; ILExpression condExpr;
ILLabel trueLabel; ILLabel trueLabel;
ILLabel falseLabel; ILLabel falseLabel;
if(block.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { if(block.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) {
// Swap bodies since that seems to be the usual C# order // Swap bodies since that seems to be the usual C# order
ILLabel temp = trueLabel; ILLabel temp = trueLabel;
@ -325,9 +336,8 @@ namespace ICSharpCode.Decompiler.ILAst
TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) }, TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) },
FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, falseLabel) } FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, falseLabel) }
}; };
block.Body.RemoveAt(block.Body.Count - 1); block.Body.RemoveTail(ILCode.Brtrue, ILCode.Br);
block.Body.Add(ilCond); block.Body.Add(ilCond);
block.FallthoughGoto = null;
result.Add(block); result.Add(block);
// Remove the item immediately so that it is not picked up as content // Remove the item immediately so that it is not picked up as content

37
ICSharpCode.Decompiler/ILAst/PatternMatching.cs

@ -98,29 +98,44 @@ namespace ICSharpCode.Decompiler.ILAst
return false; return false;
} }
public static bool MatchSingle<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel fallLabel) public static bool MatchSingle<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg)
{ {
if (bb.Body.Count == 1) { if (bb.Body.Count == 2 &&
if (bb.Body[0].Match(code, out operand, out arg)) { bb.Body[0] is ILLabel &&
fallLabel = bb.FallthoughGoto != null ? (ILLabel)bb.FallthoughGoto.Operand : null; bb.Body[1].Match(code, out operand, out arg))
return true; {
} return true;
}
operand = default(T);
arg = null;
return false;
}
public static bool MatchSingleAndBr<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel brLabel)
{
if (bb.Body.Count == 3 &&
bb.Body[0] is ILLabel &&
bb.Body[1].Match(code, out operand, out arg) &&
bb.Body[2].Match(ILCode.Br, out brLabel))
{
return true;
} }
operand = default(T); operand = default(T);
arg = null; arg = null;
fallLabel = null; brLabel = null;
return false; return false;
} }
public static bool MatchLast<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel fallLabel) public static bool MatchLastAndBr<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel brLabel)
{ {
if (bb.Body.LastOrDefault().Match(code, out operand, out arg)) { if (bb.Body.ElementAtOrDefault(bb.Body.Count - 2).Match(code, out operand, out arg) &&
fallLabel = bb.FallthoughGoto != null ? (ILLabel)bb.FallthoughGoto.Operand : null; bb.Body.LastOrDefault().Match(ILCode.Br, out brLabel))
{
return true; return true;
} }
operand = default(T); operand = default(T);
arg = null; arg = null;
fallLabel = null; brLabel = null;
return false; return false;
} }

7
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -55,10 +55,11 @@ namespace ICSharpCode.Decompiler.ILAst
static bool SimplifyLdObjAndStObj(List<ILNode> body, ILExpression expr, int pos) static bool SimplifyLdObjAndStObj(List<ILNode> body, ILExpression expr, int pos)
{ {
bool modified = false;
if (expr.Code == ILCode.Initobj) { if (expr.Code == ILCode.Initobj) {
expr.Code = ILCode.Stobj; expr.Code = ILCode.Stobj;
expr.Arguments.Add(new ILExpression(ILCode.DefaultValue, expr.Operand)); expr.Arguments.Add(new ILExpression(ILCode.DefaultValue, expr.Operand));
return true; modified = true;
} }
ILExpression arg, arg2; ILExpression arg, arg2;
TypeReference type; TypeReference type;
@ -84,9 +85,9 @@ namespace ICSharpCode.Decompiler.ILAst
arg.Arguments.Add(arg2); arg.Arguments.Add(arg2);
arg.ILRanges.AddRange(expr.ILRanges); arg.ILRanges.AddRange(expr.ILRanges);
body[pos] = arg; body[pos] = arg;
return true; modified = true;
} }
return false; return modified;
} }
#region CachedDelegateInitialization #region CachedDelegateInitialization

259
ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs

@ -0,0 +1,259 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst
{
public class SimpleControlFlow
{
Dictionary<ILLabel, int> labelGlobalRefCount = new Dictionary<ILLabel, int>();
Dictionary<ILLabel, ILBasicBlock> labelToBasicBlock = new Dictionary<ILLabel, ILBasicBlock>();
DecompilerContext context;
TypeSystem typeSystem;
public SimpleControlFlow(DecompilerContext context, ILBlock method)
{
this.context = context;
this.typeSystem = context.CurrentMethod.Module.TypeSystem;
foreach(ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) {
labelGlobalRefCount[target] = labelGlobalRefCount.GetOrDefault(target) + 1;
}
foreach(ILBasicBlock bb in method.GetSelfAndChildrenRecursive<ILBasicBlock>()) {
foreach(ILLabel label in bb.GetChildren().OfType<ILLabel>()) {
labelToBasicBlock[label] = bb;
}
}
}
public bool SimplifyTernaryOperator(List<ILNode> body, ILBasicBlock head, int pos)
{
Debug.Assert(body.Contains(head));
ILExpression condExpr;
ILLabel trueLabel;
ILLabel falseLabel;
ILVariable trueLocVar = null;
ILExpression trueExpr;
ILLabel trueFall;
ILVariable falseLocVar = null;
ILExpression falseExpr;
ILLabel falseFall;
object unused;
if (head.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) &&
labelGlobalRefCount[trueLabel] == 1 &&
labelGlobalRefCount[falseLabel] == 1 &&
((labelToBasicBlock[trueLabel].MatchSingleAndBr(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) &&
labelToBasicBlock[falseLabel].MatchSingleAndBr(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall) &&
trueLocVar == falseLocVar && trueFall == falseFall) ||
(labelToBasicBlock[trueLabel].MatchSingle(ILCode.Ret, out unused, out trueExpr) &&
labelToBasicBlock[falseLabel].MatchSingle(ILCode.Ret, out unused, out falseExpr))) &&
body.Contains(labelToBasicBlock[trueLabel]) &&
body.Contains(labelToBasicBlock[falseLabel])
)
{
bool isStloc = trueLocVar != null;
ILCode opCode = isStloc ? ILCode.Stloc : ILCode.Ret;
TypeReference retType = isStloc ? trueLocVar.Type : this.context.CurrentMethod.ReturnType;
int leftBoolVal;
int rightBoolVal;
ILExpression newExpr;
// a ? true:false is equivalent to a
// a ? false:true is equivalent to !a
// a ? true : b is equivalent to a || b
// a ? b : true is equivalent to !a || b
// a ? b : false is equivalent to a && b
// a ? false : b is equivalent to !a && b
if (retType == typeSystem.Boolean &&
trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) &&
falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) &&
((leftBoolVal != 0 && rightBoolVal == 0) || (leftBoolVal == 0 && rightBoolVal != 0))
)
{
// It can be expressed as trivilal expression
if (leftBoolVal != 0) {
newExpr = condExpr;
} else {
newExpr = new ILExpression(ILCode.LogicNot, null, condExpr);
}
} else if (retType == typeSystem.Boolean && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal)) {
// It can be expressed as logical expression
if (leftBoolVal != 0) {
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, condExpr, falseExpr);
} else {
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, new ILExpression(ILCode.LogicNot, null, condExpr), falseExpr);
}
} else if (retType == typeSystem.Boolean && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal)) {
// It can be expressed as logical expression
if (rightBoolVal != 0) {
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, new ILExpression(ILCode.LogicNot, null, condExpr), trueExpr);
} else {
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, condExpr, trueExpr);
}
} else {
// Ternary operator tends to create long complicated return statements
if (opCode == ILCode.Ret)
return false;
// Only simplify generated variables
if (opCode == ILCode.Stloc && !trueLocVar.IsGenerated)
return false;
// Create ternary expression
newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr);
}
head.Body.RemoveTail(ILCode.Brtrue, ILCode.Br);
head.Body.Add(new ILExpression(opCode, trueLocVar, newExpr));
if (isStloc)
head.Body.Add(new ILExpression(ILCode.Br, trueFall));
// Remove the old basic blocks
body.RemoveOrThrow(labelToBasicBlock[trueLabel]);
body.RemoveOrThrow(labelToBasicBlock[falseLabel]);
return true;
}
return false;
}
public bool SimplifyNullCoalescing(List<ILNode> body, ILBasicBlock head, int pos)
{
// ...
// v = ldloc(leftVar)
// brtrue(endBBLabel, ldloc(leftVar))
// br(rightBBLabel)
//
// rightBBLabel:
// v = rightExpr
// br(endBBLabel)
// ...
// =>
// ...
// v = NullCoalescing(ldloc(leftVar), rightExpr)
// br(endBBLabel)
ILVariable v, v2;
ILExpression leftExpr, leftExpr2;
ILVariable leftVar;
ILLabel endBBLabel, endBBLabel2;
ILLabel rightBBLabel;
ILBasicBlock rightBB;
ILExpression rightExpr;
if (head.Body.Count >= 3 &&
head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out v, out leftExpr) &&
leftExpr.Match(ILCode.Ldloc, out leftVar) &&
head.MatchLastAndBr(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) &&
leftExpr2.Match(ILCode.Ldloc, leftVar) &&
labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) &&
rightBB.MatchSingleAndBr(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) &&
v == v2 &&
endBBLabel == endBBLabel2 &&
labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 &&
body.Contains(rightBB)
)
{
head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br);
head.Body.Add(new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr)));
head.Body.Add(new ILExpression(ILCode.Br, endBBLabel));
body.RemoveOrThrow(labelToBasicBlock[rightBBLabel]);
return true;
}
return false;
}
public bool SimplifyShortCircuit(List<ILNode> body, ILBasicBlock head, int pos)
{
Debug.Assert(body.Contains(head));
ILExpression condExpr;
ILLabel trueLabel;
ILLabel falseLabel;
if(head.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) {
for (int pass = 0; pass < 2; pass++) {
// On the second pass, swap labels and negate expression of the first branch
// It is slightly ugly, but much better then copy-pasting this whole block
ILLabel nextLabel = (pass == 0) ? trueLabel : falseLabel;
ILLabel otherLablel = (pass == 0) ? falseLabel : trueLabel;
bool negate = (pass == 1);
ILBasicBlock nextBasicBlock = labelToBasicBlock[nextLabel];
ILExpression nextCondExpr;
ILLabel nextTrueLablel;
ILLabel nextFalseLabel;
if (body.Contains(nextBasicBlock) &&
nextBasicBlock != head &&
labelGlobalRefCount[(ILLabel)nextBasicBlock.Body.First()] == 1 &&
nextBasicBlock.MatchSingleAndBr(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) &&
(otherLablel == nextFalseLabel || otherLablel == nextTrueLablel))
{
// Create short cicuit branch
ILExpression logicExpr;
if (otherLablel == nextFalseLabel) {
logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr);
} else {
logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr);
}
head.Body.RemoveTail(ILCode.Brtrue, ILCode.Br);
head.Body.Add(new ILExpression(ILCode.Brtrue, nextTrueLablel, logicExpr));
head.Body.Add(new ILExpression(ILCode.Br, nextFalseLabel));
// Remove the inlined branch from scope
body.RemoveOrThrow(nextBasicBlock);
return true;
}
}
}
return false;
}
ILExpression MakeLeftAssociativeShortCircuit(ILCode code, ILExpression left, ILExpression right)
{
// Assuming that the inputs are already left associative
if (right.Match(code)) {
// Find the leftmost logical expression
ILExpression current = right;
while(current.Arguments[0].Match(code))
current = current.Arguments[0];
current.Arguments[0] = new ILExpression(code, null, left, current.Arguments[0]);
return right;
} else {
return new ILExpression(code, null, left, right);
}
}
public bool JoinBasicBlocks(List<ILNode> body, ILBasicBlock head, int pos)
{
ILLabel nextLabel;
ILBasicBlock nextBB;
if (!head.Body.ElementAtOrDefault(head.Body.Count - 2).IsConditionalControlFlow() &&
head.Body.Last().Match(ILCode.Br, out nextLabel) &&
labelGlobalRefCount[nextLabel] == 1 &&
labelToBasicBlock.TryGetValue(nextLabel, out nextBB) &&
body.Contains(nextBB) &&
nextBB.Body.First() == nextLabel &&
!nextBB.Body.OfType<ILTryCatchBlock>().Any()
)
{
head.Body.RemoveTail(ILCode.Br);
nextBB.Body.RemoveAt(0); // Remove label
head.Body.AddRange(nextBB.Body);
body.RemoveOrThrow(nextBB);
return true;
}
return false;
}
}
}

5
ICSharpCode.Decompiler/Tests/UnsafeCode.cs

@ -74,4 +74,9 @@ public class UnsafeCode
} }
return PointerReferenceExpression((double*)a); return PointerReferenceExpression((double*)a);
} }
unsafe ~UnsafeCode()
{
PassPointerAsRefParameter(NullPointer);
}
} }

54
ILSpy/CSharpLanguage.cs

@ -82,6 +82,7 @@ namespace ICSharpCode.ILSpy
public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options) public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options)
{ {
WriteCommentLine(output, TypeToString(method.DeclaringType, includeNamespace: true));
AstBuilder codeDomBuilder = CreateAstBuilder(options, method.DeclaringType); AstBuilder codeDomBuilder = CreateAstBuilder(options, method.DeclaringType);
codeDomBuilder.AddMethod(method); codeDomBuilder.AddMethod(method);
codeDomBuilder.GenerateCode(output, transformAbortCondition); codeDomBuilder.GenerateCode(output, transformAbortCondition);
@ -89,6 +90,7 @@ namespace ICSharpCode.ILSpy
public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options) public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options)
{ {
WriteCommentLine(output, TypeToString(property.DeclaringType, includeNamespace: true));
AstBuilder codeDomBuilder = CreateAstBuilder(options, property.DeclaringType); AstBuilder codeDomBuilder = CreateAstBuilder(options, property.DeclaringType);
codeDomBuilder.AddProperty(property); codeDomBuilder.AddProperty(property);
codeDomBuilder.GenerateCode(output, transformAbortCondition); codeDomBuilder.GenerateCode(output, transformAbortCondition);
@ -96,6 +98,7 @@ namespace ICSharpCode.ILSpy
public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options) public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options)
{ {
WriteCommentLine(output, TypeToString(field.DeclaringType, includeNamespace: true));
AstBuilder codeDomBuilder = CreateAstBuilder(options, field.DeclaringType); AstBuilder codeDomBuilder = CreateAstBuilder(options, field.DeclaringType);
codeDomBuilder.AddField(field); codeDomBuilder.AddField(field);
codeDomBuilder.GenerateCode(output, transformAbortCondition); codeDomBuilder.GenerateCode(output, transformAbortCondition);
@ -103,6 +106,7 @@ namespace ICSharpCode.ILSpy
public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options) public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options)
{ {
WriteCommentLine(output, TypeToString(ev.DeclaringType, includeNamespace: true));
AstBuilder codeDomBuilder = CreateAstBuilder(options, ev.DeclaringType); AstBuilder codeDomBuilder = CreateAstBuilder(options, ev.DeclaringType);
codeDomBuilder.AddEvent(ev); codeDomBuilder.AddEvent(ev);
codeDomBuilder.GenerateCode(output, transformAbortCondition); codeDomBuilder.GenerateCode(output, transformAbortCondition);
@ -117,26 +121,15 @@ namespace ICSharpCode.ILSpy
public override void DecompileAssembly(AssemblyDefinition assembly, string fileName, ITextOutput output, DecompilationOptions options) public override void DecompileAssembly(AssemblyDefinition assembly, string fileName, ITextOutput output, DecompilationOptions options)
{ {
if (options.FullDecompilation) { if (options.FullDecompilation && options.SaveAsProjectDirectory != null) {
if (options.SaveAsProjectDirectory != null) { HashSet<string> directories = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
HashSet<string> directories = new HashSet<string>(StringComparer.OrdinalIgnoreCase); var files = WriteCodeFilesInProject(assembly, options, directories).ToList();
var files = WriteCodeFilesInProject(assembly, options, directories).ToList(); files.AddRange(WriteResourceFilesInProject(assembly, fileName, options, directories));
files.AddRange(WriteResourceFilesInProject(assembly, fileName, options, directories)); WriteProjectFile(new TextOutputWriter(output), files, assembly.MainModule);
WriteProjectFile(new TextOutputWriter(output), files, assembly.MainModule);
} else {
foreach (TypeDefinition type in assembly.MainModule.Types) {
if (AstBuilder.MemberIsHidden(type, options.DecompilerSettings))
continue;
AstBuilder codeDomBuilder = CreateAstBuilder(options, type);
codeDomBuilder.AddType(type);
codeDomBuilder.GenerateCode(output, transformAbortCondition);
output.WriteLine();
}
}
} else { } else {
base.DecompileAssembly(assembly, fileName, output, options); base.DecompileAssembly(assembly, fileName, output, options);
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: null); AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: null);
codeDomBuilder.AddAssembly(assembly, onlyAssemblyLevel: true); codeDomBuilder.AddAssembly(assembly, onlyAssemblyLevel: !options.FullDecompilation);
codeDomBuilder.GenerateCode(output, transformAbortCondition); codeDomBuilder.GenerateCode(output, transformAbortCondition);
} }
} }
@ -392,14 +385,12 @@ namespace ICSharpCode.ILSpy
}); });
} }
public override string TypeToString(TypeReference type, bool includeNamespace, ICustomAttributeProvider typeAttributes) public override string TypeToString(TypeReference type, bool includeNamespace, ICustomAttributeProvider typeAttributes = null)
{ {
AstType astType = AstBuilder.ConvertType(type, typeAttributes); ConvertTypeOptions options = ConvertTypeOptions.IncludeTypeParameterDefinitions;
if (!includeNamespace) { if (includeNamespace)
var tre = new TypeReferenceExpression { Type = astType }; options |= ConvertTypeOptions.IncludeNamespace;
tre.AcceptVisitor(new RemoveNamespaceFromType(), null); AstType astType = AstBuilder.ConvertType(type, typeAttributes, options);
astType = tre.Type;
}
StringWriter w = new StringWriter(); StringWriter w = new StringWriter();
if (type.IsByReference) { if (type.IsByReference) {
@ -417,21 +408,6 @@ namespace ICSharpCode.ILSpy
return w.ToString(); return w.ToString();
} }
sealed class RemoveNamespaceFromType : DepthFirstAstVisitor<object, object>
{
public override object VisitMemberType(MemberType memberType, object data)
{
base.VisitMemberType(memberType, data);
SimpleType st = memberType.Target as SimpleType;
if (st != null && !st.TypeArguments.Any()) {
SimpleType newSt = new SimpleType(memberType.MemberName);
memberType.TypeArguments.MoveTo(newSt.TypeArguments);
memberType.ReplaceWith(newSt);
}
return null;
}
}
public override bool ShowMember(MemberReference member) public override bool ShowMember(MemberReference member)
{ {
return showAllMembers || !AstBuilder.MemberIsHidden(member, new DecompilationOptions().DecompilerSettings); return showAllMembers || !AstBuilder.MemberIsHidden(member, new DecompilationOptions().DecompilerSettings);

10
ILSpy/DecompilationOptions.cs

@ -51,7 +51,15 @@ namespace ICSharpCode.ILSpy
/// Gets the settings for the decompiler. /// Gets the settings for the decompiler.
/// </summary> /// </summary>
public DecompilerSettings DecompilerSettings { get; set; } public DecompilerSettings DecompilerSettings { get; set; }
/// <summary>
/// Gets/sets an optional state of a decompiler text view.
/// </summary>
/// <remarks>
/// This state is used to restore test view's state when decompilation is started by Go Back/Forward action.
/// </remarks>
public ICSharpCode.ILSpy.TextView.DecompilerTextViewState TextViewState { get; set; }
public DecompilationOptions() public DecompilationOptions()
{ {
this.DecompilerSettings = DecompilerSettingsPanel.CurrentDecompilerSettings; this.DecompilerSettings = DecompilerSettingsPanel.CurrentDecompilerSettings;

8
ILSpy/ILSpy.csproj

@ -156,6 +156,7 @@
<Compile Include="TreeNodes\Analyzer\AnalyzedMethodUsedByTreeNode.cs" /> <Compile Include="TreeNodes\Analyzer\AnalyzedMethodUsedByTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedMethodUsesNode.cs" /> <Compile Include="TreeNodes\Analyzer\AnalyzedMethodUsesNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzerTreeNode.cs" /> <Compile Include="TreeNodes\Analyzer\AnalyzerTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzerMethodOverridesTreeNode.cs" />
<Compile Include="TreeNodes\AssemblyListTreeNode.cs" /> <Compile Include="TreeNodes\AssemblyListTreeNode.cs" />
<Compile Include="TreeNodes\AssemblyReferenceTreeNode.cs" /> <Compile Include="TreeNodes\AssemblyReferenceTreeNode.cs" />
<Compile Include="TreeNodes\AssemblyTreeNode.cs" /> <Compile Include="TreeNodes\AssemblyTreeNode.cs" />
@ -259,11 +260,6 @@
<Name>ICSharpCode.TreeView</Name> <Name>ICSharpCode.TreeView</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup />
<Folder Include="Controls" />
<Folder Include="TreeNodes" />
<Folder Include="TextView" />
<Folder Include="TreeNodes\Analyzer" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
</Project> </Project>

45
ILSpy/MainWindow.xaml.cs

@ -47,7 +47,8 @@ namespace ICSharpCode.ILSpy
/// </summary> /// </summary>
partial class MainWindow : Window partial class MainWindow : Window
{ {
NavigationHistory history = new NavigationHistory(); NavigationHistory<Tuple<List<SharpTreeNode>, DecompilerTextViewState>> history =
new NavigationHistory<Tuple<List<SharpTreeNode>, DecompilerTextViewState>>();
ILSpySettings spySettings; ILSpySettings spySettings;
internal SessionSettings sessionSettings; internal SessionSettings sessionSettings;
AssemblyListManager assemblyListManager; AssemblyListManager assemblyListManager;
@ -259,7 +260,7 @@ namespace ICSharpCode.ILSpy
{ {
if (e.OldItems != null) if (e.OldItems != null)
foreach (LoadedAssembly asm in e.OldItems) foreach (LoadedAssembly asm in e.OldItems)
history.RemoveAll(n => n.AncestorsAndSelf().OfType<AssemblyTreeNode>().Any(a => a.LoadedAssembly == asm)); history.RemoveAll(n => n.Item1.Any(nd => nd.AncestorsAndSelf().OfType<AssemblyTreeNode>().Any(a => a.LoadedAssembly == asm)));
} }
void LoadInitialAssemblies() void LoadInitialAssemblies()
@ -289,7 +290,7 @@ namespace ICSharpCode.ILSpy
{ {
RefreshTreeViewFilter(); RefreshTreeViewFilter();
if (e.PropertyName == "Language") { if (e.PropertyName == "Language") {
TreeView_SelectionChanged(null, null); DecompileSelectedNodes();
} }
} }
@ -316,7 +317,7 @@ namespace ICSharpCode.ILSpy
if (obj != null) { if (obj != null) {
SharpTreeNode oldNode = treeView.SelectedItem as SharpTreeNode; SharpTreeNode oldNode = treeView.SelectedItem as SharpTreeNode;
if (oldNode != null && recordNavigationInHistory) if (oldNode != null && recordNavigationInHistory)
history.Record(oldNode); history.Record(Tuple.Create(treeView.SelectedItems.OfType<SharpTreeNode>().ToList(), decompilerTextView.GetState()));
// Set both the selection and focus to ensure that keyboard navigation works as expected. // Set both the selection and focus to ensure that keyboard navigation works as expected.
treeView.FocusNode(obj); treeView.FocusNode(obj);
treeView.SelectedItem = obj; treeView.SelectedItem = obj;
@ -437,12 +438,22 @@ namespace ICSharpCode.ILSpy
#region Decompile (TreeView_SelectionChanged) #region Decompile (TreeView_SelectionChanged)
void TreeView_SelectionChanged(object sender, SelectionChangedEventArgs e) void TreeView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{ {
DecompileSelectedNodes();
}
private bool ignoreDecompilationRequests;
private void DecompileSelectedNodes(DecompilerTextViewState state = null)
{
if (ignoreDecompilationRequests)
return;
if (treeView.SelectedItems.Count == 1) { if (treeView.SelectedItems.Count == 1) {
ILSpyTreeNode node = treeView.SelectedItem as ILSpyTreeNode; ILSpyTreeNode node = treeView.SelectedItem as ILSpyTreeNode;
if (node != null && node.View(decompilerTextView)) if (node != null && node.View(decompilerTextView))
return; return;
} }
decompilerTextView.Decompile(this.CurrentLanguage, this.SelectedNodes, new DecompilationOptions()); decompilerTextView.Decompile(this.CurrentLanguage, this.SelectedNodes, new DecompilationOptions() { TextViewState = state });
} }
void SaveCommandExecuted(object sender, ExecutedRoutedEventArgs e) void SaveCommandExecuted(object sender, ExecutedRoutedEventArgs e)
@ -458,7 +469,7 @@ namespace ICSharpCode.ILSpy
public void RefreshDecompiledView() public void RefreshDecompiledView()
{ {
TreeView_SelectionChanged(null, null); DecompileSelectedNodes();
} }
public DecompilerTextView TextView { public DecompilerTextView TextView {
@ -489,7 +500,7 @@ namespace ICSharpCode.ILSpy
{ {
if (history.CanNavigateBack) { if (history.CanNavigateBack) {
e.Handled = true; e.Handled = true;
SelectNode(history.GoBack(treeView.SelectedItem as SharpTreeNode), false); NavigateHistory(false);
} }
} }
@ -503,9 +514,27 @@ namespace ICSharpCode.ILSpy
{ {
if (history.CanNavigateForward) { if (history.CanNavigateForward) {
e.Handled = true; e.Handled = true;
SelectNode(history.GoForward(treeView.SelectedItem as SharpTreeNode), false); NavigateHistory(true);
} }
} }
void NavigateHistory(bool forward)
{
var currentSelection = treeView.SelectedItems.OfType<SharpTreeNode>().ToList();
var state = decompilerTextView.GetState();
var combinedState = Tuple.Create(currentSelection, state);
var newState = forward ? history.GoForward(combinedState) : history.GoBack(combinedState);
this.ignoreDecompilationRequests = true;
treeView.SelectedItems.Clear();
foreach (var node in newState.Item1)
{
treeView.SelectedItems.Add(node);
}
ignoreDecompilationRequests = false;
DecompileSelectedNodes(newState.Item2);
}
#endregion #endregion
#region Analyzer #region Analyzer

18
ILSpy/NavigationHistory.cs

@ -10,10 +10,10 @@ namespace ICSharpCode.ILSpy
/// <summary> /// <summary>
/// Stores the navigation history. /// Stores the navigation history.
/// </summary> /// </summary>
sealed class NavigationHistory sealed class NavigationHistory<T>
{ {
List<SharpTreeNode> back = new List<SharpTreeNode>(); List<T> back = new List<T>();
List<SharpTreeNode> forward = new List<SharpTreeNode>(); List<T> forward = new List<T>();
public bool CanNavigateBack { public bool CanNavigateBack {
get { return back.Count > 0; } get { return back.Count > 0; }
@ -23,27 +23,27 @@ namespace ICSharpCode.ILSpy
get { return forward.Count > 0; } get { return forward.Count > 0; }
} }
public SharpTreeNode GoBack(SharpTreeNode oldNode) public T GoBack(T oldNode)
{ {
if (oldNode != null) if (oldNode != null)
forward.Add(oldNode); forward.Add(oldNode);
SharpTreeNode node = back[back.Count - 1]; T node = back[back.Count - 1];
back.RemoveAt(back.Count - 1); back.RemoveAt(back.Count - 1);
return node; return node;
} }
public SharpTreeNode GoForward(SharpTreeNode oldNode) public T GoForward(T oldNode)
{ {
if (oldNode != null) if (oldNode != null)
back.Add(oldNode); back.Add(oldNode);
SharpTreeNode node = forward[forward.Count - 1]; T node = forward[forward.Count - 1];
forward.RemoveAt(forward.Count - 1); forward.RemoveAt(forward.Count - 1);
return node; return node;
} }
public void RemoveAll(Predicate<SharpTreeNode> predicate) public void RemoveAll(Predicate<T> predicate)
{ {
back.RemoveAll(predicate); back.RemoveAll(predicate);
forward.RemoveAll(predicate); forward.RemoveAll(predicate);
@ -55,7 +55,7 @@ namespace ICSharpCode.ILSpy
forward.Clear(); forward.Clear();
} }
public void Record(SharpTreeNode node) public void Record(T node)
{ {
forward.Clear(); forward.Clear();
back.Add(node); back.Add(node);

43
ILSpy/TextView/DecompilerTextView.cs

@ -298,9 +298,9 @@ namespace ICSharpCode.ILSpy.TextView
/// <summary> /// <summary>
/// Shows the given output in the text view. /// Shows the given output in the text view.
/// </summary> /// </summary>
void ShowOutput(AvalonEditTextOutput textOutput, IHighlightingDefinition highlighting = null) void ShowOutput(AvalonEditTextOutput textOutput, IHighlightingDefinition highlighting = null, DecompilerTextViewState state = null)
{ {
Debug.WriteLine("Showing {0} characters of output", textOutput.TextLength); Debug.WriteLine("Showing {0} characters of output", textOutput.TextLength);
Stopwatch w = Stopwatch.StartNew(); Stopwatch w = Stopwatch.StartNew();
textEditor.ScrollToHome(); textEditor.ScrollToHome();
@ -318,6 +318,11 @@ namespace ICSharpCode.ILSpy.TextView
textEditor.Document = textOutput.GetDocument(); textEditor.Document = textOutput.GetDocument();
Debug.WriteLine(" Assigning document: {0}", w.Elapsed); w.Restart(); Debug.WriteLine(" Assigning document: {0}", w.Elapsed); w.Restart();
if (textOutput.Foldings.Count > 0) { if (textOutput.Foldings.Count > 0) {
if (state != null) {
state.RestoreFoldings(textOutput.Foldings);
textEditor.ScrollToVerticalOffset(state.VerticalOffset);
textEditor.ScrollToHorizontalOffset(state.HorizontalOffset);
}
foldingManager = FoldingManager.Install(textEditor.TextArea); foldingManager = FoldingManager.Install(textEditor.TextArea);
foldingManager.UpdateFoldings(textOutput.Foldings.OrderBy(f => f.StartOffset), -1); foldingManager.UpdateFoldings(textOutput.Foldings.OrderBy(f => f.StartOffset), -1);
Debug.WriteLine(" Updating folding: {0}", w.Elapsed); w.Restart(); Debug.WriteLine(" Updating folding: {0}", w.Elapsed); w.Restart();
@ -383,7 +388,7 @@ namespace ICSharpCode.ILSpy.TextView
delegate (Task<AvalonEditTextOutput> task) { // handling the result delegate (Task<AvalonEditTextOutput> task) { // handling the result
try { try {
AvalonEditTextOutput textOutput = task.Result; AvalonEditTextOutput textOutput = task.Result;
ShowOutput(textOutput, context.Language.SyntaxHighlighting); ShowOutput(textOutput, context.Language.SyntaxHighlighting, context.Options.TextViewState);
} catch (AggregateException aggregateException) { } catch (AggregateException aggregateException) {
textEditor.SyntaxHighlighting = null; textEditor.SyntaxHighlighting = null;
Debug.WriteLine("Decompiler crashed: " + aggregateException.ToString()); Debug.WriteLine("Decompiler crashed: " + aggregateException.ToString());
@ -656,6 +661,16 @@ namespace ICSharpCode.ILSpy.TextView
return text; return text;
} }
#endregion #endregion
public DecompilerTextViewState GetState()
{
var state = new DecompilerTextViewState();
if (foldingManager != null)
state.SaveFoldingsState(foldingManager.AllFoldings);
state.VerticalOffset = textEditor.VerticalOffset;
state.HorizontalOffset = textEditor.HorizontalOffset;
return state;
}
#region Unfold #region Unfold
public void UnfoldAndScroll(int lineNumber) public void UnfoldAndScroll(int lineNumber)
@ -679,4 +694,26 @@ namespace ICSharpCode.ILSpy.TextView
} }
#endregion #endregion
} }
public class DecompilerTextViewState
{
private List<Tuple<int, int>> ExpandedFoldings;
private int FoldingsChecksum;
public double VerticalOffset;
public double HorizontalOffset;
public void SaveFoldingsState(IEnumerable<FoldingSection> foldings)
{
ExpandedFoldings = foldings.Where(f => !f.IsFolded).Select(f => Tuple.Create(f.StartOffset, f.EndOffset)).ToList();
FoldingsChecksum = foldings.Select(f => f.StartOffset * 3 - f.EndOffset).Aggregate((a, b) => a + b);
}
internal void RestoreFoldings(List<NewFolding> list)
{
var checksum = list.Select(f => f.StartOffset * 3 - f.EndOffset).Aggregate((a, b) => a + b);
if (FoldingsChecksum == checksum)
foreach (var folding in list)
folding.DefaultClosed = !ExpandedFoldings.Any(f => f.Item1 == folding.StartOffset && f.Item2 == folding.EndOffset);
}
}
} }

2
ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs

@ -52,6 +52,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
if (analyzedMethod.HasBody) if (analyzedMethod.HasBody)
this.Children.Add(new AnalyzedMethodUsesNode(analyzedMethod)); this.Children.Add(new AnalyzedMethodUsesNode(analyzedMethod));
this.Children.Add(new AnalyzedMethodUsedByTreeNode(analyzedMethod)); this.Children.Add(new AnalyzedMethodUsedByTreeNode(analyzedMethod));
if (analyzedMethod.IsVirtual && !analyzedMethod.IsFinal && !analyzedMethod.DeclaringType.IsInterface) // interfaces are temporarly disabled
this.Children.Add(new AnalyzerMethodOverridesTreeNode(analyzedMethod));
} }
} }
} }

203
ILSpy/TreeNodes/Analyzer/AnalyzerMethodOverridesTreeNode.cs

@ -0,0 +1,203 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using ICSharpCode.NRefactory.Utils;
using ICSharpCode.TreeView;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
/// <summary>
/// Searches for overrides of the analyzed method.
/// </summary>
class AnalyzerMethodOverridesTreeNode : AnalyzerTreeNode
{
readonly MethodDefinition analyzedMethod;
readonly ThreadingSupport threading;
/// <summary>
/// Controls whether overrides of already overriden method should be included.
/// </summary>
readonly bool onlyDirectOverrides = false;
public AnalyzerMethodOverridesTreeNode(MethodDefinition analyzedMethod)
{
if (analyzedMethod == null)
throw new ArgumentNullException("analyzedMethod");
this.analyzedMethod = analyzedMethod;
this.threading = new ThreadingSupport();
this.LazyLoading = true;
}
public override object Text
{
get { return "Overrided By"; }
}
public override object Icon
{
get { return Images.Search; }
}
protected override void LoadChildren()
{
threading.LoadChildren(this, FetchChildren);
}
protected override void OnCollapsing()
{
if (threading.IsRunning)
{
this.LazyLoading = true;
threading.Cancel();
this.Children.Clear();
}
}
IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct)
{
return FindReferences(MainWindow.Instance.AssemblyList.GetAssemblies(), ct);
}
IEnumerable<SharpTreeNode> FindReferences(LoadedAssembly[] assemblies, CancellationToken ct)
{
// use parallelism only on the assembly level (avoid locks within Cecil)
return assemblies.AsParallel().WithCancellation(ct).SelectMany((LoadedAssembly asm) => FindReferences(asm, ct));
}
IEnumerable<SharpTreeNode> FindReferences(LoadedAssembly asm, CancellationToken ct)
{
string asmName = asm.AssemblyDefinition.Name.Name;
string name = analyzedMethod.Name;
string declTypeName = analyzedMethod.DeclaringType.FullName;
foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes))
{
ct.ThrowIfCancellationRequested();
if (!IsDerived(type, analyzedMethod.DeclaringType))
continue;
foreach (MethodDefinition method in type.Methods)
{
ct.ThrowIfCancellationRequested();
if (HasCompatibleSpecification(method) && !method.IsNewSlot && DoesOverrideCorrectMethod(method))
{
yield return new AnalyzedMethodTreeNode(method);
}
}
}
}
/// <summary>
/// Tests whether the method could override analyzed method by comparing its name, return type and parameters.
/// </summary>
/// <param name="method">The method to test.</param>
/// <returns>true if the method has the same specyfication as analyzed method, otherwise false.</returns>
private bool HasCompatibleSpecification(MethodDefinition method)
{
return method.Name == analyzedMethod.Name
&& method.IsVirtual
&& AreSameType(method.ReturnType, analyzedMethod.ReturnType)
&& HaveTheSameParameters(method);
}
/// <summary>
/// Checks whether between given and analyzed method are overrides with <code>new</code> (newSlot) modifier.
/// </summary>
/// <param name="method">The method to test.</param>
/// <returns>true if the method overrides analyzed method, false if it overrides some other method that hides analyzed method.</returns>
private bool DoesOverrideCorrectMethod(MethodDefinition method)
{
var type = method.DeclaringType.BaseType.Resolve();
while (type != analyzedMethod.DeclaringType)
{
var parentOverride = type.Methods.Where(m => HasCompatibleSpecification(m)).SingleOrDefault();
if (parentOverride != null)
{
if (parentOverride.IsNewSlot)
return false;
else
return !onlyDirectOverrides;
}
type = type.BaseType.Resolve();
}
return true;
}
/// <summary>
/// Checks whether one type derives (directly or indirectly) from base type.
/// </summary>
/// <param name="derivedType">The possible derived type.</param>
/// <param name="baseType">The base type.</param>
/// <returns>true if <paramref name="derivedType"/> derives from <paramref name="baseType"/>, overwise false.</returns>
private static bool IsDerived(TypeDefinition derivedType, TypeDefinition baseType)
{
while (derivedType != null && derivedType.BaseType != null)
{
if (AreSameType(derivedType.BaseType, baseType))
return true;
derivedType = derivedType.BaseType.Resolve();
}
return false;
}
/// <summary>
/// Checks whether both <see cref="TypeReference"/> instances references the same type.
/// </summary>
/// <param name="ref1">The first type reference.</param>
/// <param name="ref2">The second type reference.</param>
/// <returns>true if both instances references the same type, overwise false.</returns>
private static bool AreSameType(TypeReference ref1, TypeReference ref2)
{
if (ref1 == ref2)
return true;
if (ref1.Name != ref2.Name || ref1.FullName != ref2.FullName)
return false;
return ref1.Resolve() == ref2.Resolve();
}
/// <summary>
/// Checkes whether the given method and the analyzed one has identical lists of parameters.
/// </summary>
/// <param name="method">The method to test.</param>
/// <returns>true if both methods has the same parameters, otherwise false.</returns>
private bool HaveTheSameParameters(MethodDefinition method)
{
if (analyzedMethod.HasParameters)
{
return CompareParameterLists(analyzedMethod.Parameters, method.Parameters);
}
else
{
return !method.HasParameters;
}
}
/// <summary>
/// Compares the list of method's parameters.
/// </summary>
/// <param name="coll1">The first list to compare.</param>
/// <param name="coll2">The second list to copare.</param>
/// <returns>true if both list have parameters of the same types at the same positions.</returns>
private static bool CompareParameterLists(Mono.Collections.Generic.Collection<ParameterDefinition> coll1, Mono.Collections.Generic.Collection<ParameterDefinition> coll2)
{
if (coll1.Count != coll2.Count)
return false;
for (int index = 0; index < coll1.Count; index++)
{
var param1 = coll1[index];
var param2 = coll2[index];
if (param1.Attributes != param2.Attributes || !AreSameType(param1.ParameterType, param2.ParameterType))
return false;
}
return true;
}
}
}

2
ILSpy/TreeNodes/BaseTypesTreeNode.cs

@ -94,7 +94,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
} }
public override object Text { public override object Text {
get { return tr.FullName; } get { return this.Language.TypeToString(tr, true); }
} }
public override object Icon { public override object Icon {

15
ILSpy/TreeNodes/DerivedTypesTreeNode.cs

@ -68,7 +68,18 @@ namespace ICSharpCode.ILSpy.TreeNodes
static bool IsSameType(TypeReference typeRef, TypeDefinition type) static bool IsSameType(TypeReference typeRef, TypeDefinition type)
{ {
return typeRef.FullName == type.FullName; if (typeRef.FullName == type.FullName)
return true;
if (typeRef.Name != type.Name || type.Namespace != typeRef.Namespace)
return false;
if (typeRef.IsNested || type.IsNested)
if (!typeRef.IsNested || !type.IsNested || !IsSameType(typeRef.DeclaringType, type.DeclaringType))
return false;
var gTypeRef = typeRef as GenericInstanceType;
if (gTypeRef != null || type.HasGenericParameters)
if (gTypeRef == null || !type.HasGenericParameters || gTypeRef.GenericArguments.Count != type.GenericParameters.Count)
return false;
return true;
} }
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
@ -98,7 +109,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
} }
public override object Text { public override object Text {
get { return def.FullName; } get { return this.Language.TypeToString(def, true); }
} }
public override object Icon { public override object Icon {

2
ILSpy/TreeNodes/TypeTreeNode.cs

@ -61,7 +61,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
} }
public override object Text { public override object Text {
get { return HighlightSearchMatch(type.Name); } get { return HighlightSearchMatch(this.Language.TypeToString(type, includeNamespace: false)); }
} }
public bool IsPublicAPI { public bool IsPublicAPI {

26
NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Linq; using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
using NUnit.Framework; using NUnit.Framework;
namespace ICSharpCode.NRefactory.CSharp.Analysis namespace ICSharpCode.NRefactory.CSharp.Analysis
@ -39,7 +40,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
Statement stmt5 = tryCatchStatement.FinallyBlock.Statements.Single(); Statement stmt5 = tryCatchStatement.FinallyBlock.Statements.Single();
LabelStatement label = (LabelStatement)block.Statements.ElementAt(1); LabelStatement label = (LabelStatement)block.Statements.ElementAt(1);
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(block); DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(block, CecilLoaderTests.Mscorlib);
da.Analyze("i"); da.Analyze("i");
Assert.AreEqual(0, da.UnassignedVariableUses.Count); Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(tryCatchStatement)); Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(tryCatchStatement));
@ -89,7 +90,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
TrueStatement = new BlockStatement(), TrueStatement = new BlockStatement(),
FalseStatement = new BlockStatement() FalseStatement = new BlockStatement()
}; };
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(ifStmt); DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(ifStmt, CecilLoaderTests.Mscorlib);
da.Analyze("i"); da.Analyze("i");
Assert.AreEqual(0, da.UnassignedVariableUses.Count); Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(ifStmt)); Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(ifStmt));
@ -120,7 +121,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
TrueStatement = new BlockStatement(), TrueStatement = new BlockStatement(),
FalseStatement = new BlockStatement() FalseStatement = new BlockStatement()
}; };
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(ifStmt); DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(ifStmt, CecilLoaderTests.Mscorlib);
da.Analyze("i"); da.Analyze("i");
Assert.AreEqual(0, da.UnassignedVariableUses.Count); Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(ifStmt)); Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(ifStmt));
@ -128,5 +129,24 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBefore(ifStmt.FalseStatement)); Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBefore(ifStmt.FalseStatement));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusAfter(ifStmt)); Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusAfter(ifStmt));
} }
[Test]
public void WhileTrue()
{
WhileStatement loop = new WhileStatement {
Condition = new PrimitiveExpression(true),
EmbeddedStatement = new BlockStatement {
new AssignmentExpression(new IdentifierExpression("i"), new PrimitiveExpression(0)),
new BreakStatement()
}
};
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(loop, CecilLoaderTests.Mscorlib);
da.Analyze("i");
Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(loop));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(loop.EmbeddedStatement));
Assert.AreEqual(DefiniteAssignmentStatus.CodeUnreachable, da.GetStatusAfter(loop.EmbeddedStatement));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop));
}
} }
} }

28
NRefactory/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices;
[assembly: ICSharpCode.NRefactory.TypeSystem.TestCase.TypeTestAttribute( [assembly: ICSharpCode.NRefactory.TypeSystem.TestCase.TypeTestAttribute(
42, typeof(System.Action<>), typeof(IDictionary<string, IList<NUnit.Framework.TestAttribute>>))] 42, typeof(System.Action<>), typeof(IDictionary<string, IList<NUnit.Framework.TestAttribute>>))]
@ -65,4 +66,31 @@ namespace ICSharpCode.NRefactory.TypeSystem.TestCase
{ {
public MyStructWithCtor(int a) {} public MyStructWithCtor(int a) {}
} }
[Serializable]
public class NonCustomAttributes
{
[NonSerialized]
public readonly int NonSerializedField;
[DllImport("unmanaged.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DllMethod([In, Out] ref int p);
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8)]
public struct ExplicitFieldLayoutStruct
{
[FieldOffset(0)]
public int Field0;
[FieldOffset(100)]
public int Field100;
}
public class ParameterTests
{
public void MethodWithOutParameter(out int x) { x = 0; }
public void MethodWithParamsArray(params object[] x) {}
}
} }

101
NRefactory/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using ICSharpCode.NRefactory.TypeSystem.Implementation; using ICSharpCode.NRefactory.TypeSystem.Implementation;
using ICSharpCode.NRefactory.TypeSystem.TestCase; using ICSharpCode.NRefactory.TypeSystem.TestCase;
using NUnit.Framework; using NUnit.Framework;
@ -261,5 +262,105 @@ namespace ICSharpCode.NRefactory.TypeSystem
Assert.AreEqual(2, ctors.Count()); Assert.AreEqual(2, ctors.Count());
Assert.IsFalse(ctors.Any(c => c.IsStatic)); Assert.IsFalse(ctors.Any(c => c.IsStatic));
} }
[Test]
public void SerializableAttribute()
{
IAttribute attr = ctx.GetClass(typeof(NonCustomAttributes)).Attributes.Single();
Assert.AreEqual("System.SerializableAttribute", attr.AttributeType.Resolve(ctx).FullName);
}
[Test]
public void NonSerializedAttribute()
{
IField field = ctx.GetClass(typeof(NonCustomAttributes)).Fields.Single(f => f.Name == "NonSerializedField");
Assert.AreEqual("System.NonSerializedAttribute", field.Attributes.Single().AttributeType.Resolve(ctx).FullName);
}
[Test]
public void ExplicitStructLayoutAttribute()
{
IAttribute attr = ctx.GetClass(typeof(ExplicitFieldLayoutStruct)).Attributes.Single();
Assert.AreEqual("System.Runtime.InteropServices.StructLayoutAttribute", attr.AttributeType.Resolve(ctx).FullName);
IConstantValue arg1 = attr.PositionalArguments.Single();
Assert.AreEqual("System.Runtime.InteropServices.LayoutKind", arg1.GetValueType(ctx).FullName);
Assert.AreEqual((int)LayoutKind.Explicit, arg1.GetValue(ctx));
var arg2 = attr.NamedArguments[0];
Assert.AreEqual("CharSet", arg2.Key);
Assert.AreEqual("System.Runtime.InteropServices.CharSet", arg2.Value.GetValueType(ctx).FullName);
Assert.AreEqual((int)CharSet.Unicode, arg2.Value.GetValue(ctx));
var arg3 = attr.NamedArguments[1];
Assert.AreEqual("Pack", arg3.Key);
Assert.AreEqual("System.Int32", arg3.Value.GetValueType(ctx).FullName);
Assert.AreEqual(8, arg3.Value.GetValue(ctx));
}
[Test]
public void FieldOffsetAttribute()
{
IField field = ctx.GetClass(typeof(ExplicitFieldLayoutStruct)).Fields.Single(f => f.Name == "Field0");
Assert.AreEqual("System.Runtime.InteropServices.FieldOffsetAttribute", field.Attributes.Single().AttributeType.Resolve(ctx).FullName);
IConstantValue arg = field.Attributes.Single().PositionalArguments.Single();
Assert.AreEqual("System.Int32", arg.GetValueType(ctx).FullName);
Assert.AreEqual(0, arg.GetValue(ctx));
field = ctx.GetClass(typeof(ExplicitFieldLayoutStruct)).Fields.Single(f => f.Name == "Field100");
Assert.AreEqual("System.Runtime.InteropServices.FieldOffsetAttribute", field.Attributes.Single().AttributeType.Resolve(ctx).FullName);
arg = field.Attributes.Single().PositionalArguments.Single();
Assert.AreEqual("System.Int32", arg.GetValueType(ctx).FullName);
Assert.AreEqual(100, arg.GetValue(ctx));
}
[Test]
public void DllImportAttribute()
{
IMethod method = ctx.GetClass(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod");
IAttribute dllImport = method.Attributes.Single();
Assert.AreEqual("System.Runtime.InteropServices.DllImportAttribute", dllImport.AttributeType.Resolve(ctx).FullName);
Assert.AreEqual("unmanaged.dll", dllImport.PositionalArguments[0].GetValue(ctx));
Assert.AreEqual((int)CharSet.Unicode, dllImport.NamedArguments.Single().Value.GetValue(ctx));
}
[Test]
public void InOutParametersOnRefMethod()
{
IParameter p = ctx.GetClass(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod").Parameters.Single();
Assert.IsTrue(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.AreEqual(2, p.Attributes.Count);
Assert.AreEqual("System.Runtime.InteropServices.InAttribute", p.Attributes[0].AttributeType.Resolve(ctx).FullName);
Assert.AreEqual("System.Runtime.InteropServices.OutAttribute", p.Attributes[1].AttributeType.Resolve(ctx).FullName);
}
[Test]
public void MarshalAsAttributeOnMethod()
{
IMethod method = ctx.GetClass(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod");
IAttribute marshalAs = method.ReturnTypeAttributes.Single();
Assert.AreEqual((int)UnmanagedType.Bool, marshalAs.PositionalArguments.Single().GetValue(ctx));
}
[Test]
public void MethodWithOutParameter()
{
IParameter p = ctx.GetClass(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOutParameter").Parameters.Single();
Assert.IsFalse(p.IsRef);
Assert.IsTrue(p.IsOut);
Assert.AreEqual(0, p.Attributes.Count);
Assert.IsTrue(p.Type is ByReferenceTypeReference);
}
[Test]
public void MethodWithParamsArray()
{
IParameter p = ctx.GetClass(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithParamsArray").Parameters.Single();
Assert.IsFalse(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.IsTrue(p.IsParams);
Assert.AreEqual(0, p.Attributes.Count);
Assert.IsTrue(p.Type is ArrayTypeReference);
}
} }
} }

29
NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs

@ -5,7 +5,10 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading;
using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp.Analysis namespace ICSharpCode.NRefactory.CSharp.Analysis
{ {
@ -135,12 +138,24 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
} }
Statement rootStatement; Statement rootStatement;
ResolveVisitor resolveVisitor;
List<ControlFlowNode> nodes; List<ControlFlowNode> nodes;
Dictionary<string, ControlFlowNode> labels; Dictionary<string, ControlFlowNode> labels;
List<ControlFlowNode> gotoStatements; List<ControlFlowNode> gotoStatements;
public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement) public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken))
{ {
return BuildControlFlowGraph(statement, new ResolveVisitor(
new CSharpResolver(context, cancellationToken), null, ConstantModeResolveVisitorNavigator.Skip));
}
public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, ResolveVisitor resolveVisitor)
{
if (statement == null)
throw new ArgumentNullException("statement");
if (resolveVisitor == null)
throw new ArgumentNullException("resolveVisitor");
NodeCreationVisitor nodeCreationVisitor = new NodeCreationVisitor(); NodeCreationVisitor nodeCreationVisitor = new NodeCreationVisitor();
nodeCreationVisitor.builder = this; nodeCreationVisitor.builder = this;
try { try {
@ -148,6 +163,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
this.labels = new Dictionary<string, ControlFlowNode>(); this.labels = new Dictionary<string, ControlFlowNode>();
this.gotoStatements = new List<ControlFlowNode>(); this.gotoStatements = new List<ControlFlowNode>();
this.rootStatement = statement; this.rootStatement = statement;
this.resolveVisitor = resolveVisitor;
ControlFlowNode entryPoint = CreateStartNode(statement); ControlFlowNode entryPoint = CreateStartNode(statement);
statement.AcceptVisitor(nodeCreationVisitor, entryPoint); statement.AcceptVisitor(nodeCreationVisitor, entryPoint);
@ -167,6 +183,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
this.labels = null; this.labels = null;
this.gotoStatements = null; this.gotoStatements = null;
this.rootStatement = null; this.rootStatement = null;
this.resolveVisitor = null;
} }
} }
@ -206,7 +223,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type) ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type)
{ {
ControlFlowNode node = CreateNode(statement, null, type); ControlFlowNode node = CreateNode(null, statement, type);
nodes.Add(node); nodes.Add(node);
return node; return node;
} }
@ -238,7 +255,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// <returns>The constant value of the expression; or null if the expression is not a constant.</returns> /// <returns>The constant value of the expression; or null if the expression is not a constant.</returns>
ConstantResolveResult EvaluateConstant(Expression expr) ConstantResolveResult EvaluateConstant(Expression expr)
{ {
return null; // TODO: implement this using the C# resolver return resolveVisitor.Resolve(expr) as ConstantResolveResult;
} }
/// <summary> /// <summary>
@ -256,7 +273,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
bool AreEqualConstants(ConstantResolveResult c1, ConstantResolveResult c2) bool AreEqualConstants(ConstantResolveResult c1, ConstantResolveResult c2)
{ {
return false; // TODO: implement this using the resolver's operator== if (c1 == null || c2 == null)
return false;
CSharpResolver r = new CSharpResolver(resolveVisitor.TypeResolveContext, resolveVisitor.CancellationToken);
ResolveResult c = r.ResolveBinaryOperator(BinaryOperatorType.Equality, c1, c2);
return c.IsCompileTimeConstant && (c.ConstantValue as bool?) == true;
} }
#endregion #endregion

25
NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs

@ -5,7 +5,9 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading;
using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.Utils; using ICSharpCode.NRefactory.Utils;
namespace ICSharpCode.NRefactory.CSharp.Analysis namespace ICSharpCode.NRefactory.CSharp.Analysis
@ -46,6 +48,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
readonly List<ControlFlowNode> allNodes = new List<ControlFlowNode>(); readonly List<ControlFlowNode> allNodes = new List<ControlFlowNode>();
readonly Dictionary<Statement, ControlFlowNode> beginNodeDict = new Dictionary<Statement, ControlFlowNode>(); readonly Dictionary<Statement, ControlFlowNode> beginNodeDict = new Dictionary<Statement, ControlFlowNode>();
readonly Dictionary<Statement, ControlFlowNode> endNodeDict = new Dictionary<Statement, ControlFlowNode>(); readonly Dictionary<Statement, ControlFlowNode> endNodeDict = new Dictionary<Statement, ControlFlowNode>();
readonly ResolveVisitor resolveVisitor;
readonly CancellationToken cancellationToken;
Dictionary<ControlFlowNode, DefiniteAssignmentStatus> nodeStatus = new Dictionary<ControlFlowNode, DefiniteAssignmentStatus>(); Dictionary<ControlFlowNode, DefiniteAssignmentStatus> nodeStatus = new Dictionary<ControlFlowNode, DefiniteAssignmentStatus>();
Dictionary<ControlFlowEdge, DefiniteAssignmentStatus> edgeStatus = new Dictionary<ControlFlowEdge, DefiniteAssignmentStatus>(); Dictionary<ControlFlowEdge, DefiniteAssignmentStatus> edgeStatus = new Dictionary<ControlFlowEdge, DefiniteAssignmentStatus>();
@ -54,19 +58,30 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
Queue<ControlFlowNode> nodesWithModifiedInput = new Queue<ControlFlowNode>(); Queue<ControlFlowNode> nodesWithModifiedInput = new Queue<ControlFlowNode>();
public DefiniteAssignmentAnalysis(Statement rootStatement) public DefiniteAssignmentAnalysis(Statement rootStatement, ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken))
: this(rootStatement, new ResolveVisitor(new CSharpResolver(context, cancellationToken), null, ConstantModeResolveVisitorNavigator.Skip))
{ {
}
public DefiniteAssignmentAnalysis(Statement rootStatement, ResolveVisitor resolveVisitor)
{
if (rootStatement == null)
throw new ArgumentNullException("rootStatement");
if (resolveVisitor == null)
throw new ArgumentNullException("resolveVisitor");
this.resolveVisitor = resolveVisitor;
this.cancellationToken = resolveVisitor.CancellationToken;
visitor.analysis = this; visitor.analysis = this;
ControlFlowGraphBuilder b = new ControlFlowGraphBuilder(); ControlFlowGraphBuilder b = new ControlFlowGraphBuilder();
allNodes.AddRange(b.BuildControlFlowGraph(rootStatement)); allNodes.AddRange(b.BuildControlFlowGraph(rootStatement, resolveVisitor));
foreach (AstNode descendant in rootStatement.Descendants) { foreach (AstNode descendant in rootStatement.Descendants) {
// Anonymous methods have separate control flow graphs, but we also need to analyze those. // Anonymous methods have separate control flow graphs, but we also need to analyze those.
AnonymousMethodExpression ame = descendant as AnonymousMethodExpression; AnonymousMethodExpression ame = descendant as AnonymousMethodExpression;
if (ame != null) if (ame != null)
allNodes.AddRange(b.BuildControlFlowGraph(ame.Body)); allNodes.AddRange(b.BuildControlFlowGraph(ame.Body, resolveVisitor));
LambdaExpression lambda = descendant as LambdaExpression; LambdaExpression lambda = descendant as LambdaExpression;
if (lambda != null && lambda.Body is Statement) if (lambda != null && lambda.Body is Statement)
allNodes.AddRange(b.BuildControlFlowGraph((Statement)lambda.Body)); allNodes.AddRange(b.BuildControlFlowGraph((Statement)lambda.Body, resolveVisitor));
} }
// Verify that we created nodes for all statements: // Verify that we created nodes for all statements:
Debug.Assert(!rootStatement.DescendantsAndSelf.OfType<Statement>().Except(allNodes.Select(n => n.NextStatement)).Any()); Debug.Assert(!rootStatement.DescendantsAndSelf.OfType<Statement>().Except(allNodes.Select(n => n.NextStatement)).Any());
@ -289,7 +304,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// <returns>The constant value of the expression; or null if the expression is not a constant.</returns> /// <returns>The constant value of the expression; or null if the expression is not a constant.</returns>
ConstantResolveResult EvaluateConstant(Expression expr) ConstantResolveResult EvaluateConstant(Expression expr)
{ {
return null; // TODO: implement this using the C# resolver return resolveVisitor.Resolve(expr) as ConstantResolveResult;
} }
/// <summary> /// <summary>

1
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs

@ -129,6 +129,7 @@ namespace ICSharpCode.NRefactory.CSharp
get { get {
AstNode next; AstNode next;
for (AstNode cur = firstChild; cur != null; cur = next) { for (AstNode cur = firstChild; cur != null; cur = next) {
Debug.Assert(cur.parent == this);
// Remember next before yielding cur. // Remember next before yielding cur.
// This allows removing/replacing nodes while iterating through the list. // This allows removing/replacing nodes while iterating through the list.
next = cur.nextSibling; next = cur.nextSibling;

1
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs

@ -117,6 +117,7 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
AstNode next; AstNode next;
for (AstNode cur = node.FirstChild; cur != null; cur = next) { for (AstNode cur = node.FirstChild; cur != null; cur = next) {
Debug.Assert(cur.Parent == node);
// Remember next before yielding cur. // Remember next before yielding cur.
// This allows removing/replacing nodes while iterating through the list. // This allows removing/replacing nodes while iterating through the list.
next = cur.NextSibling; next = cur.NextSibling;

4
NRefactory/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs

@ -374,9 +374,9 @@ namespace ICSharpCode.NRefactory.CSharp
variable.AddChild (new Identifier (em.Name, Convert (em.Location)), AstNode.Roles.Identifier); variable.AddChild (new Identifier (em.Name, Convert (em.Location)), AstNode.Roles.Identifier);
if (em.Initializer != null) { if (em.Initializer != null) {
var initializer = (VariableInitializer)em.Initializer.Accept (this); var initializer = (Expression)em.Initializer.Accept (this);
if (initializer != null) if (initializer != null)
variable.AddChild (initializer, AstNode.Roles.Variable); variable.AddChild (initializer, EnumMemberDeclaration.InitializerRole);
} }
newField.AddChild (variable, AstNode.Roles.Variable); newField.AddChild (variable, AstNode.Roles.Variable);

2
NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs

@ -33,7 +33,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
this.context = context; this.context = context;
} }
#if !DOTNET35
public CSharpResolver(ITypeResolveContext context, CancellationToken cancellationToken) public CSharpResolver(ITypeResolveContext context, CancellationToken cancellationToken)
{ {
if (context == null) if (context == null)
@ -41,7 +40,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
this.context = context; this.context = context;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
} }
#endif
#endregion #endregion
#region Properties #region Properties

12
NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs

@ -37,4 +37,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// </summary> /// </summary>
ResolveAll ResolveAll
} }
sealed class ConstantModeResolveVisitorNavigator : IResolveVisitorNavigator
{
ResolveVisitorNavigationMode mode;
public static readonly IResolveVisitorNavigator Skip = new ConstantModeResolveVisitorNavigator { mode = ResolveVisitorNavigationMode.Skip };
ResolveVisitorNavigationMode IResolveVisitorNavigator.Scan(AstNode node)
{
return mode;
}
}
} }

15
NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs

@ -74,6 +74,20 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
} }
#endregion #endregion
/// <summary>
/// Gets the TypeResolveContext used by this ResolveVisitor.
/// </summary>
public ITypeResolveContext TypeResolveContext {
get { return resolver.Context; }
}
/// <summary>
/// Gets the CancellationToken used by this ResolveVisitor.
/// </summary>
public CancellationToken CancellationToken {
get { return resolver.cancellationToken; }
}
#region Scan / Resolve #region Scan / Resolve
bool resolverEnabled { bool resolverEnabled {
get { return mode != ResolveVisitorNavigationMode.Scan; } get { return mode != ResolveVisitorNavigationMode.Scan; }
@ -118,6 +132,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
mode = ResolveVisitorNavigationMode.Resolve; mode = ResolveVisitorNavigationMode.Resolve;
ResolveResult result; ResolveResult result;
if (!cache.TryGetValue(node, out result)) { if (!cache.TryGetValue(node, out result)) {
resolver.cancellationToken.ThrowIfCancellationRequested();
result = cache[node] = node.AcceptVisitor(this, null) ?? errorResult; result = cache[node] = node.AcceptVisitor(this, null) ?? errorResult;
} }
if (wasScan) if (wasScan)

258
NRefactory/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs

@ -46,6 +46,10 @@ namespace ICSharpCode.NRefactory.TypeSystem
#endregion #endregion
#region Load From AssemblyDefinition #region Load From AssemblyDefinition
/// <summary>
/// Loads the assembly definition into a project content.
/// </summary>
/// <returns>IProjectContent that represents the assembly</returns>
public IProjectContent LoadAssembly(AssemblyDefinition assemblyDefinition) public IProjectContent LoadAssembly(AssemblyDefinition assemblyDefinition)
{ {
if (assemblyDefinition == null) if (assemblyDefinition == null)
@ -144,7 +148,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
public void Dispose() public void Dispose()
{ {
// Disposibng the synchronization context has no effect // Disposing the synchronization context has no effect
} }
string IDocumentationProvider.GetDocumentation(IEntity entity) string IDocumentationProvider.GetDocumentation(IEntity entity)
@ -297,14 +301,13 @@ namespace ICSharpCode.NRefactory.TypeSystem
} }
} }
const string DynamicAttributeFullName = "System.Runtime.CompilerServices.DynamicAttribute";
static bool HasDynamicAttribute(ICustomAttributeProvider attributeProvider, int typeIndex) static bool HasDynamicAttribute(ICustomAttributeProvider attributeProvider, int typeIndex)
{ {
if (attributeProvider == null || !attributeProvider.HasCustomAttributes) if (attributeProvider == null || !attributeProvider.HasCustomAttributes)
return false; return false;
foreach (CustomAttribute a in attributeProvider.CustomAttributes) { foreach (CustomAttribute a in attributeProvider.CustomAttributes) {
if (a.Constructor.DeclaringType.FullName == DynamicAttributeFullName) { TypeReference type = a.AttributeType;
if (type.Name == "DynamicAttribute" && type.Namespace == "System.Runtime.CompilerServices") {
if (a.ConstructorArguments.Count == 1) { if (a.ConstructorArguments.Count == 1) {
CustomAttributeArgument[] values = a.ConstructorArguments[0].Value as CustomAttributeArgument[]; CustomAttributeArgument[] values = a.ConstructorArguments[0].Value as CustomAttributeArgument[];
if (values != null && typeIndex < values.Length && values[typeIndex].Value is bool) if (values != null && typeIndex < values.Length && values[typeIndex].Value is bool)
@ -325,21 +328,150 @@ namespace ICSharpCode.NRefactory.TypeSystem
} }
} }
static readonly IAttribute inAttribute = new DefaultAttribute(typeof(InAttribute).ToTypeReference(), null);
static readonly IAttribute outAttribute = new DefaultAttribute(typeof(OutAttribute).ToTypeReference(), null);
void AddAttributes(ParameterDefinition parameter, DefaultParameter targetParameter) void AddAttributes(ParameterDefinition parameter, DefaultParameter targetParameter)
{ {
if (!targetParameter.IsOut) {
if (parameter.IsIn)
targetParameter.Attributes.Add(inAttribute);
if (parameter.IsOut)
targetParameter.Attributes.Add(outAttribute);
}
if (parameter.HasCustomAttributes) { if (parameter.HasCustomAttributes) {
AddCustomAttributes(parameter.CustomAttributes, targetParameter.Attributes); AddCustomAttributes(parameter.CustomAttributes, targetParameter.Attributes);
} }
} }
void AddAttributes(MethodDefinition accessorMethod, DefaultAccessor targetAccessor) static readonly ITypeReference dllImportAttributeTypeRef = typeof(DllImportAttribute).ToTypeReference();
static readonly SimpleConstantValue trueValue = new SimpleConstantValue(KnownTypeReference.Boolean, true);
static readonly SimpleConstantValue falseValue = new SimpleConstantValue(KnownTypeReference.Boolean, true);
static readonly ITypeReference callingConventionTypeRef = typeof(CallingConvention).ToTypeReference();
static readonly IAttribute preserveSigAttribute = new DefaultAttribute(typeof(PreserveSigAttribute).ToTypeReference(), null);
static readonly ITypeReference methodImplAttributeTypeRef = typeof(MethodImplAttribute).ToTypeReference();
static readonly ITypeReference methodImplOptionsTypeRef = typeof(MethodImplOptions).ToTypeReference();
bool HasAnyAttributes(MethodDefinition methodDefinition)
{ {
if (accessorMethod.HasCustomAttributes) { if (methodDefinition.HasPInvokeInfo)
AddCustomAttributes(accessorMethod.CustomAttributes, targetAccessor.Attributes); return true;
if ((methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask) != 0)
return true;
if (methodDefinition.MethodReturnType.HasFieldMarshal)
return true;
return methodDefinition.HasCustomAttributes || methodDefinition.MethodReturnType.HasCustomAttributes;
}
void AddAttributes(MethodDefinition methodDefinition, IList<IAttribute> attributes, IList<IAttribute> returnTypeAttributes)
{
MethodImplAttributes implAttributes = methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask;
#region DllImportAttribute
if (methodDefinition.HasPInvokeInfo) {
PInvokeInfo info = methodDefinition.PInvokeInfo;
DefaultAttribute dllImport = new DefaultAttribute(dllImportAttributeTypeRef, new[] { KnownTypeReference.String });
dllImport.PositionalArguments.Add(new SimpleConstantValue(KnownTypeReference.String, info.Module.Name));
if (info.IsBestFitDisabled)
AddNamedArgument(dllImport, "BestFitMapping", falseValue);
if (info.IsBestFitEnabled)
AddNamedArgument(dllImport, "BestFitMapping", trueValue);
CallingConvention callingConvention;
switch (info.Attributes & PInvokeAttributes.CallConvMask) {
case PInvokeAttributes.CallConvCdecl:
callingConvention = CallingConvention.Cdecl;
break;
case PInvokeAttributes.CallConvFastcall:
callingConvention = CallingConvention.FastCall;
break;
case PInvokeAttributes.CallConvStdCall:
callingConvention = CallingConvention.StdCall;
break;
case PInvokeAttributes.CallConvThiscall:
callingConvention = CallingConvention.ThisCall;
break;
case PInvokeAttributes.CallConvWinapi:
callingConvention = CallingConvention.Winapi;
break;
default:
throw new NotSupportedException("unknown calling convention");
}
if (callingConvention != CallingConvention.Winapi)
AddNamedArgument(dllImport, "CallingConvention", new SimpleConstantValue(callingConventionTypeRef, (int)callingConvention));
CharSet charSet = CharSet.None;
switch (info.Attributes & PInvokeAttributes.CharSetMask) {
case PInvokeAttributes.CharSetAnsi:
charSet = CharSet.Ansi;
break;
case PInvokeAttributes.CharSetAuto:
charSet = CharSet.Auto;
break;
case PInvokeAttributes.CharSetUnicode:
charSet = CharSet.Unicode;
break;
}
if (charSet != CharSet.None)
dllImport.NamedArguments.Add(new KeyValuePair<string, IConstantValue>(
"CharSet", new SimpleConstantValue(charSetTypeRef, (int)charSet)));
if (!string.IsNullOrEmpty(info.EntryPoint) && info.EntryPoint != methodDefinition.Name)
AddNamedArgument(dllImport, "EntryPoint", new SimpleConstantValue(KnownTypeReference.String, info.EntryPoint));
if (info.IsNoMangle)
AddNamedArgument(dllImport, "ExactSpelling", trueValue);
if ((implAttributes & MethodImplAttributes.PreserveSig) == MethodImplAttributes.PreserveSig)
implAttributes &= ~MethodImplAttributes.PreserveSig;
else
AddNamedArgument(dllImport, "PreserveSig", falseValue);
if (info.SupportsLastError)
AddNamedArgument(dllImport, "SetLastError", trueValue);
if (info.IsThrowOnUnmappableCharDisabled)
AddNamedArgument(dllImport, "ThrowOnUnmappableChar", falseValue);
if (info.IsThrowOnUnmappableCharEnabled)
AddNamedArgument(dllImport, "ThrowOnUnmappableChar", trueValue);
attributes.Add(dllImport);
}
#endregion
#region PreserveSigAttribute
if (implAttributes == MethodImplAttributes.PreserveSig) {
attributes.Add(preserveSigAttribute);
implAttributes = 0;
}
#endregion
#region MethodImplAttribute
if (implAttributes != 0) {
DefaultAttribute methodImpl = new DefaultAttribute(methodImplAttributeTypeRef, new[] { methodImplOptionsTypeRef });
methodImpl.PositionalArguments.Add(new SimpleConstantValue(methodImplOptionsTypeRef, (int)implAttributes));
attributes.Add(methodImpl);
}
#endregion
if (methodDefinition.HasCustomAttributes) {
AddCustomAttributes(methodDefinition.CustomAttributes, attributes);
}
if (methodDefinition.MethodReturnType.HasMarshalInfo) {
returnTypeAttributes.Add(ConvertMarshalInfo(methodDefinition.MethodReturnType.MarshalInfo));
} }
if (methodDefinition.MethodReturnType.HasCustomAttributes) {
AddCustomAttributes(methodDefinition.MethodReturnType.CustomAttributes, returnTypeAttributes);
}
}
static void AddNamedArgument(DefaultAttribute attribute, string name, IConstantValue value)
{
attribute.NamedArguments.Add(new KeyValuePair<string, IConstantValue>(name, value));
} }
static readonly DefaultAttribute serializableAttribute = new DefaultAttribute(typeof(SerializableAttribute).ToTypeReference()); static readonly DefaultAttribute serializableAttribute = new DefaultAttribute(typeof(SerializableAttribute).ToTypeReference(), null);
static readonly ITypeReference structLayoutAttributeTypeRef = typeof(StructLayoutAttribute).ToTypeReference(); static readonly ITypeReference structLayoutAttributeTypeRef = typeof(StructLayoutAttribute).ToTypeReference();
static readonly ITypeReference layoutKindTypeRef = typeof(LayoutKind).ToTypeReference(); static readonly ITypeReference layoutKindTypeRef = typeof(LayoutKind).ToTypeReference();
static readonly ITypeReference charSetTypeRef = typeof(CharSet).ToTypeReference(); static readonly ITypeReference charSetTypeRef = typeof(CharSet).ToTypeReference();
@ -373,8 +505,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
charSet = CharSet.Unicode; charSet = CharSet.Unicode;
break; break;
} }
if (layoutKind != LayoutKind.Auto || charSet != CharSet.Ansi || typeDefinition.PackingSize > 0 || typeDefinition.ClassSize > 0) { LayoutKind defaultLayoutKind = (typeDefinition.IsValueType && !typeDefinition.IsEnum) ? LayoutKind.Sequential: LayoutKind.Auto;
DefaultAttribute structLayout = new DefaultAttribute(structLayoutAttributeTypeRef); if (layoutKind != defaultLayoutKind || charSet != CharSet.Ansi || typeDefinition.PackingSize > 0 || typeDefinition.ClassSize > 0) {
DefaultAttribute structLayout = new DefaultAttribute(structLayoutAttributeTypeRef, new[] { layoutKindTypeRef });
structLayout.PositionalArguments.Add(new SimpleConstantValue(layoutKindTypeRef, (int)layoutKind)); structLayout.PositionalArguments.Add(new SimpleConstantValue(layoutKindTypeRef, (int)layoutKind));
if (charSet != CharSet.Ansi) { if (charSet != CharSet.Ansi) {
structLayout.NamedArguments.Add(new KeyValuePair<string, IConstantValue>( structLayout.NamedArguments.Add(new KeyValuePair<string, IConstantValue>(
@ -400,12 +533,58 @@ namespace ICSharpCode.NRefactory.TypeSystem
} }
} }
static readonly ITypeReference fieldOffsetAttributeTypeRef = typeof(FieldOffsetAttribute).ToTypeReference();
static readonly DefaultAttribute nonSerializedAttribute = new DefaultAttribute(typeof(NonSerializedAttribute).ToTypeReference(), null);
void AddAttributes(FieldDefinition fieldDefinition, IEntity targetEntity)
{
#region FieldOffsetAttribute
if (fieldDefinition.HasLayoutInfo) {
DefaultAttribute fieldOffset = new DefaultAttribute(fieldOffsetAttributeTypeRef, new[] { KnownTypeReference.Int32 });
fieldOffset.PositionalArguments.Add(new SimpleConstantValue(KnownTypeReference.Int32, fieldDefinition.Offset));
targetEntity.Attributes.Add(fieldOffset);
}
#endregion
#region NonSerializedAttribute
if (fieldDefinition.IsNotSerialized) {
targetEntity.Attributes.Add(nonSerializedAttribute);
}
#endregion
if (fieldDefinition.HasMarshalInfo) {
targetEntity.Attributes.Add(ConvertMarshalInfo(fieldDefinition.MarshalInfo));
}
if (fieldDefinition.HasCustomAttributes) {
AddCustomAttributes(fieldDefinition.CustomAttributes, targetEntity.Attributes);
}
}
#region MarshalAsAttribute (ConvertMarshalInfo)
static readonly ITypeReference marshalAsAttributeTypeRef = typeof(MarshalAsAttribute).ToTypeReference();
static readonly ITypeReference unmanagedTypeTypeRef = typeof(UnmanagedType).ToTypeReference();
static IAttribute ConvertMarshalInfo(MarshalInfo marshalInfo)
{
DefaultAttribute attr = new DefaultAttribute(marshalAsAttributeTypeRef, new[] { unmanagedTypeTypeRef });
attr.PositionalArguments.Add(new SimpleConstantValue(unmanagedTypeTypeRef, (int)marshalInfo.NativeType));
// TODO: handle classes derived from MarshalInfo
return attr;
}
#endregion
void AddCustomAttributes(Mono.Collections.Generic.Collection<CustomAttribute> attributes, IList<IAttribute> targetCollection) void AddCustomAttributes(Mono.Collections.Generic.Collection<CustomAttribute> attributes, IList<IAttribute> targetCollection)
{ {
foreach (var cecilAttribute in attributes) { foreach (var cecilAttribute in attributes) {
if (cecilAttribute.AttributeType.FullName != DynamicAttributeFullName) { TypeReference type = cecilAttribute.AttributeType;
targetCollection.Add(ReadAttribute(cecilAttribute)); if (type.Namespace == "System.Runtime.CompilerServices") {
if (type.Name == "DynamicAttribute" || type.Name == "ExtensionAttribute")
continue;
} else if (type.Name == "ParamArrayAttribute" && type.Namespace == "System") {
continue;
} }
targetCollection.Add(ReadAttribute(cecilAttribute));
} }
} }
@ -413,7 +592,15 @@ namespace ICSharpCode.NRefactory.TypeSystem
{ {
if (attribute == null) if (attribute == null)
throw new ArgumentNullException("attribute"); throw new ArgumentNullException("attribute");
DefaultAttribute a = new DefaultAttribute(ReadTypeReference(attribute.AttributeType)); MethodReference ctor = attribute.Constructor;
ITypeReference[] ctorParameters = null;
if (ctor.HasParameters) {
ctorParameters = new ITypeReference[ctor.Parameters.Count];
for (int i = 0; i < ctorParameters.Length; i++) {
ctorParameters[i] = ReadTypeReference(ctor.Parameters[i].ParameterType);
}
}
DefaultAttribute a = new DefaultAttribute(ReadTypeReference(attribute.AttributeType), ctorParameters);
try { try {
if (attribute.HasConstructorArguments) { if (attribute.HasConstructorArguments) {
foreach (var arg in attribute.ConstructorArguments) { foreach (var arg in attribute.ConstructorArguments) {
@ -442,8 +629,13 @@ namespace ICSharpCode.NRefactory.TypeSystem
#region Read Constant Value #region Read Constant Value
public IConstantValue ReadConstantValue(CustomAttributeArgument arg) public IConstantValue ReadConstantValue(CustomAttributeArgument arg)
{ {
ITypeReference type = ReadTypeReference(arg.Type);
object value = arg.Value; object value = arg.Value;
if (value is CustomAttributeArgument) {
// Cecil uses this representation for boxed values
arg = (CustomAttributeArgument)value;
value = arg.Value;
}
ITypeReference type = ReadTypeReference(arg.Type);
CustomAttributeArgument[] array = value as CustomAttributeArgument[]; CustomAttributeArgument[] array = value as CustomAttributeArgument[];
if (array != null) { if (array != null) {
// TODO: write unit test for this // TODO: write unit test for this
@ -500,9 +692,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
InitNestedTypes(loader); // nested types can be initialized only after generic parameters were created InitNestedTypes(loader); // nested types can be initialized only after generic parameters were created
if (typeDefinition.HasCustomAttributes) { loader.AddAttributes(typeDefinition, this);
loader.AddAttributes(typeDefinition, this);
} this.HasExtensionMethods = HasExtensionAttribute(typeDefinition);
// set base classes // set base classes
if (typeDefinition.IsEnum) { if (typeDefinition.IsEnum) {
@ -699,7 +891,8 @@ namespace ICSharpCode.NRefactory.TypeSystem
else else
m.ReturnType = ReadTypeReference(method.ReturnType, typeAttributes: method.MethodReturnType, entity: m); m.ReturnType = ReadTypeReference(method.ReturnType, typeAttributes: method.MethodReturnType, entity: m);
AddAttributes(method, m); if (HasAnyAttributes(method))
AddAttributes(method, m.Attributes, m.ReturnTypeAttributes);
TranslateModifiers(method, m); TranslateModifiers(method, m);
if (method.HasParameters) { if (method.HasParameters) {
@ -708,18 +901,26 @@ namespace ICSharpCode.NRefactory.TypeSystem
} }
} }
// mark as extension method is the attribute is set // mark as extension method if the attribute is set
if (method.IsStatic && method.HasCustomAttributes) { if (method.IsStatic && HasExtensionAttribute(method)) {
foreach (var attr in method.CustomAttributes) { m.IsExtensionMethod = true;
if (attr.AttributeType.FullName == typeof(ExtensionAttribute).FullName)
m.IsExtensionMethod = true;
}
} }
FinishReadMember(m); FinishReadMember(m);
return m; return m;
} }
static bool HasExtensionAttribute(ICustomAttributeProvider provider)
{
if (provider.HasCustomAttributes) {
foreach (var attr in provider.CustomAttributes) {
if (attr.AttributeType.Name == "ExtensionAttribute" && attr.AttributeType.Namespace == "System.Runtime.CompilerServices")
return true;
}
}
return false;
}
bool IsVisible(MethodAttributes att) bool IsVisible(MethodAttributes att)
{ {
att &= MethodAttributes.MemberAccessMask; att &= MethodAttributes.MemberAccessMask;
@ -782,14 +983,13 @@ namespace ICSharpCode.NRefactory.TypeSystem
var type = ReadTypeReference(parameter.ParameterType, typeAttributes: parameter, entity: parentMember); var type = ReadTypeReference(parameter.ParameterType, typeAttributes: parameter, entity: parentMember);
DefaultParameter p = new DefaultParameter(type, parameter.Name); DefaultParameter p = new DefaultParameter(type, parameter.Name);
AddAttributes(parameter, p);
if (parameter.ParameterType is Mono.Cecil.ByReferenceType) { if (parameter.ParameterType is Mono.Cecil.ByReferenceType) {
if (parameter.IsOut) if (!parameter.IsIn && parameter.IsOut)
p.IsOut = true; p.IsOut = true;
else else
p.IsRef = true; p.IsRef = true;
} }
AddAttributes(parameter, p);
if (parameter.IsOptional) { if (parameter.IsOptional) {
p.DefaultValue = ReadConstantValue(new CustomAttributeArgument(parameter.ParameterType, parameter.Constant)); p.DefaultValue = ReadConstantValue(new CustomAttributeArgument(parameter.ParameterType, parameter.Constant));
@ -917,10 +1117,10 @@ namespace ICSharpCode.NRefactory.TypeSystem
{ {
if (accessorMethod != null && IsVisible(accessorMethod.Attributes)) { if (accessorMethod != null && IsVisible(accessorMethod.Attributes)) {
Accessibility accessibility = GetAccessibility(accessorMethod.Attributes); Accessibility accessibility = GetAccessibility(accessorMethod.Attributes);
if (accessorMethod.HasCustomAttributes) { if (HasAnyAttributes(accessorMethod)) {
DefaultAccessor a = new DefaultAccessor(); DefaultAccessor a = new DefaultAccessor();
a.Accessibility = accessibility; a.Accessibility = accessibility;
AddAttributes(accessorMethod, a); AddAttributes(accessorMethod, a.Attributes, a.ReturnTypeAttributes);
return a; return a;
} else { } else {
return DefaultAccessor.GetFromAccessibility(accessibility); return DefaultAccessor.GetFromAccessibility(accessibility);

5
NRefactory/ICSharpCode.NRefactory/TypeSystem/IAccessor.cs

@ -21,6 +21,11 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary> /// </summary>
IList<IAttribute> Attributes { get; } IList<IAttribute> Attributes { get; }
/// <summary>
/// Gets the attributes defined on the return type of the accessor. (e.g. [return: MarshalAs(...)])
/// </summary>
IList<IAttribute> ReturnTypeAttributes { get; }
/// <summary> /// <summary>
/// Gets the accessibility of this accessor. /// Gets the accessibility of this accessor.
/// </summary> /// </summary>

6
NRefactory/ICSharpCode.NRefactory/TypeSystem/IAttribute.cs

@ -35,6 +35,12 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// Gets the named arguments passed to the attribute. /// Gets the named arguments passed to the attribute.
/// </summary> /// </summary>
IList<KeyValuePair<string, IConstantValue>> NamedArguments { get; } IList<KeyValuePair<string, IConstantValue>> NamedArguments { get; }
/// <summary>
/// Resolves the constructor method used for this attribute invocation.
/// Returns null if the constructor cannot be found.
/// </summary>
IMethod ResolveConstructor(ITypeResolveContext context);
} }
#if WITH_CONTRACTS #if WITH_CONTRACTS

2
NRefactory/ICSharpCode.NRefactory/TypeSystem/IMethod.cs

@ -16,7 +16,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
public interface IMethod : IParameterizedMember public interface IMethod : IParameterizedMember
{ {
/// <summary> /// <summary>
/// Gets the attributes associated with the return type. /// Gets the attributes associated with the return type. (e.g. [return: MarshalAs(...)])
/// </summary> /// </summary>
IList<IAttribute> ReturnTypeAttributes { get; } IList<IAttribute> ReturnTypeAttributes { get; }

6
NRefactory/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs

@ -44,6 +44,12 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// Gets all members declared in this class. This is the union of Fields,Properties,Methods and Events. /// Gets all members declared in this class. This is the union of Fields,Properties,Methods and Events.
/// </summary> /// </summary>
IEnumerable<IMember> Members { get; } IEnumerable<IMember> Members { get; }
/// <summary>
/// Gets whether this type contains extension methods.
/// </summary>
/// <remarks>This property is used to speed up the search for extension methods.</remarks>
bool HasExtensionMethods { get; }
} }
#if WITH_CONTRACTS #if WITH_CONTRACTS

17
NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultAccessor.cs

@ -44,6 +44,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
Accessibility accessibility; Accessibility accessibility;
DomRegion region; DomRegion region;
IList<IAttribute> attributes; IList<IAttribute> attributes;
IList<IAttribute> returnTypeAttributes;
protected override void FreezeInternal() protected override void FreezeInternal()
{ {
@ -75,20 +76,32 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
} }
} }
public IList<IAttribute> ReturnTypeAttributes {
get {
if (returnTypeAttributes == null)
returnTypeAttributes = new List<IAttribute>();
return returnTypeAttributes;
}
}
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) void ISupportsInterning.PrepareForInterning(IInterningProvider provider)
{ {
attributes = provider.InternList(attributes); attributes = provider.InternList(attributes);
returnTypeAttributes = provider.InternList(returnTypeAttributes);
} }
int ISupportsInterning.GetHashCodeForInterning() int ISupportsInterning.GetHashCodeForInterning()
{ {
return (attributes != null ? attributes.GetHashCode() : 0) ^ region.GetHashCode() ^ (int)accessibility; return (attributes != null ? attributes.GetHashCode() : 0)
^ (returnTypeAttributes != null ? returnTypeAttributes.GetHashCode() : 0)
^ region.GetHashCode() ^ (int)accessibility;
} }
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) bool ISupportsInterning.EqualsForInterning(ISupportsInterning other)
{ {
DefaultAccessor a = other as DefaultAccessor; DefaultAccessor a = other as DefaultAccessor;
return a != null && (attributes == a.attributes && accessibility == a.accessibility && region == a.region); return a != null && (attributes == a.attributes && returnTypeAttributes == a.returnTypeAttributes
&& accessibility == a.accessibility && region == a.region);
} }
} }
} }

60
NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultAttribute.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -13,8 +14,9 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
/// </summary> /// </summary>
public sealed class DefaultAttribute : AbstractFreezable, IAttribute, ISupportsInterning public sealed class DefaultAttribute : AbstractFreezable, IAttribute, ISupportsInterning
{ {
DomRegion region;
ITypeReference attributeType; ITypeReference attributeType;
readonly ITypeReference[] constructorParameterTypes;
DomRegion region;
IList<IConstantValue> positionalArguments; IList<IConstantValue> positionalArguments;
IList<KeyValuePair<string, IConstantValue>> namedArguments; IList<KeyValuePair<string, IConstantValue>> namedArguments;
@ -34,11 +36,20 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
base.FreezeInternal(); base.FreezeInternal();
} }
public DefaultAttribute(ITypeReference attributeType) public DefaultAttribute(ITypeReference attributeType, IEnumerable<ITypeReference> constructorParameterTypes)
{ {
if (attributeType == null) if (attributeType == null)
throw new ArgumentNullException("attributeType"); throw new ArgumentNullException("attributeType");
this.attributeType = attributeType; this.attributeType = attributeType;
this.constructorParameterTypes = constructorParameterTypes != null ? constructorParameterTypes.ToArray() : null;
}
public ITypeReference AttributeType {
get { return attributeType; }
}
public ReadOnlyCollection<ITypeReference> ConstructorParameterTypes {
get { return Array.AsReadOnly(constructorParameterTypes); }
} }
public DomRegion Region { public DomRegion Region {
@ -49,14 +60,6 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
} }
} }
public ITypeReference AttributeType {
get { return attributeType; }
set {
CheckBeforeMutation();
attributeType = value;
}
}
public IList<IConstantValue> PositionalArguments { public IList<IConstantValue> PositionalArguments {
get { get {
if (positionalArguments == null) if (positionalArguments == null)
@ -73,6 +76,38 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
} }
} }
public IMethod ResolveConstructor(ITypeResolveContext context)
{
IType[] parameterTypes = null;
if (constructorParameterTypes != null && constructorParameterTypes.Length > 0) {
parameterTypes = new IType[constructorParameterTypes.Length];
for (int i = 0; i < parameterTypes.Length; i++) {
parameterTypes[i] = constructorParameterTypes[i].Resolve(context);
}
}
IMethod bestMatch = null;
foreach (IMethod ctor in attributeType.Resolve(context).GetConstructors(context)) {
if (ctor.IsStatic)
continue;
if (parameterTypes == null) {
if (ctor.Parameters.Count == 0)
return ctor;
} else if (ctor.Parameters.Count == parameterTypes.Length) {
bestMatch = ctor;
bool ok = true;
for (int i = 0; i < parameterTypes.Length; i++) {
if (ctor.Parameters[i].Type != parameterTypes[i]) {
ok = false;
break;
}
}
if (ok)
return ctor;
}
}
return bestMatch;
}
public override string ToString() public override string ToString()
{ {
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
@ -100,6 +135,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) void ISupportsInterning.PrepareForInterning(IInterningProvider provider)
{ {
attributeType = provider.Intern(attributeType); attributeType = provider.Intern(attributeType);
if (constructorParameterTypes != null) {
for (int i = 0; i < constructorParameterTypes.Length; i++) {
constructorParameterTypes[i] = provider.Intern(constructorParameterTypes[i]);
}
}
positionalArguments = provider.InternList(positionalArguments); positionalArguments = provider.InternList(positionalArguments);
} }

9
NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeDefinition.cs

@ -40,6 +40,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
const ushort FlagShadowing = 0x0004; const ushort FlagShadowing = 0x0004;
const ushort FlagSynthetic = 0x0008; const ushort FlagSynthetic = 0x0008;
const ushort FlagAddDefaultConstructorIfRequired = 0x0010; const ushort FlagAddDefaultConstructorIfRequired = 0x0010;
const ushort FlagHasExtensionMethods = 0x0020;
protected override void FreezeInternal() protected override void FreezeInternal()
{ {
@ -312,6 +313,14 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
} }
} }
public bool HasExtensionMethods {
get { return flags[FlagHasExtensionMethods]; }
set {
CheckBeforeMutation();
flags[FlagHasExtensionMethods] = value;
}
}
public IProjectContent ProjectContent { public IProjectContent ProjectContent {
get { return projectContent; } get { return projectContent; }
} }

Loading…
Cancel
Save