Browse Source

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

pull/205/head
Pent Ploompuu 15 years ago
parent
commit
7286fba6a4
  1. 1
      AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
  2. 214
      ICSharpCode.Decompiler/Ast/AstBuilder.cs
  3. 17
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  4. 2
      ICSharpCode.Decompiler/Ast/NameVariables.cs
  5. 14
      ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs
  6. 56
      ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs
  7. 58
      ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs
  8. 15
      ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
  9. 127
      ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
  10. 7
      ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs
  11. 1
      ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs
  12. 250
      ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
  13. 6
      ICSharpCode.Decompiler/Disassembler/ILStructure.cs
  14. 84
      ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs
  15. 691
      ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
  16. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  17. 10
      ICSharpCode.Decompiler/ILAst/GotoRemoval.cs
  18. 4
      ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs
  19. 21
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  20. 18
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  21. 32
      ICSharpCode.Decompiler/ILAst/ILInlining.cs
  22. 4
      ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs
  23. 75
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  24. 107
      ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs
  25. 4
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  26. 75
      ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs
  27. 30
      ICSharpCode.Decompiler/Tests/ExceptionHandling.cs
  28. 6
      ICSharpCode.Decompiler/Tests/Generics.cs
  29. 2
      ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs
  30. 2
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  31. 6
      ICSharpCode.Decompiler/Tests/IncrementDecrement.cs
  32. 87
      ICSharpCode.Decompiler/Tests/PInvoke.cs
  33. 14
      ICSharpCode.Decompiler/Tests/TestRunner.cs
  34. 4
      ILSpy/AboutPage.cs
  35. 2
      ILSpy/App.xaml.cs
  36. 2
      ILSpy/BamlDecompiler.cs
  37. 200
      ILSpy/CSharpLanguage.cs
  38. 2
      ILSpy/Commands.cs
  39. 19
      ILSpy/ConnectMethodDecompiler.cs
  40. 93
      ILSpy/DisplaySettings.cs
  41. 64
      ILSpy/DisplaySettingsPanel.xaml
  42. 154
      ILSpy/DisplaySettingsPanel.xaml.cs
  43. 4
      ILSpy/ILAstLanguage.cs
  44. 44
      ILSpy/ILLanguage.cs
  45. 13
      ILSpy/ILSpy.csproj
  46. 21
      ILSpy/ILSpySettings.cs
  47. 5
      ILSpy/Images/Images.cs
  48. BIN
      ILSpy/Images/StaticClass.png
  49. 3
      ILSpy/Images/TypeIcon.cs
  50. 69
      ILSpy/Language.cs
  51. 15
      ILSpy/MainWindow.xaml.cs
  52. 12
      ILSpy/NavigationHistory.cs
  53. 10
      ILSpy/NavigationState.cs
  54. 29
      ILSpy/TextView/DecompilerTextView.cs
  55. 1
      ILSpy/TextView/ILAsm-Mode.xshd
  56. 43
      ILSpy/TreeNodes/Analyzer/AnalyzedEventAccessorTreeNode.cs
  57. 33
      ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs
  58. 17
      ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs
  59. 34
      ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessTreeNode.cs
  60. 4
      ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceEventImplementedByTreeNode.cs
  61. 4
      ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceMethodImplementedByTreeNode.cs
  62. 4
      ILSpy/TreeNodes/Analyzer/AnalyzedInterfacePropertyImplementedByTreeNode.cs
  63. 51
      ILSpy/TreeNodes/Analyzer/AnalyzedMethodOverridesTreeNode.cs
  64. 8
      ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs
  65. 28
      ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs
  66. 43
      ILSpy/TreeNodes/Analyzer/AnalyzedPropertyAccessorTreeNode.cs
  67. 40
      ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs
  68. 13
      ILSpy/TreeNodes/Analyzer/AnalyzedPropertyTreeNode.cs
  69. 4
      ILSpy/TreeNodes/Analyzer/AnalyzedTypeExposedByTreeNode.cs
  70. 4
      ILSpy/TreeNodes/Analyzer/AnalyzedTypeExtensionMethodsTreeNode.cs
  71. 4
      ILSpy/TreeNodes/Analyzer/AnalyzedTypeInstantiationsTreeNode.cs
  72. 151
      ILSpy/TreeNodes/Analyzer/AnalyzedVirtualMethodUsedByTreeNode.cs
  73. 63
      ILSpy/TreeNodes/Analyzer/Helpers.cs
  74. 89
      ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs
  75. 4
      ILSpy/TreeNodes/AssemblyTreeNode.cs
  76. 2
      ILSpy/TreeNodes/ResourceEntryNode.cs
  77. 2
      ILSpy/TreeNodes/ResourceTreeNode.cs
  78. 19
      ILSpy/TreeNodes/TypeTreeNode.cs
  79. 2
      ILSpy/TreeNodes/XamlResourceNode.cs
  80. 4
      ILSpy/XmlDoc/XmlDocKeyProvider.cs
  81. 12
      NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs
  82. 4
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs
  83. 2
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Identifier.cs
  84. 17
      NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs
  85. 4
      NRefactory/ICSharpCode.NRefactory/CSharp/Parser/TypeSystemConvertVisitor.cs
  86. 4
      NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs
  87. 2
      NRefactory/ICSharpCode.NRefactory/Documentation/XmlDocumentationProvider.cs
  88. 14
      SharpTreeView/SharpTreeNode.cs
  89. 17
      SharpTreeView/SharpTreeView.cs

1
AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj

@ -215,6 +215,7 @@ @@ -215,6 +215,7 @@
<Compile Include="Indentation\CSharp\DocumentAccessor.cs" />
<Compile Include="Indentation\DefaultIndentationStrategy.cs" />
<Compile Include="Indentation\IIndentationStrategy.cs" />
<Compile Include="Properties\GlobalAssemblyInfo.cs" />
<Compile Include="Rendering\BackgroundGeometryBuilder.cs">
<DependentUpon>IBackgroundRenderer.cs</DependentUpon>
</Compile>

214
ICSharpCode.Decompiler/Ast/AstBuilder.cs

@ -125,8 +125,28 @@ namespace ICSharpCode.Decompiler.Ast @@ -125,8 +125,28 @@ namespace ICSharpCode.Decompiler.Ast
public void AddAssembly(AssemblyDefinition assemblyDefinition, bool onlyAssemblyLevel = false)
{
if (assemblyDefinition.Name.Version != null) {
astCompileUnit.AddChild(
new AttributeSection {
AttributeTarget = "assembly",
Attributes = {
new NRefactory.CSharp.Attribute {
Type = new SimpleType("AssemblyVersion")
.WithAnnotation(new TypeReference(
"System.Reflection", "AssemblyVersionAttribute",
assemblyDefinition.MainModule, assemblyDefinition.MainModule.TypeSystem.Corlib)),
Arguments = {
new PrimitiveExpression(assemblyDefinition.Name.Version.ToString())
}
}
}
}, AttributedNode.AttributeRole);
}
ConvertCustomAttributes(astCompileUnit, assemblyDefinition, "assembly");
ConvertSecurityAttributes(astCompileUnit, assemblyDefinition, "assembly");
ConvertCustomAttributes(astCompileUnit, assemblyDefinition.MainModule, "module");
AddTypeForwarderAttributes(astCompileUnit, assemblyDefinition.MainModule, "assembly");
if (!onlyAssemblyLevel) {
foreach (TypeDefinition typeDef in assemblyDefinition.MainModule.Types) {
@ -141,6 +161,30 @@ namespace ICSharpCode.Decompiler.Ast @@ -141,6 +161,30 @@ namespace ICSharpCode.Decompiler.Ast
}
}
void AddTypeForwarderAttributes(CompilationUnit astCompileUnit, ModuleDefinition module, string target)
{
if (!module.HasExportedTypes)
return;
foreach (ExportedType type in module.ExportedTypes) {
if (type.IsForwarder) {
var forwardedType = CreateTypeOfExpression(new TypeReference(type.Namespace, type.Name, module, type.Scope));
astCompileUnit.AddChild(
new AttributeSection {
AttributeTarget = target,
Attributes = {
new NRefactory.CSharp.Attribute {
Type = new SimpleType("TypeForwardedTo")
.WithAnnotation(new TypeReference(
"System.Runtime.CompilerServices", "TypeForwardedToAttribute",
module, module.TypeSystem.Corlib)),
Arguments = { forwardedType }
}
}
}, AttributedNode.AttributeRole);
}
}
}
NamespaceDeclaration GetCodeNamespace(string name)
{
if (string.IsNullOrEmpty(name)) {
@ -228,15 +272,6 @@ namespace ICSharpCode.Decompiler.Ast @@ -228,15 +272,6 @@ namespace ICSharpCode.Decompiler.Ast
astType.TypeParameters.AddRange(MakeTypeParameters(genericParameters));
astType.Constraints.AddRange(MakeConstraints(genericParameters));
// Nested types
foreach (TypeDefinition nestedTypeDef in typeDef.NestedTypes) {
if (MemberIsHidden(nestedTypeDef, context.Settings))
continue;
var nestedType = CreateType(nestedTypeDef);
SetNewModifier(nestedType);
astType.AddChild(nestedType, TypeDeclaration.MemberRole);
}
AttributedNode result = astType;
if (typeDef.IsEnum) {
long expectedEnumMemberValue = 0;
@ -314,6 +349,45 @@ namespace ICSharpCode.Decompiler.Ast @@ -314,6 +349,45 @@ namespace ICSharpCode.Decompiler.Ast
return name;
}
#region Create TypeOf Expression
/// <summary>
/// Creates a typeof-expression for the specified type.
/// </summary>
public static TypeOfExpression CreateTypeOfExpression(TypeReference type)
{
return new TypeOfExpression(AddEmptyTypeArgumentsForUnboundGenerics(ConvertType(type)));
}
static AstType AddEmptyTypeArgumentsForUnboundGenerics(AstType type)
{
TypeReference typeRef = type.Annotation<TypeReference>();
if (typeRef == null)
return type;
TypeDefinition typeDef = typeRef.Resolve(); // need to resolve to figure out the number of type parameters
if (typeDef == null || !typeDef.HasGenericParameters)
return type;
SimpleType sType = type as SimpleType;
MemberType mType = type as MemberType;
if (sType != null) {
while (typeDef.GenericParameters.Count > sType.TypeArguments.Count) {
sType.TypeArguments.Add(new SimpleType(""));
}
}
if (mType != null) {
AddEmptyTypeArgumentsForUnboundGenerics(mType.Target);
int outerTypeParamCount = typeDef.DeclaringType == null ? 0 : typeDef.DeclaringType.GenericParameters.Count;
while (typeDef.GenericParameters.Count - outerTypeParamCount > mType.TypeArguments.Count) {
mType.TypeArguments.Add(new SimpleType(""));
}
}
return type;
}
#endregion
#region Convert Type Reference
/// <summary>
/// Converts a type reference.
@ -597,6 +671,15 @@ namespace ICSharpCode.Decompiler.Ast @@ -597,6 +671,15 @@ namespace ICSharpCode.Decompiler.Ast
void AddTypeMembers(TypeDeclaration astType, TypeDefinition typeDef)
{
// Nested types
foreach (TypeDefinition nestedTypeDef in typeDef.NestedTypes) {
if (MemberIsHidden(nestedTypeDef, context.Settings))
continue;
var nestedType = CreateType(nestedTypeDef);
SetNewModifier(nestedType);
astType.AddChild(nestedType, TypeDeclaration.MemberRole);
}
// Add fields
foreach(FieldDefinition fieldDef in typeDef.Fields) {
if (MemberIsHidden(fieldDef, context.Settings)) continue;
@ -729,6 +812,9 @@ namespace ICSharpCode.Decompiler.Ast @@ -729,6 +812,9 @@ namespace ICSharpCode.Decompiler.Ast
astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters);
ConvertAttributes(astMethod, methodDef);
astMethod.WithAnnotation(methodMapping);
if (methodDef.IsStatic && methodDef.DeclaringType.IsBeforeFieldInit && !astMethod.Body.IsNull) {
astMethod.Body.InsertChildAfter(null, new Comment(" Note: this type is marked as 'beforefieldinit'."), AstNode.Roles.Comment);
}
return astMethod;
}
@ -798,7 +884,13 @@ namespace ICSharpCode.Decompiler.Ast @@ -798,7 +884,13 @@ namespace ICSharpCode.Decompiler.Ast
astProp.Setter.Body = CreateMethodBody(propDef.SetMethod);
astProp.Setter.AddAnnotation(propDef.SetMethod);
ConvertAttributes(astProp.Setter, propDef.SetMethod);
ConvertCustomAttributes(astProp.Setter, propDef.SetMethod.Parameters.Last(), "param");
ParameterDefinition lastParam = propDef.SetMethod.Parameters.LastOrDefault();
if (lastParam != null) {
ConvertCustomAttributes(astProp.Setter, lastParam, "param");
if (lastParam.HasMarshalInfo) {
astProp.Setter.Attributes.Add(new AttributeSection(ConvertMarshalInfo(lastParam, propDef.Module)) { AttributeTarget = "param" });
}
}
if ((setterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask))
astProp.Setter.Modifiers = setterModifiers & Modifiers.VisibilityMask;
@ -981,6 +1073,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -981,6 +1073,7 @@ namespace ICSharpCode.Decompiler.Ast
void ConvertAttributes(AttributedNode attributedNode, TypeDefinition typeDefinition)
{
ConvertCustomAttributes(attributedNode, typeDefinition);
ConvertSecurityAttributes(attributedNode, typeDefinition);
// Handle the non-custom attributes:
#region SerializableAttribute
@ -1031,6 +1124,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -1031,6 +1124,7 @@ namespace ICSharpCode.Decompiler.Ast
void ConvertAttributes(AttributedNode attributedNode, MethodDefinition methodDefinition)
{
ConvertCustomAttributes(attributedNode, methodDefinition);
ConvertSecurityAttributes(attributedNode, methodDefinition);
MethodImplAttributes implAttributes = methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask;
@ -1167,6 +1261,37 @@ namespace ICSharpCode.Decompiler.Ast @@ -1167,6 +1261,37 @@ namespace ICSharpCode.Decompiler.Ast
Ast.Attribute attr = CreateNonCustomAttribute(typeof(MarshalAsAttribute), module);
var unmanagedType = new TypeReference("System.Runtime.InteropServices", "UnmanagedType", module, module.TypeSystem.Corlib);
attr.Arguments.Add(MakePrimitive((int)marshalInfo.NativeType, unmanagedType));
FixedArrayMarshalInfo fami = marshalInfo as FixedArrayMarshalInfo;
if (fami != null) {
attr.AddNamedArgument("SizeConst", new PrimitiveExpression(fami.Size));
if (fami.ElementType != NativeType.None)
attr.AddNamedArgument("ArraySubType", MakePrimitive((int)fami.ElementType, unmanagedType));
}
SafeArrayMarshalInfo sami = marshalInfo as SafeArrayMarshalInfo;
if (sami != null && sami.ElementType != VariantType.None) {
var varEnum = new TypeReference("System.Runtime.InteropServices", "VarEnum", module, module.TypeSystem.Corlib);
attr.AddNamedArgument("SafeArraySubType", MakePrimitive((int)sami.ElementType, varEnum));
}
ArrayMarshalInfo ami = marshalInfo as ArrayMarshalInfo;
if (ami != null) {
if (ami.ElementType != NativeType.Max)
attr.AddNamedArgument("ArraySubType", MakePrimitive((int)ami.ElementType, unmanagedType));
if (ami.Size >= 0)
attr.AddNamedArgument("SizeConst", new PrimitiveExpression(ami.Size));
if (ami.SizeParameterMultiplier != 0 && ami.SizeParameterIndex >= 0)
attr.AddNamedArgument("SizeParamIndex", new PrimitiveExpression(ami.SizeParameterIndex));
}
CustomMarshalInfo cmi = marshalInfo as CustomMarshalInfo;
if (cmi != null) {
attr.AddNamedArgument("MarshalType", new PrimitiveExpression(cmi.ManagedType.FullName));
if (!string.IsNullOrEmpty(cmi.Cookie))
attr.AddNamedArgument("MarshalCookie", new PrimitiveExpression(cmi.Cookie));
}
FixedSysStringMarshalInfo fssmi = marshalInfo as FixedSysStringMarshalInfo;
if (fssmi != null) {
attr.AddNamedArgument("SizeConst", new PrimitiveExpression(fssmi.Size));
}
return attr;
}
#endregion
@ -1256,6 +1381,65 @@ namespace ICSharpCode.Decompiler.Ast @@ -1256,6 +1381,65 @@ namespace ICSharpCode.Decompiler.Ast
}
}
static void ConvertSecurityAttributes(AstNode attributedNode, ISecurityDeclarationProvider secDeclProvider, string attributeTarget = null)
{
if (!secDeclProvider.HasSecurityDeclarations)
return;
var attributes = new List<ICSharpCode.NRefactory.CSharp.Attribute>();
foreach (var secDecl in secDeclProvider.SecurityDeclarations) {
foreach (var secAttribute in secDecl.SecurityAttributes) {
var attribute = new ICSharpCode.NRefactory.CSharp.Attribute();
attribute.AddAnnotation(secAttribute);
attribute.Type = ConvertType(secAttribute.AttributeType);
attributes.Add(attribute);
SimpleType st = attribute.Type as SimpleType;
if (st != null && st.Identifier.EndsWith("Attribute", StringComparison.Ordinal)) {
st.Identifier = st.Identifier.Substring(0, st.Identifier.Length - "Attribute".Length);
}
var module = secAttribute.AttributeType.Module;
var securityActionType = new TypeReference("System.Security.Permissions", "SecurityAction", module, module.TypeSystem.Corlib);
attribute.Arguments.Add(MakePrimitive((int)secDecl.Action, securityActionType));
if (secAttribute.HasProperties) {
TypeDefinition resolvedAttributeType = secAttribute.AttributeType.Resolve();
foreach (var propertyNamedArg in secAttribute.Properties) {
var propertyReference = resolvedAttributeType != null ? resolvedAttributeType.Properties.FirstOrDefault(pr => pr.Name == propertyNamedArg.Name) : null;
var propertyName = new IdentifierExpression(propertyNamedArg.Name).WithAnnotation(propertyReference);
var argumentValue = ConvertArgumentValue(propertyNamedArg.Argument);
attribute.Arguments.Add(new AssignmentExpression(propertyName, argumentValue));
}
}
if (secAttribute.HasFields) {
TypeDefinition resolvedAttributeType = secAttribute.AttributeType.Resolve();
foreach (var fieldNamedArg in secAttribute.Fields) {
var fieldReference = resolvedAttributeType != null ? resolvedAttributeType.Fields.FirstOrDefault(f => f.Name == fieldNamedArg.Name) : null;
var fieldName = new IdentifierExpression(fieldNamedArg.Name).WithAnnotation(fieldReference);
var argumentValue = ConvertArgumentValue(fieldNamedArg.Argument);
attribute.Arguments.Add(new AssignmentExpression(fieldName, argumentValue));
}
}
}
}
if (attributeTarget == "module" || attributeTarget == "assembly") {
// use separate section for each attribute
foreach (var attribute in attributes) {
var section = new AttributeSection();
section.AttributeTarget = attributeTarget;
section.Attributes.Add(attribute);
attributedNode.AddChild(section, AttributedNode.AttributeRole);
}
} else if (attributes.Count > 0) {
// use single section for all attributes
var section = new AttributeSection();
section.AttributeTarget = attributeTarget;
section.Attributes.AddRange(attributes);
attributedNode.AddChild(section, AttributedNode.AttributeRole);
}
}
private static Expression ConvertArgumentValue(CustomAttributeArgument argument)
{
if (argument.Value is CustomAttributeArgument[]) {
@ -1276,9 +1460,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -1276,9 +1460,7 @@ namespace ICSharpCode.Decompiler.Ast
if (type != null && type.IsEnum) {
return MakePrimitive(Convert.ToInt64(argument.Value), type);
} else if (argument.Value is TypeReference) {
return new TypeOfExpression() {
Type = ConvertType((TypeReference)argument.Value),
};
return CreateTypeOfExpression((TypeReference)argument.Value);
} else {
return new PrimitiveExpression(argument.Value);
}
@ -1433,7 +1615,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -1433,7 +1615,7 @@ namespace ICSharpCode.Decompiler.Ast
if (baseType.HasFields && AnyIsHiddenBy(baseType.Fields, member))
return true;
if (includeBaseMethods && baseType.HasMethods
&& AnyIsHiddenBy(baseType.Methods, member, m => !m.IsSpecialName))
&& AnyIsHiddenBy(baseType.Methods, member, m => !m.IsSpecialName))
return true;
if (baseType.HasNestedTypes && AnyIsHiddenBy(baseType.NestedTypes, member))
return true;
@ -1447,8 +1629,8 @@ namespace ICSharpCode.Decompiler.Ast @@ -1447,8 +1629,8 @@ namespace ICSharpCode.Decompiler.Ast
where T : IMemberDefinition
{
return members.Any(m => m.Name == derived.Name
&& (condition == null || condition(m))
&& TypesHierarchyHelpers.IsVisibleFromDerived(m, derived.DeclaringType));
&& (condition == null || condition(m))
&& TypesHierarchyHelpers.IsVisibleFromDerived(m, derived.DeclaringType));
}
/// <summary>

17
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -490,9 +490,12 @@ namespace ICSharpCode.Decompiler.Ast @@ -490,9 +490,12 @@ namespace ICSharpCode.Decompiler.Ast
return arg1;
else
return arg1.CastTo(operandAsTypeRef);
case ILCode.Isinst: return arg1.CastAs(operandAsTypeRef);
case ILCode.Box: return arg1;
case ILCode.Unbox: return InlineAssembly(byteCode, args);
case ILCode.Isinst:
return arg1.CastAs(operandAsTypeRef);
case ILCode.Box:
return arg1;
case ILCode.Unbox:
return MakeRef(arg1.CastTo(operandAsTypeRef));
#endregion
#region Indirect
case ILCode.Ldind_Ref:
@ -544,11 +547,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -544,11 +547,7 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Endfilter: return InlineAssembly(byteCode, args);
case ILCode.Endfinally: return null;
case ILCode.Initblk: return InlineAssembly(byteCode, args);
case ILCode.Initobj:
if (args[0] is DirectionExpression)
return new AssignmentExpression(((DirectionExpression)args[0]).Expression.Detach(), MakeDefaultValue((TypeReference)operand));
else
return InlineAssembly(byteCode, args);
case ILCode.Initobj: return InlineAssembly(byteCode, args);
case ILCode.DefaultValue:
return MakeDefaultValue((TypeReference)operand);
case ILCode.Jmp: return InlineAssembly(byteCode, args);
@ -607,7 +606,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -607,7 +606,7 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Ldstr: return new Ast.PrimitiveExpression(operand);
case ILCode.Ldtoken:
if (operand is Cecil.TypeReference) {
return new Ast.TypeOfExpression { Type = operandAsTypeRef }.Member("TypeHandle");
return AstBuilder.CreateTypeOfExpression((TypeReference)operand).Member("TypeHandle");
} else {
return InlineAssembly(byteCode, args);
}

2
ICSharpCode.Decompiler/Ast/NameVariables.cs

@ -279,6 +279,8 @@ namespace ICSharpCode.Decompiler.Ast @@ -279,6 +279,8 @@ namespace ICSharpCode.Decompiler.Ast
name = "array";
} else if (type.IsPointer || type.IsByReference) {
name = "ptr";
} else if (type.Name.EndsWith("Exception", StringComparison.Ordinal)) {
name = "ex";
} else if (!typeNameToVariableNameDict.TryGetValue(type.FullName, out name)) {
name = type.Name;
// remove the 'I' for interfaces

14
ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs

@ -17,6 +17,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -17,6 +17,7 @@ namespace ICSharpCode.Decompiler.Ast
readonly ITextOutput output;
readonly Stack<AstNode> nodeStack = new Stack<AstNode>();
int braceLevelWithinType = -1;
bool inDocumentationComment = false;
public TextOutputFormatter(ITextOutput output)
{
@ -115,8 +116,17 @@ namespace ICSharpCode.Decompiler.Ast @@ -115,8 +116,17 @@ namespace ICSharpCode.Decompiler.Ast
output.Write("*/");
break;
case CommentType.Documentation:
if (!inDocumentationComment)
output.MarkFoldStart("///" + content, true);
output.Write("///");
output.WriteLine(content);
output.Write(content);
inDocumentationComment = true;
bool isLastLine = !(nodeStack.Peek().NextSibling is Comment);
if (isLastLine) {
inDocumentationComment = false;
output.MarkFoldEnd();
}
output.WriteLine();
break;
}
}
@ -127,7 +137,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -127,7 +137,7 @@ namespace ICSharpCode.Decompiler.Ast
if (ranges != null && ranges.Count > 0)
{
// find the ancestor that has method mapping as annotation
if (node.Ancestors != null && node.Ancestors.Count() > 0)
if (node.Parent != null)
{
var n = node.Ancestors.FirstOrDefault(a => a.Annotation<MemberMapping>() != null);
if (n != null) {

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

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
@ -69,9 +70,29 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -69,9 +70,29 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
{
var instanceCtors = typeDeclaration.Members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
// Handle initializers on instance fields
HandleInstanceFieldInitializers(typeDeclaration.Members);
// Now convert base constructor calls to initializers:
base.VisitTypeDeclaration(typeDeclaration, data);
// Remove single empty constructor:
RemoveSingleEmptyConstructor(typeDeclaration);
// Handle initializers on static fields:
HandleStaticFieldInitializers(typeDeclaration.Members);
return null;
}
void HandleInstanceFieldInitializers(IEnumerable<AstNode> members)
{
var instanceCtors = members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray();
if (instanceCtorsNotChainingWithThis.Length > 0 && typeDeclaration.ClassType == NRefactory.TypeSystem.ClassType.Class) {
if (instanceCtorsNotChainingWithThis.Length > 0) {
MethodDefinition ctorMethodDef = instanceCtorsNotChainingWithThis[0].Annotation<MethodDefinition>();
if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsValueType)
return;
// Recognize field initializers:
// Convert first statement in all ctors (if all ctors have the same statement) into a field initializer.
bool allSame;
@ -83,7 +104,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -83,7 +104,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
FieldDefinition fieldDef = m.Get<AstNode>("fieldAccess").Single().Annotation<FieldReference>().ResolveWithinSameModule();
if (fieldDef == null)
break;
AttributedNode fieldOrEventDecl = typeDeclaration.Members.FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
AstNode fieldOrEventDecl = members.FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
if (fieldOrEventDecl == null)
break;
@ -99,11 +120,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -99,11 +120,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
} while (allSame);
}
// Now convert base constructor calls to initializers:
base.VisitTypeDeclaration(typeDeclaration, data);
// Remove single empty constructor:
}
void RemoveSingleEmptyConstructor(TypeDeclaration typeDeclaration)
{
var instanceCtors = typeDeclaration.Members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
if (instanceCtors.Length == 1) {
ConstructorDeclaration emptyCtor = new ConstructorDeclaration();
emptyCtor.Modifiers = ((typeDeclaration.Modifiers & Modifiers.Abstract) == Modifiers.Abstract ? Modifiers.Protected : Modifiers.Public);
@ -111,12 +132,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -111,12 +132,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
if (emptyCtor.IsMatch(instanceCtors[0]))
instanceCtors[0].Remove();
}
}
void HandleStaticFieldInitializers(IEnumerable<AstNode> members)
{
// Convert static constructor into field initializers if the class is BeforeFieldInit
var staticCtor = typeDeclaration.Members.OfType<ConstructorDeclaration>().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static);
var staticCtor = members.OfType<ConstructorDeclaration>().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static);
if (staticCtor != null) {
TypeDefinition typeDef = typeDeclaration.Annotation<TypeDefinition>();
if (typeDef != null && typeDef.IsBeforeFieldInit) {
MethodDefinition ctorMethodDef = staticCtor.Annotation<MethodDefinition>();
if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsBeforeFieldInit) {
while (true) {
ExpressionStatement es = staticCtor.Body.Statements.FirstOrDefault() as ExpressionStatement;
if (es == null)
@ -127,7 +151,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -127,7 +151,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
FieldDefinition fieldDef = assignment.Left.Annotation<FieldReference>().ResolveWithinSameModule();
if (fieldDef == null || !fieldDef.IsStatic)
break;
FieldDeclaration fieldDecl = typeDeclaration.Members.OfType<FieldDeclaration>().FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
FieldDeclaration fieldDecl = members.OfType<FieldDeclaration>().FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
if (fieldDecl == null)
break;
fieldDecl.Variables.Single().Initializer = assignment.Right.Detach();
@ -137,11 +161,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -137,11 +161,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
staticCtor.Remove();
}
}
return null;
}
void IAstTransform.Run(AstNode node)
{
// If we're viewing some set of members (fields are direct children of CompilationUnit),
// we also need to handle those:
HandleInstanceFieldInitializers(node.Children);
HandleStaticFieldInitializers(node.Children);
node.AcceptVisitor(this, null);
}
}

58
ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs

@ -0,0 +1,58 @@ @@ -0,0 +1,58 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Transforms decimal constant fields.
/// </summary>
public class DecimalConstantTransform : DepthFirstAstVisitor<object, object>, IAstTransform
{
static readonly PrimitiveType decimalType = new PrimitiveType("decimal");
public override object VisitFieldDeclaration(FieldDeclaration fieldDeclaration, object data)
{
const Modifiers staticReadOnly = Modifiers.Static | Modifiers.Readonly;
if ((fieldDeclaration.Modifiers & staticReadOnly) == staticReadOnly && decimalType.IsMatch(fieldDeclaration.ReturnType)) {
foreach (var attributeSection in fieldDeclaration.Attributes) {
foreach (var attribute in attributeSection.Attributes) {
TypeReference tr = attribute.Type.Annotation<TypeReference>();
if (tr != null && tr.Name == "DecimalConstantAttribute" && tr.Namespace == "System.Runtime.CompilerServices") {
attribute.Remove();
if (attributeSection.Attributes.Count == 0)
attributeSection.Remove();
fieldDeclaration.Modifiers = (fieldDeclaration.Modifiers & ~staticReadOnly) | Modifiers.Const;
return null;
}
}
}
}
return null;
}
public void Run(AstNode compilationUnit)
{
compilationUnit.AcceptVisitor(this, null);
}
}
}

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

@ -61,7 +61,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -61,7 +61,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public override object VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, object data)
{
if (objectCreateExpression.Arguments.Count() == 2) {
if (objectCreateExpression.Arguments.Count == 2) {
Expression obj = objectCreateExpression.Arguments.First();
Expression func = objectCreateExpression.Arguments.Last();
Annotation annotation = func.Annotation<Annotation>();
@ -115,7 +115,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -115,7 +115,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
internal static bool IsAnonymousMethod(DecompilerContext context, MethodDefinition method)
{
if (method == null || !method.Name.StartsWith("<", StringComparison.Ordinal))
if (method == null || !(method.Name.StartsWith("<", StringComparison.Ordinal) || method.Name.Contains("$")))
return false;
if (!(method.IsCompilerGenerated() || IsPotentialClosure(context, method.DeclaringType)))
return false;
@ -388,10 +388,13 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -388,10 +388,13 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
continue; // skip static fields
if (dict.ContainsKey(field)) // skip field if it already was handled as parameter
continue;
EnsureVariableNameIsAvailable(blockStatement, field.Name);
currentlyUsedVariableNames.Add(field.Name);
variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), field.Name));
dict[field] = new IdentifierExpression(field.Name);
string capturedVariableName = field.Name;
if (capturedVariableName.StartsWith("$VB$Local_", StringComparison.Ordinal) && capturedVariableName.Length > 10)
capturedVariableName = capturedVariableName.Substring(10);
EnsureVariableNameIsAvailable(blockStatement, capturedVariableName);
currentlyUsedVariableNames.Add(capturedVariableName);
variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), capturedVariableName));
dict[field] = new IdentifierExpression(capturedVariableName);
}
// Now figure out where the closure was accessed and use the simpler replacement expression there:

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

@ -154,7 +154,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -154,7 +154,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
static readonly AstNode usingTryCatchPattern = new TryCatchStatement {
TryBlock = new AnyNode("body"),
TryBlock = new AnyNode(),
FinallyBlock = new BlockStatement {
new Choice {
{ "valueType",
@ -180,44 +180,60 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -180,44 +180,60 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
{
Match m1 = variableAssignPattern.Match(node);
if (!m1.Success) return null;
AstNode tryCatch = node.NextSibling;
TryCatchStatement tryCatch = node.NextSibling as TryCatchStatement;
Match m2 = usingTryCatchPattern.Match(tryCatch);
if (!m2.Success) return null;
string variableName = m1.Get<IdentifierExpression>("variable").Single().Identifier;
if (variableName == m2.Get<IdentifierExpression>("ident").Single().Identifier) {
if (m2.Has("valueType")) {
// if there's no if(x!=null), then it must be a value type
ILVariable v = m1.Get<AstNode>("variable").Single().Annotation<ILVariable>();
if (v == null || v.Type == null || !v.Type.IsValueType)
return null;
}
node.Remove();
BlockStatement body = m2.Get<BlockStatement>("body").Single();
UsingStatement usingStatement = new UsingStatement();
usingStatement.ResourceAcquisition = node.Expression.Detach();
usingStatement.EmbeddedStatement = body.Detach();
tryCatch.ReplaceWith(usingStatement);
// Move the variable declaration into the resource acquisition, if possible
// This is necessary for the foreach-pattern to work on the result of the using-pattern
VariableDeclarationStatement varDecl = FindVariableDeclaration(usingStatement, variableName);
if (varDecl != null && varDecl.Parent is BlockStatement) {
Statement declarationPoint;
if (CanMoveVariableDeclarationIntoStatement(varDecl, usingStatement, out declarationPoint)) {
// Moving the variable into the UsingStatement is allowed:
usingStatement.ResourceAcquisition = new VariableDeclarationStatement {
Type = (AstType)varDecl.Type.Clone(),
Variables = {
new VariableInitializer {
Name = variableName,
Initializer = m1.Get<Expression>("initializer").Single().Detach()
}.CopyAnnotationsFrom(usingStatement.ResourceAcquisition)
}
}.CopyAnnotationsFrom(node);
if (variableName != m2.Get<IdentifierExpression>("ident").Single().Identifier)
return null;
if (m2.Has("valueType")) {
// if there's no if(x!=null), then it must be a value type
ILVariable v = m1.Get<AstNode>("variable").Single().Annotation<ILVariable>();
if (v == null || v.Type == null || !v.Type.IsValueType)
return null;
}
// There are two variants of the using statement:
// "using (var a = init)" and "using (expr)".
// The former declares a read-only variable 'a', and the latter declares an unnamed read-only variable
// to store the original value of 'expr'.
// This means that in order to introduce a using statement, in both cases we need to detect a read-only
// variable that is used only within that block.
if (HasAssignment(tryCatch, variableName))
return null;
VariableDeclarationStatement varDecl = FindVariableDeclaration(node, variableName);
if (varDecl == null || !(varDecl.Parent is BlockStatement))
return null;
// Validate that the variable is not used after the using statement:
if (!IsVariableValueUnused(varDecl, tryCatch))
return null;
node.Remove();
UsingStatement usingStatement = new UsingStatement();
usingStatement.EmbeddedStatement = tryCatch.TryBlock.Detach();
tryCatch.ReplaceWith(usingStatement);
// If possible, we'll eliminate the variable completely:
if (usingStatement.EmbeddedStatement.Descendants.OfType<IdentifierExpression>().Any(ident => ident.Identifier == variableName)) {
// variable is used, so we'll create a variable declaration
usingStatement.ResourceAcquisition = new VariableDeclarationStatement {
Type = (AstType)varDecl.Type.Clone(),
Variables = {
new VariableInitializer {
Name = variableName,
Initializer = m1.Get<Expression>("initializer").Single().Detach()
}.CopyAnnotationsFrom(node.Expression)
}
}
return usingStatement;
}.CopyAnnotationsFrom(node);
} else {
// the variable is never used; eliminate it:
usingStatement.ResourceAcquisition = m1.Get<Expression>("initializer").Single().Detach();
}
return null;
return usingStatement;
}
internal static VariableDeclarationStatement FindVariableDeclaration(AstNode node, string identifier)
@ -235,6 +251,29 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -235,6 +251,29 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return null;
}
/// <summary>
/// Gets whether the old variable value (assigned inside 'targetStatement' or earlier)
/// is read anywhere in the remaining scope of the variable declaration.
/// </summary>
bool IsVariableValueUnused(VariableDeclarationStatement varDecl, Statement targetStatement)
{
Debug.Assert(targetStatement.Ancestors.Contains(varDecl.Parent));
BlockStatement block = (BlockStatement)varDecl.Parent;
DefiniteAssignmentAnalysis daa = new DefiniteAssignmentAnalysis(block, context.CancellationToken);
daa.SetAnalyzedRange(targetStatement, block, startInclusive: false);
daa.Analyze(varDecl.Variables.Single().Name);
return daa.UnassignedVariableUses.Count == 0;
}
// I used this in the first implementation of the using-statement transform, but now no longer
// because there were problems when multiple using statements were using the same variable
// - no single using statement could be transformed without making the C# code invalid,
// but transforming both would work.
// We now use 'IsVariableValueUnused' which will perform the transform
// even if it results in two variables with the same name and overlapping scopes.
// (this issue could be fixed later by renaming one of the variables)
// I'm not sure whether the other consumers of 'CanMoveVariableDeclarationIntoStatement' should be changed the same way.
bool CanMoveVariableDeclarationIntoStatement(VariableDeclarationStatement varDecl, Statement targetStatement, out Statement declarationPoint)
{
Debug.Assert(targetStatement.Ancestors.Contains(varDecl.Parent));
@ -251,6 +290,24 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -251,6 +290,24 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
return true;
}
/// <summary>
/// Gets whether there is an assignment to 'variableName' anywhere within the given node.
/// </summary>
bool HasAssignment(AstNode root, string variableName)
{
foreach (AstNode node in root.DescendantsAndSelf) {
IdentifierExpression ident = node as IdentifierExpression;
if (ident != null && ident.Identifier == variableName) {
if (ident.Parent is AssignmentExpression && ident.Role == AssignmentExpression.LeftRole
|| ident.Parent is DirectionExpression)
{
return true;
}
}
}
return false;
}
#endregion
#region foreach (generic)
@ -648,7 +705,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -648,7 +705,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
// 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().IsMatch(m.Get("switchExpr").Single())) {
AssignmentExpression assign = m.Get("switchExpr").Single() as AssignmentExpression;
if (!m.Get("switchVar").Single().IsMatch(assign.Left))
if (!(assign != null && m.Get("switchVar").Single().IsMatch(assign.Left)))
return null;
}
FieldReference cachedDictField = m.Get<AstNode>("cachedDict").Single().Annotation<FieldReference>();

7
ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs

@ -50,7 +50,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -50,7 +50,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
var arguments = invocationExpression.Arguments.ToArray();
// Reduce "String.Concat(a, b)" to "a + b"
if (methodRef != null && methodRef.Name == "Concat" && methodRef.DeclaringType.FullName == "System.String" && arguments.Length >= 2)
if (methodRef.Name == "Concat" && methodRef.DeclaringType.FullName == "System.String" && arguments.Length >= 2)
{
invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression
Expression expr = arguments[0];
@ -97,7 +97,10 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -97,7 +97,10 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return null;
}
if (methodRef.Name == "op_Implicit" && arguments.Length == 1) {
arguments[0].Remove(); // detach argument
invocationExpression.ReplaceWith(arguments[0]);
return null;
}
if (methodRef.Name == "op_True" && arguments.Length == 1 && invocationExpression.Role == AstNode.Roles.Condition) {
invocationExpression.ReplaceWith(arguments[0]);
return null;
}

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

@ -40,6 +40,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -40,6 +40,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
new AddCheckedBlocks(),
new DeclareVariables(context), // should run after most transforms that modify statements
new ConvertConstructorCallIntoInitializer(), // must run after DeclareVariables
new DecimalConstantTransform(),
new IntroduceUsingDeclarations(context),
new IntroduceExtensionMethods(context), // must run after IntroduceUsingDeclarations
new IntroduceQueryExpressions(context), // must run after IntroduceExtensionMethods

250
ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs

@ -17,11 +17,32 @@ @@ -17,11 +17,32 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.Disassembler
{
public enum ILNameSyntax
{
/// <summary>
/// class/valuetype + TypeName (built-in types use keyword syntax)
/// </summary>
Signature,
/// <summary>
/// Like signature, but always refers to type parameters using their position
/// </summary>
SignatureNoNamedTypeParameters,
/// <summary>
/// [assembly]Full.Type.Name (even for built-in types)
/// </summary>
TypeName,
/// <summary>
/// Name (but built-in types use keyword syntax)
/// </summary>
ShortTypeName
}
public static class DisassemblerHelpers
{
public static void WriteOffsetReference(ITextOutput writer, Instruction instruction)
@ -35,6 +56,7 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -35,6 +56,7 @@ namespace ICSharpCode.Decompiler.Disassembler
WriteOffsetReference(writer, exceptionHandler.TryStart);
writer.Write('-');
WriteOffsetReference(writer, exceptionHandler.TryEnd);
writer.Write(' ');
writer.Write(exceptionHandler.HandlerType.ToString());
if (exceptionHandler.FilterStart != null) {
writer.Write(' ');
@ -56,8 +78,14 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -56,8 +78,14 @@ namespace ICSharpCode.Decompiler.Disassembler
writer.WriteDefinition(CecilExtensions.OffsetToString(instruction.Offset), instruction);
writer.Write(": ");
writer.WriteReference(instruction.OpCode.Name, instruction.OpCode);
if(null != instruction.Operand) {
if (instruction.Operand != null) {
writer.Write(' ');
if (instruction.OpCode == OpCodes.Ldtoken) {
if (instruction.Operand is MethodReference)
writer.Write("method ");
else if (instruction.Operand is FieldReference)
writer.Write("field ");
}
WriteOperand(writer, instruction.Operand);
}
}
@ -82,46 +110,131 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -82,46 +110,131 @@ namespace ICSharpCode.Decompiler.Disassembler
public static void WriteTo(this MethodReference method, ITextOutput writer)
{
if (method.HasThis)
if (method.ExplicitThis) {
writer.Write("instance explicit ");
}
else if (method.HasThis) {
writer.Write("instance ");
method.ReturnType.WriteTo(writer);
}
method.ReturnType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters);
writer.Write(' ');
if (method.DeclaringType != null) {
method.DeclaringType.WriteTo(writer, true);
method.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write("::");
}
writer.WriteReference(method.Name, method);
MethodDefinition md = method as MethodDefinition;
if (md != null && md.IsCompilerControlled) {
writer.WriteReference(Escape(method.Name + "$PST" + method.MetadataToken.ToInt32().ToString("X8")), method);
} else {
writer.WriteReference(Escape(method.Name), method);
}
GenericInstanceMethod gim = method as GenericInstanceMethod;
if (gim != null) {
writer.Write('<');
for (int i = 0; i < gim.GenericArguments.Count; i++) {
if (i > 0)
writer.Write(", ");
gim.GenericArguments[i].WriteTo(writer);
}
writer.Write('>');
}
writer.Write("(");
var parameters = method.Parameters;
for(int i = 0; i < parameters.Count; ++i) {
if (i > 0) writer.Write(", ");
parameters[i].ParameterType.WriteTo(writer);
parameters[i].ParameterType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters);
}
writer.Write(")");
}
static void WriteTo(this FieldReference field, ITextOutput writer)
{
field.FieldType.WriteTo(writer);
field.FieldType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters);
writer.Write(' ');
field.DeclaringType.WriteTo(writer);
field.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write("::");
writer.WriteReference(field.Name, field);
writer.WriteReference(Escape(field.Name), field);
}
static bool IsValidIdentifierCharacter(char c)
{
return c == '_' || c == '$' || c == '@' || c == '?' || c == '`';
}
static bool IsValidIdentifier(string identifier)
{
if (string.IsNullOrEmpty(identifier))
return false;
if (!(char.IsLetter(identifier[0]) || IsValidIdentifierCharacter(identifier[0]))) {
// As a special case, .ctor and .cctor are valid despite starting with a dot
return identifier == ".ctor" || identifier == ".cctor";
}
for (int i = 1; i < identifier.Length; i++) {
if (!(char.IsLetterOrDigit(identifier[i]) || IsValidIdentifierCharacter(identifier[i]) || identifier[i] == '.'))
return false;
}
return true;
}
static readonly HashSet<string> ilKeywords = BuildKeywordList(
"abstract", "algorithm", "alignment", "ansi", "any", "arglist",
"array", "as", "assembly", "assert", "at", "auto", "autochar", "beforefieldinit",
"blob", "blob_object", "bool", "brnull", "brnull.s", "brzero", "brzero.s", "bstr",
"bytearray", "byvalstr", "callmostderived", "carray", "catch", "cdecl", "cf",
"char", "cil", "class", "clsid", "const", "currency", "custom", "date", "decimal",
"default", "demand", "deny", "endmac", "enum", "error", "explicit", "extends", "extern",
"false", "famandassem", "family", "famorassem", "fastcall", "fault", "field", "filetime",
"filter", "final", "finally", "fixed", "float", "float32", "float64", "forwardref",
"fromunmanaged", "handler", "hidebysig", "hresult", "idispatch", "il", "illegal",
"implements", "implicitcom", "implicitres", "import", "in", "inheritcheck", "init",
"initonly", "instance", "int", "int16", "int32", "int64", "int8", "interface", "internalcall",
"iunknown", "lasterr", "lcid", "linkcheck", "literal", "localloc", "lpstr", "lpstruct", "lptstr",
"lpvoid", "lpwstr", "managed", "marshal", "method", "modopt", "modreq", "native", "nested",
"newslot", "noappdomain", "noinlining", "nomachine", "nomangle", "nometadata", "noncasdemand",
"noncasinheritance", "noncaslinkdemand", "noprocess", "not", "not_in_gc_heap", "notremotable",
"notserialized", "null", "nullref", "object", "objectref", "opt", "optil", "out",
"permitonly", "pinned", "pinvokeimpl", "prefix1", "prefix2", "prefix3", "prefix4", "prefix5", "prefix6",
"prefix7", "prefixref", "prejitdeny", "prejitgrant", "preservesig", "private", "privatescope", "protected",
"public", "record", "refany", "reqmin", "reqopt", "reqrefuse", "reqsecobj", "request", "retval",
"rtspecialname", "runtime", "safearray", "sealed", "sequential", "serializable", "special", "specialname",
"static", "stdcall", "storage", "stored_object", "stream", "streamed_object", "string", "struct",
"synchronized", "syschar", "sysstring", "tbstr", "thiscall", "tls", "to", "true", "typedref",
"unicode", "unmanaged", "unmanagedexp", "unsigned", "unused", "userdefined", "value", "valuetype",
"vararg", "variant", "vector", "virtual", "void", "wchar", "winapi", "with", "wrapper",
// These are not listed as keywords in spec, but ILAsm treats them as such
"property", "type", "flags", "callconv", "strict"
);
static HashSet<string> BuildKeywordList(params string[] keywords)
{
HashSet<string> s = new HashSet<string>(keywords);
foreach (var field in typeof(OpCodes).GetFields()) {
s.Add(((OpCode)field.GetValue(null)).Name);
}
return s;
}
public static string Escape(string identifier)
{
return identifier;
if (IsValidIdentifier(identifier) && !ilKeywords.Contains(identifier)) {
return identifier;
} else {
// The ECMA specification says that ' inside SQString should be ecaped using an octal escape sequence,
// but we follow Microsoft's ILDasm and use \'.
return "'" + NRefactory.CSharp.OutputVisitor.ConvertString(identifier).Replace("'", "\\'") + "'";
}
}
public static void WriteTo(this TypeReference type, ITextOutput writer, bool onlyName = false, bool shortName = false)
public static void WriteTo(this TypeReference type, ITextOutput writer, ILNameSyntax syntax = ILNameSyntax.Signature)
{
ILNameSyntax syntaxForElementTypes = syntax == ILNameSyntax.SignatureNoNamedTypeParameters ? syntax : ILNameSyntax.Signature;
if (type is PinnedType) {
writer.Write("pinned ");
((PinnedType)type).ElementType.WriteTo(writer, onlyName, shortName);
((PinnedType)type).ElementType.WriteTo(writer, syntaxForElementTypes);
writer.Write(" pinned");
} else if (type is ArrayType) {
ArrayType at = (ArrayType)type;
at.ElementType.WriteTo(writer, onlyName, shortName);
at.ElementType.WriteTo(writer, syntaxForElementTypes);
writer.Write('[');
writer.Write(string.Join(", ", at.Dimensions));
writer.Write(']');
@ -129,49 +242,57 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -129,49 +242,57 @@ namespace ICSharpCode.Decompiler.Disassembler
writer.Write('!');
if (((GenericParameter)type).Owner.GenericParameterType == GenericParameterType.Method)
writer.Write('!');
writer.Write(type.Name);
if (string.IsNullOrEmpty(type.Name) || type.Name[0] == '!' || syntax == ILNameSyntax.SignatureNoNamedTypeParameters)
writer.Write(((GenericParameter)type).Position.ToString());
else
writer.Write(Escape(type.Name));
} else if (type is ByReferenceType) {
((ByReferenceType)type).ElementType.WriteTo(writer, onlyName, shortName);
((ByReferenceType)type).ElementType.WriteTo(writer, syntaxForElementTypes);
writer.Write('&');
} else if (type is PointerType) {
((PointerType)type).ElementType.WriteTo(writer, onlyName, shortName);
((PointerType)type).ElementType.WriteTo(writer, syntaxForElementTypes);
writer.Write('*');
} else if (type is GenericInstanceType) {
type.GetElementType().WriteTo(writer, onlyName, shortName);
type.GetElementType().WriteTo(writer, syntaxForElementTypes);
writer.Write('<');
var arguments = ((GenericInstanceType)type).GenericArguments;
for (int i = 0; i < arguments.Count; i++) {
if (i > 0)
writer.Write(", ");
arguments[i].WriteTo(writer, onlyName, shortName);
arguments[i].WriteTo(writer, syntaxForElementTypes);
}
writer.Write('>');
} else if (type is OptionalModifierType) {
writer.Write("modopt(");
((OptionalModifierType)type).ModifierType.WriteTo(writer, true, shortName);
((OptionalModifierType)type).ElementType.WriteTo(writer, syntax);
writer.Write(" modopt(");
((OptionalModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write(") ");
((OptionalModifierType)type).ElementType.WriteTo(writer, onlyName, shortName);
} else if (type is RequiredModifierType) {
writer.Write("modreq(");
((RequiredModifierType)type).ModifierType.WriteTo(writer, true, shortName);
((RequiredModifierType)type).ElementType.WriteTo(writer, syntax);
writer.Write(" modreq(");
((RequiredModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write(") ");
((RequiredModifierType)type).ElementType.WriteTo(writer, onlyName, shortName);
} else {
string name = PrimitiveTypeName(type);
if (name != null) {
string name = PrimitiveTypeName(type.FullName);
if (syntax == ILNameSyntax.ShortTypeName) {
if (name != null)
writer.Write(name);
else
writer.WriteReference(Escape(type.Name), type);
} else if ((syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters) && name != null) {
writer.Write(name);
} else {
if (!onlyName)
if (syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters)
writer.Write(type.IsValueType ? "valuetype " : "class ");
if (type.DeclaringType != null) {
type.DeclaringType.WriteTo(writer, true, shortName);
type.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write('/');
writer.WriteReference(Escape(type.Name), type);
} else {
if (!type.IsDefinition && type.Scope != null && !shortName && !(type is TypeSpecification))
if (!type.IsDefinition && type.Scope != null && !(type is TypeSpecification))
writer.Write("[{0}]", Escape(type.Scope.Name));
writer.WriteReference(shortName ? type.Name : type.FullName, type);
writer.WriteReference(Escape(type.FullName), type);
}
}
}
@ -196,7 +317,19 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -196,7 +317,19 @@ namespace ICSharpCode.Decompiler.Disassembler
VariableReference variableRef = operand as VariableReference;
if (variableRef != null) {
writer.WriteReference(variableRef.Index.ToString(), variableRef);
if (string.IsNullOrEmpty(variableRef.Name))
writer.WriteReference(variableRef.Index.ToString(), variableRef);
else
writer.WriteReference(Escape(variableRef.Name), variableRef);
return;
}
ParameterReference paramRef = operand as ParameterReference;
if (paramRef != null) {
if (string.IsNullOrEmpty(paramRef.Name))
writer.WriteReference(paramRef.Index.ToString(), paramRef);
else
writer.WriteReference(Escape(paramRef.Name), paramRef);
return;
}
@ -208,7 +341,7 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -208,7 +341,7 @@ namespace ICSharpCode.Decompiler.Disassembler
TypeReference typeRef = operand as TypeReference;
if (typeRef != null) {
typeRef.WriteTo(writer);
typeRef.WriteTo(writer, ILNameSyntax.TypeName);
return;
}
@ -220,17 +353,52 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -220,17 +353,52 @@ namespace ICSharpCode.Decompiler.Disassembler
string s = operand as string;
if (s != null) {
writer.Write("\"" + s.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"");
return;
writer.Write("\"" + NRefactory.CSharp.OutputVisitor.ConvertString(s) + "\"");
} else if (operand is char) {
writer.Write(((int)(char)operand).ToString());
} else if (operand is float) {
float val = (float)operand;
if (val == 0) {
writer.Write("0.0");
} else if (float.IsInfinity(val) || float.IsNaN(val)) {
byte[] data = BitConverter.GetBytes(val);
writer.Write('(');
for (int i = 0; i < data.Length; i++) {
if (i > 0)
writer.Write(' ');
writer.Write(data[i].ToString("X2"));
}
writer.Write(')');
} else {
writer.Write(val.ToString("R", System.Globalization.CultureInfo.InvariantCulture));
}
} else if (operand is double) {
double val = (double)operand;
if (val == 0) {
writer.Write("0.0");
} else if (double.IsInfinity(val) || double.IsNaN(val)) {
byte[] data = BitConverter.GetBytes(val);
writer.Write('(');
for (int i = 0; i < data.Length; i++) {
if (i > 0)
writer.Write(' ');
writer.Write(data[i].ToString("X2"));
}
writer.Write(')');
} else {
writer.Write(val.ToString("R", System.Globalization.CultureInfo.InvariantCulture));
}
} else if (operand is bool) {
writer.Write((bool)operand ? "true" : "false");
} else {
s = ToInvariantCultureString(operand);
writer.Write(s);
}
s = ToInvariantCultureString(operand);
writer.Write(s);
}
public static string PrimitiveTypeName(this TypeReference type)
public static string PrimitiveTypeName(string fullName)
{
switch (type.FullName) {
switch (fullName) {
case "System.SByte":
return "int8";
case "System.Int16":
@ -261,6 +429,8 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -261,6 +429,8 @@ namespace ICSharpCode.Decompiler.Disassembler
return "char";
case "System.Object":
return "object";
case "System.IntPtr":
return "native int";
default:
return null;
}

6
ICSharpCode.Decompiler/Disassembler/ILStructure.cs

@ -88,8 +88,10 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -88,8 +88,10 @@ namespace ICSharpCode.Decompiler.Disassembler
: this(ILStructureType.Root, 0, body.CodeSize)
{
// Build the tree of exception structures:
foreach (ExceptionHandler eh in body.ExceptionHandlers) {
AddNestedStructure(new ILStructure(ILStructureType.Try, eh.TryStart.Offset, eh.TryEnd.Offset, eh));
for (int i = 0; i < body.ExceptionHandlers.Count; i++) {
ExceptionHandler eh = body.ExceptionHandlers[i];
if (!body.ExceptionHandlers.Take(i).Any(oldEh => oldEh.TryStart == eh.TryStart && oldEh.TryEnd == eh.TryEnd))
AddNestedStructure(new ILStructure(ILStructureType.Try, eh.TryStart.Offset, eh.TryEnd.Offset, eh));
if (eh.HandlerType == ExceptionHandlerType.Filter)
AddNestedStructure(new ILStructure(ILStructureType.Filter, eh.FilterStart.Offset, eh.HandlerStart.Offset, eh));
AddNestedStructure(new ILStructure(ILStructureType.Handler, eh.HandlerStart.Offset, eh.HandlerEnd == null ? body.CodeSize : eh.HandlerEnd.Offset, eh));

84
ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs

@ -52,9 +52,6 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -52,9 +52,6 @@ namespace ICSharpCode.Decompiler.Disassembler
// start writing IL code
MethodDefinition method = body.Method;
output.WriteLine("// Method begins at RVA 0x{0:x4}", method.RVA);
if (method.HasOverrides)
foreach (var methodOverride in method.Overrides)
output.WriteLine(".override {0}::{1}", methodOverride.DeclaringType.FullName, methodOverride.Name);
output.WriteLine("// Code size {0} (0x{0:x})", body.CodeSize);
output.WriteLine(".maxstack {0}", body.MaxStackSize);
if (method.DeclaringType.Module.Assembly.EntryPoint == method)
@ -69,8 +66,12 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -69,8 +66,12 @@ namespace ICSharpCode.Decompiler.Disassembler
foreach (var v in method.Body.Variables) {
output.WriteDefinition("[" + v.Index + "] ", v);
v.VariableType.WriteTo(output);
output.Write(' ');
output.Write(DisassemblerHelpers.Escape(v.Name));
if (!string.IsNullOrEmpty(v.Name)) {
output.Write(' ');
output.Write(DisassemblerHelpers.Escape(v.Name));
}
if (v.Index + 1 < method.Body.Variables.Count)
output.Write(',');
output.WriteLine();
}
output.Unindent();
@ -80,29 +81,49 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -80,29 +81,49 @@ namespace ICSharpCode.Decompiler.Disassembler
if (detectControlStructure && body.Instructions.Count > 0) {
Instruction inst = body.Instructions[0];
WriteStructureBody(new ILStructure(body), ref inst, methodMapping, method.Body.CodeSize);
HashSet<int> branchTargets = GetBranchTargets(body.Instructions);
WriteStructureBody(new ILStructure(body), branchTargets, ref inst, methodMapping, method.Body.CodeSize);
} else {
foreach (var inst in method.Body.Instructions) {
inst.WriteTo(output);
// add IL code mappings - used in debugger
methodMapping.MemberCodeMappings.Add(
new SourceCodeMapping() {
SourceCodeLine = output.CurrentLine,
ILInstructionOffset = new ILRange { From = inst.Offset, To = inst.Next == null ? method.Body.CodeSize : inst.Next.Offset },
MemberMapping = methodMapping
});
if (methodMapping != null) {
// add IL code mappings - used in debugger
methodMapping.MemberCodeMappings.Add(
new SourceCodeMapping() {
SourceCodeLine = output.CurrentLine,
ILInstructionOffset = new ILRange { From = inst.Offset, To = inst.Next == null ? method.Body.CodeSize : inst.Next.Offset },
MemberMapping = methodMapping
});
}
output.WriteLine();
}
output.WriteLine();
foreach (var eh in method.Body.ExceptionHandlers) {
eh.WriteTo(output);
if (method.Body.HasExceptionHandlers) {
output.WriteLine();
foreach (var eh in method.Body.ExceptionHandlers) {
eh.WriteTo(output);
output.WriteLine();
}
}
}
}
HashSet<int> GetBranchTargets(IEnumerable<Instruction> instructions)
{
HashSet<int> branchTargets = new HashSet<int>();
foreach (var inst in instructions) {
Instruction target = inst.Operand as Instruction;
if (target != null)
branchTargets.Add(target.Offset);
Instruction[] targets = inst.Operand as Instruction[];
if (targets != null)
foreach (Instruction t in targets)
branchTargets.Add(t.Offset);
}
return branchTargets;
}
void WriteStructureHeader(ILStructure s)
{
switch (s.Type) {
@ -116,7 +137,8 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -116,7 +137,8 @@ namespace ICSharpCode.Decompiler.Disassembler
output.WriteLine();
break;
case ILStructureType.Try:
output.WriteLine(".try {");
output.WriteLine(".try");
output.WriteLine("{");
break;
case ILStructureType.Handler:
switch (s.ExceptionHandler.HandlerType) {
@ -125,22 +147,24 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -125,22 +147,24 @@ namespace ICSharpCode.Decompiler.Disassembler
output.Write("catch");
if (s.ExceptionHandler.CatchType != null) {
output.Write(' ');
s.ExceptionHandler.CatchType.WriteTo(output);
s.ExceptionHandler.CatchType.WriteTo(output, ILNameSyntax.TypeName);
}
output.WriteLine(" {");
output.WriteLine();
break;
case Mono.Cecil.Cil.ExceptionHandlerType.Finally:
output.WriteLine("finally {");
output.WriteLine("finally");
break;
case Mono.Cecil.Cil.ExceptionHandlerType.Fault:
output.WriteLine("fault {");
output.WriteLine("fault");
break;
default:
throw new NotSupportedException();
}
output.WriteLine("{");
break;
case ILStructureType.Filter:
output.WriteLine("filter {");
output.WriteLine("filter");
output.WriteLine("{");
break;
default:
throw new NotSupportedException();
@ -148,17 +172,22 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -148,17 +172,22 @@ namespace ICSharpCode.Decompiler.Disassembler
output.Indent();
}
void WriteStructureBody(ILStructure s, ref Instruction inst, MemberMapping currentMethodMapping, int codeSize)
void WriteStructureBody(ILStructure s, HashSet<int> branchTargets, ref Instruction inst, MemberMapping currentMethodMapping, int codeSize)
{
bool isFirstInstructionInStructure = true;
bool prevInstructionWasBranch = false;
int childIndex = 0;
while (inst != null && inst.Offset < s.EndOffset) {
int offset = inst.Offset;
if (childIndex < s.Children.Count && s.Children[childIndex].StartOffset <= offset && offset < s.Children[childIndex].EndOffset) {
ILStructure child = s.Children[childIndex++];
WriteStructureHeader(child);
WriteStructureBody(child, ref inst, currentMethodMapping, codeSize);
WriteStructureBody(child, branchTargets, ref inst, currentMethodMapping, codeSize);
WriteStructureFooter(child);
} else {
if (!isFirstInstructionInStructure && (prevInstructionWasBranch || branchTargets.Contains(offset))) {
output.WriteLine(); // put an empty line after branches, and in front of branch targets
}
inst.WriteTo(output);
// add IL code mappings - used in debugger
@ -172,8 +201,15 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -172,8 +201,15 @@ namespace ICSharpCode.Decompiler.Disassembler
}
output.WriteLine();
prevInstructionWasBranch = inst.OpCode.FlowControl == FlowControl.Branch
|| inst.OpCode.FlowControl == FlowControl.Cond_Branch
|| inst.OpCode.FlowControl == FlowControl.Return
|| inst.OpCode.FlowControl == FlowControl.Throw;
inst = inst.Next;
}
isFirstInstructionInStructure = false;
}
}

691
ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs

@ -18,8 +18,10 @@ @@ -18,8 +18,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using Mono.Cecil;
using Mono.Collections.Generic;
@ -32,7 +34,6 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -32,7 +34,6 @@ namespace ICSharpCode.Decompiler.Disassembler
{
ITextOutput output;
CancellationToken cancellationToken;
bool detectControlStructure;
bool isInType; // whether we are currently disassembling a whole type (-> defaultCollapsed for foldings)
MethodBodyDisassembler methodBodyDisassembler;
@ -42,23 +43,24 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -42,23 +43,24 @@ namespace ICSharpCode.Decompiler.Disassembler
throw new ArgumentNullException("output");
this.output = output;
this.cancellationToken = cancellationToken;
this.detectControlStructure = detectControlStructure;
this.methodBodyDisassembler = new MethodBodyDisassembler(output, detectControlStructure, cancellationToken);
}
#region Disassemble Method
EnumNameCollection<MethodAttributes> methodAttributeFlags = new EnumNameCollection<MethodAttributes>() {
{ MethodAttributes.Static, "static" },
{ MethodAttributes.Final, "final" },
{ MethodAttributes.Virtual, "virtual" },
{ MethodAttributes.HideBySig, "hidebysig" },
{ MethodAttributes.Abstract, "abstract" },
{ MethodAttributes.SpecialName, "specialname" },
{ MethodAttributes.PInvokeImpl, "pinvokeimpl" },
{ MethodAttributes.PInvokeImpl, null }, // handled separately
{ MethodAttributes.UnmanagedExport, "export" },
{ MethodAttributes.RTSpecialName, "rtspecialname" },
{ MethodAttributes.RequireSecObject, "requiresecobj" },
{ MethodAttributes.NewSlot, "newslot" }
{ MethodAttributes.RequireSecObject, "reqsecobj" },
{ MethodAttributes.NewSlot, "newslot" },
{ MethodAttributes.CheckAccessOnOverride, "strict" },
{ MethodAttributes.Abstract, "abstract" },
{ MethodAttributes.Virtual, "virtual" },
{ MethodAttributes.Static, "static" },
{ MethodAttributes.HasSecurity, null }, // ?? also invisible in ILDasm
};
EnumNameCollection<MethodAttributes> methodVisibility = new EnumNameCollection<MethodAttributes>() {
@ -76,7 +78,7 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -76,7 +78,7 @@ namespace ICSharpCode.Decompiler.Disassembler
{ MethodCallingConvention.ThisCall, "unmanaged thiscall" },
{ MethodCallingConvention.FastCall, "unmanaged fastcall" },
{ MethodCallingConvention.VarArg, "vararg" },
{ MethodCallingConvention.Generic, "generic" },
{ MethodCallingConvention.Generic, null },
};
EnumNameCollection<MethodImplAttributes> methodCodeType = new EnumNameCollection<MethodImplAttributes>() {
@ -90,6 +92,9 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -90,6 +92,9 @@ namespace ICSharpCode.Decompiler.Disassembler
{ MethodImplAttributes.Synchronized, "synchronized" },
{ MethodImplAttributes.NoInlining, "noinlining" },
{ MethodImplAttributes.NoOptimization, "nooptimization" },
{ MethodImplAttributes.PreserveSig, "preservesig" },
{ MethodImplAttributes.InternalCall, "internalcall" },
{ MethodImplAttributes.ForwardRef, "forwardref" },
};
public void DisassembleMethod(MethodDefinition method)
@ -108,12 +113,53 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -108,12 +113,53 @@ namespace ICSharpCode.Decompiler.Disassembler
//emit flags
WriteEnum(method.Attributes & MethodAttributes.MemberAccessMask, methodVisibility);
WriteFlags(method.Attributes & ~MethodAttributes.MemberAccessMask, methodAttributeFlags);
if(method.IsCompilerControlled) output.Write("privatescope ");
if ((method.Attributes & MethodAttributes.PInvokeImpl) == MethodAttributes.PInvokeImpl) {
output.Write("pinvokeimpl");
if (method.HasPInvokeInfo) {
PInvokeInfo info = method.PInvokeInfo;
output.Write("(\"" + NRefactory.CSharp.OutputVisitor.ConvertString(info.Module.Name) + "\"");
if (!string.IsNullOrEmpty(info.EntryPoint) && info.EntryPoint != method.Name)
output.Write(" as \"" + NRefactory.CSharp.OutputVisitor.ConvertString(info.EntryPoint) + "\"");
if (info.IsNoMangle)
output.Write(" nomangle");
if (info.IsCharSetAnsi)
output.Write(" ansi");
else if (info.IsCharSetAuto)
output.Write(" autochar");
else if (info.IsCharSetUnicode)
output.Write(" unicode");
if (info.SupportsLastError)
output.Write(" lasterr");
if (info.IsCallConvCdecl)
output.Write(" cdecl");
else if (info.IsCallConvFastcall)
output.Write(" fastcall");
else if (info.IsCallConvStdCall)
output.Write(" stdcall");
else if (info.IsCallConvThiscall)
output.Write(" thiscall");
else if (info.IsCallConvWinapi)
output.Write(" winapi");
output.Write(')');
}
output.Write(' ');
}
output.WriteLine();
output.Indent();
if (method.HasThis)
if (method.ExplicitThis) {
output.Write("instance explicit ");
} else if (method.HasThis) {
output.Write("instance ");
}
//call convention
WriteEnum(method.CallingConvention & (MethodCallingConvention)0x1f, callingConvention);
@ -122,7 +168,16 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -122,7 +168,16 @@ namespace ICSharpCode.Decompiler.Disassembler
//return type
method.ReturnType.WriteTo(output);
output.Write(' ');
output.Write(DisassemblerHelpers.Escape(method.Name));
if (method.MethodReturnType.HasMarshalInfo) {
WriteMarshalInfo(method.MethodReturnType.MarshalInfo);
}
if (method.IsCompilerControlled) {
output.Write(DisassemblerHelpers.Escape(method.Name + "$PST" + method.MetadataToken.ToInt32().ToString("X8")));
} else {
output.Write(DisassemblerHelpers.Escape(method.Name));
}
WriteTypeParameters(output, method);
//( params )
@ -143,34 +198,454 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -143,34 +198,454 @@ namespace ICSharpCode.Decompiler.Disassembler
WriteFlags(method.ImplAttributes & ~(MethodImplAttributes.CodeTypeMask | MethodImplAttributes.ManagedMask), methodImpl);
output.Unindent();
if (method.HasBody || method.HasCustomAttributes) {
OpenBlock(defaultCollapsed: isInType);
WriteAttributes(method.CustomAttributes);
if (method.HasBody) {
// create IL code mappings - used in debugger
MemberMapping methodMapping = method.CreateCodeMapping(this.CodeMappings);
methodBodyDisassembler.Disassemble(method.Body, methodMapping);
OpenBlock(defaultCollapsed: isInType);
WriteAttributes(method.CustomAttributes);
if (method.HasOverrides) {
foreach (var methodOverride in method.Overrides) {
output.Write(".override method ");
methodOverride.WriteTo(output);
output.WriteLine();
}
}
foreach (var p in method.Parameters) {
WriteParameterAttributes(p);
}
WriteSecurityDeclarations(method);
if (method.HasBody) {
// create IL code mappings - used in debugger
MemberMapping methodMapping = method.CreateCodeMapping(this.CodeMappings);
methodBodyDisassembler.Disassemble(method.Body, methodMapping);
}
CloseBlock("end of method " + DisassemblerHelpers.Escape(method.DeclaringType.Name) + "::" + DisassemblerHelpers.Escape(method.Name));
}
#region Write Security Declarations
void WriteSecurityDeclarations(ISecurityDeclarationProvider secDeclProvider)
{
if (!secDeclProvider.HasSecurityDeclarations)
return;
foreach (var secdecl in secDeclProvider.SecurityDeclarations) {
output.Write(".permissionset ");
switch (secdecl.Action) {
case SecurityAction.Request:
output.Write("request");
break;
case SecurityAction.Demand:
output.Write("demand");
break;
case SecurityAction.Assert:
output.Write("assert");
break;
case SecurityAction.Deny:
output.Write("deny");
break;
case SecurityAction.PermitOnly:
output.Write("permitonly");
break;
case SecurityAction.LinkDemand:
output.Write("linkcheck");
break;
case SecurityAction.InheritDemand:
output.Write("inheritcheck");
break;
case SecurityAction.RequestMinimum:
output.Write("reqmin");
break;
case SecurityAction.RequestOptional:
output.Write("reqopt");
break;
case SecurityAction.RequestRefuse:
output.Write("reqrefuse");
break;
case SecurityAction.PreJitGrant:
output.Write("prejitgrant");
break;
case SecurityAction.PreJitDeny:
output.Write("prejitdeny");
break;
case SecurityAction.NonCasDemand:
output.Write("noncasdemand");
break;
case SecurityAction.NonCasLinkDemand:
output.Write("noncaslinkdemand");
break;
case SecurityAction.NonCasInheritance:
output.Write("noncasinheritance");
break;
default:
output.Write(secdecl.Action.ToString());
break;
}
output.WriteLine(" = {");
output.Indent();
for (int i = 0; i < secdecl.SecurityAttributes.Count; i++) {
SecurityAttribute sa = secdecl.SecurityAttributes[i];
if (sa.AttributeType.Scope == sa.AttributeType.Module) {
output.Write("class ");
output.Write(DisassemblerHelpers.Escape(GetAssemblyQualifiedName(sa.AttributeType)));
} else {
sa.AttributeType.WriteTo(output, ILNameSyntax.TypeName);
}
output.Write(" = {");
if (sa.HasFields || sa.HasProperties) {
output.WriteLine();
output.Indent();
foreach (CustomAttributeNamedArgument na in sa.Fields) {
output.Write("field ");
WriteSecurityDeclarationArgument(na);
output.WriteLine();
}
foreach (CustomAttributeNamedArgument na in sa.Properties) {
output.Write("property ");
WriteSecurityDeclarationArgument(na);
output.WriteLine();
}
output.Unindent();
}
output.Write('}');
if (i + 1< secdecl.SecurityAttributes.Count)
output.Write(',');
output.WriteLine();
}
output.Unindent();
output.WriteLine("}");
}
}
void WriteSecurityDeclarationArgument(CustomAttributeNamedArgument na)
{
TypeReference type = na.Argument.Type;
if (type.MetadataType == MetadataType.Class || type.MetadataType == MetadataType.ValueType) {
output.Write("enum ");
if (type.Scope != type.Module) {
output.Write("class ");
output.Write(DisassemblerHelpers.Escape(GetAssemblyQualifiedName(type)));
} else {
type.WriteTo(output, ILNameSyntax.TypeName);
}
CloseBlock("End of method " + method.DeclaringType.Name + "." + method.Name);
} else {
output.WriteLine();
type.WriteTo(output);
}
output.Write(' ');
output.Write(DisassemblerHelpers.Escape(na.Name));
output.Write(" = ");
if (na.Argument.Value is string) {
// secdecls use special syntax for strings
output.Write("string('{0}')", NRefactory.CSharp.OutputVisitor.ConvertString((string)na.Argument.Value).Replace("'", "\'"));
} else {
WriteConstant(na.Argument.Value);
}
}
string GetAssemblyQualifiedName(TypeReference type)
{
AssemblyNameReference anr = type.Scope as AssemblyNameReference;
if (anr == null) {
ModuleDefinition md = type.Scope as ModuleDefinition;
if (md != null) {
anr = md.Assembly.Name;
}
}
if (anr != null) {
return type.FullName + ", " + anr.FullName;
} else {
return type.FullName;
}
}
#endregion
#region WriteMarshalInfo
void WriteMarshalInfo(MarshalInfo marshalInfo)
{
output.Write("marshal(");
WriteNativeType(marshalInfo.NativeType, marshalInfo);
output.Write(") ");
}
void WriteNativeType(NativeType nativeType, MarshalInfo marshalInfo = null)
{
switch (nativeType) {
case NativeType.None:
break;
case NativeType.Boolean:
output.Write("bool");
break;
case NativeType.I1:
output.Write("int8");
break;
case NativeType.U1:
output.Write("unsigned int8");
break;
case NativeType.I2:
output.Write("int16");
break;
case NativeType.U2:
output.Write("unsigned int16");
break;
case NativeType.I4:
output.Write("int32");
break;
case NativeType.U4:
output.Write("unsigned int32");
break;
case NativeType.I8:
output.Write("int64");
break;
case NativeType.U8:
output.Write("unsigned int64");
break;
case NativeType.R4:
output.Write("float32");
break;
case NativeType.R8:
output.Write("float64");
break;
case NativeType.LPStr:
output.Write("lpstr");
break;
case NativeType.Int:
output.Write("int");
break;
case NativeType.UInt:
output.Write("unsigned int");
break;
case NativeType.Func:
goto default; // ??
case NativeType.Array:
ArrayMarshalInfo ami = (ArrayMarshalInfo)marshalInfo;
if (ami == null)
goto default;
if (ami.ElementType != NativeType.Max)
WriteNativeType(ami.ElementType);
output.Write('[');
if (ami.SizeParameterMultiplier == 0) {
output.Write(ami.Size.ToString());
} else {
if (ami.Size >= 0)
output.Write(ami.Size.ToString());
output.Write(" + ");
output.Write(ami.SizeParameterIndex.ToString());
}
output.Write(']');
break;
case NativeType.Currency:
output.Write("currency");
break;
case NativeType.BStr:
output.Write("bstr");
break;
case NativeType.LPWStr:
output.Write("lpwstr");
break;
case NativeType.LPTStr:
output.Write("lptstr");
break;
case NativeType.FixedSysString:
output.Write("fixed sysstring[{0}]", ((FixedSysStringMarshalInfo)marshalInfo).Size);
break;
case NativeType.IUnknown:
output.Write("iunknown");
break;
case NativeType.IDispatch:
output.Write("idispatch");
break;
case NativeType.Struct:
output.Write("struct");
break;
case NativeType.IntF:
output.Write("interface");
break;
case NativeType.SafeArray:
output.Write("safearray ");
SafeArrayMarshalInfo sami = marshalInfo as SafeArrayMarshalInfo;
if (sami != null) {
switch (sami.ElementType) {
case VariantType.None:
break;
case VariantType.I2:
output.Write("int16");
break;
case VariantType.I4:
output.Write("int32");
break;
case VariantType.R4:
output.Write("float32");
break;
case VariantType.R8:
output.Write("float64");
break;
case VariantType.CY:
output.Write("currency");
break;
case VariantType.Date:
output.Write("date");
break;
case VariantType.BStr:
output.Write("bstr");
break;
case VariantType.Dispatch:
output.Write("idispatch");
break;
case VariantType.Error:
output.Write("error");
break;
case VariantType.Bool:
output.Write("bool");
break;
case VariantType.Variant:
output.Write("variant");
break;
case VariantType.Unknown:
output.Write("iunknown");
break;
case VariantType.Decimal:
output.Write("decimal");
break;
case VariantType.I1:
output.Write("int8");
break;
case VariantType.UI1:
output.Write("unsigned int8");
break;
case VariantType.UI2:
output.Write("unsigned int16");
break;
case VariantType.UI4:
output.Write("unsigned int32");
break;
case VariantType.Int:
output.Write("int");
break;
case VariantType.UInt:
output.Write("unsigned int");
break;
default:
output.Write(sami.ElementType.ToString());
break;
}
}
break;
case NativeType.FixedArray:
output.Write("fixed array");
FixedArrayMarshalInfo fami = marshalInfo as FixedArrayMarshalInfo;
if (fami != null) {
output.Write("[{0}]", fami.Size);
if (fami.ElementType != NativeType.None) {
output.Write(' ');
WriteNativeType(fami.ElementType);
}
}
break;
case NativeType.ByValStr:
output.Write("byvalstr");
break;
case NativeType.ANSIBStr:
output.Write("ansi bstr");
break;
case NativeType.TBStr:
output.Write("tbstr");
break;
case NativeType.VariantBool:
output.Write("variant bool");
break;
case NativeType.ASAny:
output.Write("as any");
break;
case NativeType.LPStruct:
output.Write("lpstruct");
break;
case NativeType.CustomMarshaler:
CustomMarshalInfo cmi = marshalInfo as CustomMarshalInfo;
if (cmi == null)
goto default;
output.Write("custom(\"{0}\", \"{1}\"",
NRefactory.CSharp.OutputVisitor.ConvertString(cmi.ManagedType.FullName),
NRefactory.CSharp.OutputVisitor.ConvertString(cmi.Cookie));
if (cmi.Guid != Guid.Empty || !string.IsNullOrEmpty(cmi.UnmanagedType)) {
output.Write(", \"{0}\", \"{1}\"", cmi.Guid.ToString(), NRefactory.CSharp.OutputVisitor.ConvertString(cmi.UnmanagedType));
}
output.Write(')');
break;
case NativeType.Error:
output.Write("error");
break;
default:
output.Write(nativeType.ToString());
break;
}
}
#endregion
void WriteParameters(Collection<ParameterDefinition> parameters)
{
for (int i = 0; i < parameters.Count; i++) {
var p = parameters[i];
if (p.IsIn)
output.Write("[in] ");
if (p.IsOut)
output.Write("[out] ");
if (p.IsOptional)
output.Write("[opt] ");
p.ParameterType.WriteTo(output);
output.Write(' ');
if (p.HasMarshalInfo) {
WriteMarshalInfo(p.MarshalInfo);
}
output.WriteDefinition(DisassemblerHelpers.Escape(p.Name), p);
if (i < parameters.Count - 1)
output.Write(',');
output.WriteLine();
}
}
bool HasParameterAttributes(ParameterDefinition p)
{
return p.HasConstant || p.HasCustomAttributes;
}
void WriteParameterAttributes(ParameterDefinition p)
{
if (!HasParameterAttributes(p))
return;
output.Write(".param [{0}]", p.Index + 1);
if (p.HasConstant) {
output.Write(" = ");
WriteConstant(p.Constant);
}
output.WriteLine();
WriteAttributes(p.CustomAttributes);
}
void WriteConstant(object constant)
{
if (constant == null) {
output.Write("nullref");
} else {
string typeName = DisassemblerHelpers.PrimitiveTypeName(constant.GetType().FullName);
if (typeName != null && typeName != "string") {
output.Write(typeName);
output.Write('(');
float? cf = constant as float?;
double? cd = constant as double?;
if (cf.HasValue && (float.IsNaN(cf.Value) || float.IsInfinity(cf.Value))) {
output.Write("0x{0:x8}", BitConverter.ToInt32(BitConverter.GetBytes(cf.Value), 0));
} else if (cd.HasValue && (double.IsNaN(cd.Value) || double.IsInfinity(cd.Value))) {
output.Write("0x{0:x16}", BitConverter.DoubleToInt64Bits(cd.Value));
} else {
DisassemblerHelpers.WriteOperand(output, constant);
}
output.Write(')');
} else {
DisassemblerHelpers.WriteOperand(output, constant);
}
}
}
#endregion
#region Disassemble Field
@ -196,20 +671,26 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -196,20 +671,26 @@ namespace ICSharpCode.Decompiler.Disassembler
{
output.WriteDefinition(".field ", field);
WriteEnum(field.Attributes & FieldAttributes.FieldAccessMask, fieldVisibility);
WriteFlags(field.Attributes & ~(FieldAttributes.FieldAccessMask | FieldAttributes.HasDefault), fieldAttributes);
const FieldAttributes hasXAttributes = FieldAttributes.HasDefault | FieldAttributes.HasFieldMarshal | FieldAttributes.HasFieldRVA;
WriteFlags(field.Attributes & ~(FieldAttributes.FieldAccessMask | hasXAttributes), fieldAttributes);
if (field.HasMarshalInfo) {
WriteMarshalInfo(field.MarshalInfo);
}
field.FieldType.WriteTo(output);
output.Write(' ');
output.Write(DisassemblerHelpers.Escape(field.Name));
if ((field.Attributes & FieldAttributes.HasFieldRVA) == FieldAttributes.HasFieldRVA) {
output.Write(" at I_{0:x8}", field.RVA);
}
if (field.HasConstant) {
output.Write(" = ");
DisassemblerHelpers.WriteOperand(output, field.Constant);
WriteConstant(field.Constant);
}
output.WriteLine();
if (field.HasCustomAttributes) {
OpenBlock(false);
output.MarkFoldStart();
WriteAttributes(field.CustomAttributes);
CloseBlock();
} else {
output.WriteLine();
output.MarkFoldEnd();
}
}
#endregion
@ -225,15 +706,27 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -225,15 +706,27 @@ namespace ICSharpCode.Decompiler.Disassembler
{
output.WriteDefinition(".property ", property);
WriteFlags(property.Attributes, propertyAttributes);
if (property.HasThis)
output.Write("instance ");
property.PropertyType.WriteTo(output);
output.Write(' ');
output.Write(DisassemblerHelpers.Escape(property.Name));
output.Write("(");
if (property.HasParameters) {
output.WriteLine();
output.Indent();
WriteParameters(property.Parameters);
output.Unindent();
}
output.Write(")");
OpenBlock(false);
WriteAttributes(property.CustomAttributes);
WriteNestedMethod(".get", property.GetMethod);
WriteNestedMethod(".set", property.SetMethod);
foreach (var method in property.OtherMethods) {
WriteNestedMethod(".method", method);
WriteNestedMethod(".other", method);
}
CloseBlock();
}
@ -242,16 +735,10 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -242,16 +735,10 @@ namespace ICSharpCode.Decompiler.Disassembler
{
if (method == null)
return;
if (detectControlStructure) {
output.WriteDefinition(keyword, method);
output.Write(' ');
DisassembleMethodInternal(method);
} else {
output.Write(keyword);
output.Write(' ');
method.WriteTo(output);
output.WriteLine();
}
output.Write(keyword);
output.Write(' ');
method.WriteTo(output);
output.WriteLine();
}
#endregion
@ -265,16 +752,16 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -265,16 +752,16 @@ namespace ICSharpCode.Decompiler.Disassembler
{
output.WriteDefinition(".event ", ev);
WriteFlags(ev.Attributes, eventAttributes);
ev.EventType.WriteTo(output);
ev.EventType.WriteTo(output, ILNameSyntax.TypeName);
output.Write(' ');
output.Write(DisassemblerHelpers.Escape(ev.Name));
OpenBlock(false);
WriteAttributes(ev.CustomAttributes);
WriteNestedMethod(".add", ev.AddMethod);
WriteNestedMethod(".remove", ev.RemoveMethod);
WriteNestedMethod(".invoke", ev.InvokeMethod);
WriteNestedMethod(".addon", ev.AddMethod);
WriteNestedMethod(".removeon", ev.RemoveMethod);
WriteNestedMethod(".fire", ev.InvokeMethod);
foreach (var method in ev.OtherMethods) {
WriteNestedMethod(".method", method);
WriteNestedMethod(".other", method);
}
CloseBlock();
}
@ -331,7 +818,7 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -331,7 +818,7 @@ namespace ICSharpCode.Decompiler.Disassembler
const TypeAttributes masks = TypeAttributes.ClassSemanticMask | TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.StringFormatMask;
WriteFlags(type.Attributes & ~masks, typeAttributes);
output.Write(DisassemblerHelpers.Escape(type.Name));
output.Write(DisassemblerHelpers.Escape(type.DeclaringType != null ? type.Name : type.FullName));
WriteTypeParameters(output, type);
output.MarkFoldStart(defaultCollapsed: isInType);
output.WriteLine();
@ -339,7 +826,7 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -339,7 +826,7 @@ namespace ICSharpCode.Decompiler.Disassembler
if (type.BaseType != null) {
output.Indent();
output.Write("extends ");
type.BaseType.WriteTo(output, true);
type.BaseType.WriteTo(output, ILNameSyntax.TypeName);
output.WriteLine();
output.Unindent();
}
@ -352,9 +839,7 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -352,9 +839,7 @@ namespace ICSharpCode.Decompiler.Disassembler
output.Write("implements ");
else
output.Write(" ");
if (type.Interfaces[index].Namespace != null)
output.Write("{0}.", type.Interfaces[index].Namespace);
output.Write(type.Interfaces[index].Name);
type.Interfaces[index].WriteTo(output, ILNameSyntax.TypeName);
}
output.WriteLine();
output.Unindent();
@ -365,6 +850,7 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -365,6 +850,7 @@ namespace ICSharpCode.Decompiler.Disassembler
bool oldIsInType = isInType;
isInType = true;
WriteAttributes(type.CustomAttributes);
WriteSecurityDeclarations(type);
if (type.HasLayoutInfo) {
output.WriteLine(".pack {0}", type.PackingSize);
output.WriteLine(".size {0}", type.ClassSize);
@ -387,13 +873,13 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -387,13 +873,13 @@ namespace ICSharpCode.Decompiler.Disassembler
}
output.WriteLine();
}
if (type.HasProperties) {
output.WriteLine("// Properties");
foreach (var prop in type.Properties) {
if (type.HasMethods) {
output.WriteLine("// Methods");
foreach (var m in type.Methods) {
cancellationToken.ThrowIfCancellationRequested();
DisassembleProperty(prop);
DisassembleMethod(m);
output.WriteLine();
}
output.WriteLine();
}
if (type.HasEvents) {
output.WriteLine("// Events");
@ -404,18 +890,15 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -404,18 +890,15 @@ namespace ICSharpCode.Decompiler.Disassembler
}
output.WriteLine();
}
if (type.HasMethods) {
output.WriteLine("// Methods");
var accessorMethods = type.GetAccessorMethods();
foreach (var m in type.Methods) {
if (type.HasProperties) {
output.WriteLine("// Properties");
foreach (var prop in type.Properties) {
cancellationToken.ThrowIfCancellationRequested();
if (!(detectControlStructure && accessorMethods.Contains(m))) {
DisassembleMethod(m);
output.WriteLine();
}
DisassembleProperty(prop);
}
output.WriteLine();
}
CloseBlock("End of class " + type.FullName);
CloseBlock("end of class " + (type.DeclaringType != null ? type.Name : type.FullName));
isInType = oldIsInType;
}
@ -432,18 +915,18 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -432,18 +915,18 @@ namespace ICSharpCode.Decompiler.Disassembler
} else if (gp.HasNotNullableValueTypeConstraint) {
output.Write("valuetype ");
}
if (gp.HasDefaultConstructorConstraint) {
output.Write(".ctor ");
}
if (gp.HasConstraints) {
output.Write('(');
for (int j = 0; j < gp.Constraints.Count; j++) {
if (j > 0)
output.Write(", ");
gp.Constraints[j].WriteTo(output, true);
gp.Constraints[j].WriteTo(output, ILNameSyntax.TypeName);
}
output.Write(") ");
}
if (gp.HasDefaultConstructorConstraint) {
output.Write(".ctor ");
}
if (gp.IsContravariant) {
output.Write('-');
} else if (gp.IsCovariant) {
@ -586,9 +1069,12 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -586,9 +1069,12 @@ namespace ICSharpCode.Decompiler.Disassembler
{
output.Write(".assembly " + DisassemblerHelpers.Escape(asm.Name.Name));
OpenBlock(false);
Version v = asm.Name.Version;
if (v != null) {
output.WriteLine(".ver {0}:{1}:{2}:{3}", v.Major, v.Minor, v.Build, v.Revision);
WriteAttributes(asm.CustomAttributes);
WriteSecurityDeclarations(asm);
if (asm.Name.PublicKey != null && asm.Name.PublicKey.Length > 0) {
output.Write(".publickey = ");
WriteBlob(asm.Name.PublicKey);
output.WriteLine();
}
if (asm.Name.HashAlgorithm != AssemblyHashAlgorithm.None) {
output.Write(".hash algorithm 0x{0:x8}", (int)asm.Name.HashAlgorithm);
@ -596,15 +1082,66 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -596,15 +1082,66 @@ namespace ICSharpCode.Decompiler.Disassembler
output.Write(" // SHA1");
output.WriteLine();
}
if (asm.Name.PublicKey != null && asm.Name.PublicKey.Length > 0) {
output.Write(".publickey = ");
WriteBlob(asm.Name.PublicKey);
output.WriteLine();
Version v = asm.Name.Version;
if (v != null) {
output.WriteLine(".ver {0}:{1}:{2}:{3}", v.Major, v.Minor, v.Build, v.Revision);
}
WriteAttributes(asm.CustomAttributes);
CloseBlock();
}
public void WriteAssemblyReferences(ModuleDefinition module)
{
foreach (var mref in module.ModuleReferences) {
output.WriteLine(".module extern {0}", DisassemblerHelpers.Escape(mref.Name));
}
foreach (var aref in module.AssemblyReferences) {
output.Write(".assembly extern {0}", DisassemblerHelpers.Escape(aref.Name));
OpenBlock(false);
if (aref.PublicKeyToken != null) {
output.Write(".publickeytoken = ");
WriteBlob(aref.PublicKeyToken);
output.WriteLine();
}
if (aref.Version != null) {
output.WriteLine(".ver {0}:{1}:{2}:{3}", aref.Version.Major, aref.Version.Minor, aref.Version.Build, aref.Version.Revision);
}
CloseBlock();
}
}
public void WriteModuleHeader(ModuleDefinition module)
{
if (module.HasExportedTypes) {
foreach (ExportedType exportedType in module.ExportedTypes) {
output.Write(".class extern ");
if (exportedType.IsForwarder)
output.Write("forwarder ");
output.Write(exportedType.DeclaringType != null ? exportedType.Name : exportedType.FullName);
OpenBlock(false);
if (exportedType.DeclaringType != null)
output.WriteLine(".class extern {0}", DisassemblerHelpers.Escape(exportedType.DeclaringType.FullName));
else
output.WriteLine(".assembly extern {0}", DisassemblerHelpers.Escape(exportedType.Scope.Name));
CloseBlock();
}
}
output.WriteLine(".module {0}", module.Name);
output.WriteLine("// MVID: {0}", module.Mvid.ToString("B").ToUpperInvariant());
// TODO: imagebase, file alignment, stackreserve, subsystem
output.WriteLine(".corflags 0x{0:x} // {1}", module.Attributes, module.Attributes.ToString());
WriteAttributes(module.CustomAttributes);
}
public void WriteModuleContents(ModuleDefinition module)
{
foreach (TypeDefinition td in module.Types) {
DisassembleType(td);
output.WriteLine();
}
}
/// <inheritdoc/>
public Tuple<string, List<MemberMapping>> CodeMappings {
get;

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -61,6 +61,7 @@ @@ -61,6 +61,7 @@
<Compile Include="Ast\Transforms\CombineQueryExpressions.cs" />
<Compile Include="Ast\Transforms\ContextTrackingVisitor.cs" />
<Compile Include="Ast\Transforms\ConvertConstructorCallIntoInitializer.cs" />
<Compile Include="Ast\Transforms\DecimalConstantTransform.cs" />
<Compile Include="Ast\Transforms\DeclareVariables.cs" />
<Compile Include="Ast\Transforms\DelegateConstruction.cs" />
<Compile Include="Ast\Transforms\IntroduceExtensionMethods.cs" />

10
ICSharpCode.Decompiler/ILAst/GotoRemoval.cs

@ -91,7 +91,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -91,7 +91,7 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
var defaultCase = ilSwitch.CaseBlocks.Where(cb => cb.Values == null).SingleOrDefault();
var defaultCase = ilSwitch.CaseBlocks.SingleOrDefault(cb => cb.Values == null);
// If there is no default block, remove empty case blocks
if (defaultCase == null || (defaultCase.Body.Count == 1 && defaultCase.Body.Single().Match(ILCode.LoopOrSwitchBreak))) {
ilSwitch.CaseBlocks.RemoveAll(b => b.Body.Count == 1 && b.Body.Single().Match(ILCode.LoopOrSwitchBreak));
@ -140,14 +140,14 @@ namespace ICSharpCode.Decompiler.ILAst @@ -140,14 +140,14 @@ namespace ICSharpCode.Decompiler.ILAst
return true;
}
ILNode breakBlock = GetParents(gotoExpr).Where(n => n is ILWhileLoop || n is ILSwitch).FirstOrDefault();
ILNode breakBlock = GetParents(gotoExpr).FirstOrDefault(n => n is ILWhileLoop || n is ILSwitch);
if (breakBlock != null && target == Exit(breakBlock, new HashSet<ILNode>() { gotoExpr })) {
gotoExpr.Code = ILCode.LoopOrSwitchBreak;
gotoExpr.Operand = null;
return true;
}
ILNode continueBlock = GetParents(gotoExpr).Where(n => n is ILWhileLoop).FirstOrDefault();
ILNode continueBlock = GetParents(gotoExpr).FirstOrDefault(n => n is ILWhileLoop);
if (continueBlock != null && target == Enter(continueBlock, new HashSet<ILNode>() { gotoExpr })) {
gotoExpr.Code = ILCode.LoopContinue;
gotoExpr.Operand = null;
@ -209,10 +209,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -209,10 +209,10 @@ namespace ICSharpCode.Decompiler.ILAst
} else if (expr.Code == ILCode.Nop) {
return Exit(expr, visitedNodes);
} else if (expr.Code == ILCode.LoopOrSwitchBreak) {
ILNode breakBlock = GetParents(expr).Where(n => n is ILWhileLoop || n is ILSwitch).First();
ILNode breakBlock = GetParents(expr).First(n => n is ILWhileLoop || n is ILSwitch);
return Exit(breakBlock, new HashSet<ILNode>() { expr });
} else if (expr.Code == ILCode.LoopContinue) {
ILNode continueBlock = GetParents(expr).Where(n => n is ILWhileLoop).First();
ILNode continueBlock = GetParents(expr).First(n => n is ILWhileLoop);
return Enter(continueBlock, new HashSet<ILNode>() { expr });
} else {
return expr;

4
ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs

@ -455,7 +455,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -455,7 +455,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1) {
var locVars = byteCode.StoreTo;
// For each of the variables, find the location where it is loaded - there should be preciesly one
var loadedBy = locVars.Select(locVar => reachableBody.SelectMany(bc => bc.StackBefore).Where(s => s.LoadFrom == locVar).Single()).ToList();
var loadedBy = locVars.Select(locVar => reachableBody.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList();
// We now know that all the variables have a single load,
// Let's make sure that they have also a single store - us
if (loadedBy.All(slot => slot.PushedBy.Length == 1 && slot.PushedBy[0] == byteCode)) {
@ -572,7 +572,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -572,7 +572,7 @@ namespace ICSharpCode.Decompiler.ILAst
Loads = new List<ByteCode>() { load }
});
} else if (storedBy.Length == 1) {
VariableInfo newVar = newVars.Where(v => v.Stores.Contains(storedBy[0])).Single();
VariableInfo newVar = newVars.Single(v => v.Stores.Contains(storedBy[0]));
newVar.Loads.Add(load);
} else {
List<VariableInfo> mergeVars = newVars.Where(v => v.Stores.Union(storedBy).Any()).ToList();

21
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -45,6 +45,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -45,6 +45,7 @@ namespace ICSharpCode.Decompiler.ILAst
SimplifyLogicNot,
TransformDecimalCtorToConstant,
SimplifyLdObjAndStObj,
SimplifyCustomShortCircuit,
TransformArrayInitializers,
TransformObjectInitializers,
SimplifyNullableOperators,
@ -141,6 +142,9 @@ namespace ICSharpCode.Decompiler.ILAst @@ -141,6 +142,9 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.SimplifyLdObjAndStObj) return;
modified |= block.RunOptimization(SimplifyLdObjAndStObj);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyCustomShortCircuit) return;
modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyCustomShortCircuit);
if (abortBeforeStep == ILAstOptimizationStep.TransformArrayInitializers) return;
modified |= block.RunOptimization(TransformArrayInitializers);
@ -319,6 +323,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -319,6 +323,8 @@ namespace ICSharpCode.Decompiler.ILAst
/// Converts call and callvirt instructions that read/write properties into CallGetter/CallSetter instructions.
///
/// CallGetter/CallSetter is used to allow the ILAst to represent "while ((SomeProperty = value) != null)".
///
/// Also simplifies 'newobj(SomeDelegate, target, ldvirtftn(F, target))' to 'newobj(SomeDelegate, target, ldvirtftn(F))'
/// </summary>
void IntroducePropertyAccessInstructions(ILNode node)
{
@ -376,6 +382,19 @@ namespace ICSharpCode.Decompiler.ILAst @@ -376,6 +382,19 @@ namespace ICSharpCode.Decompiler.ILAst
expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallSetter : ILCode.CallvirtSetter;
}
}
} else if (expr.Code == ILCode.Newobj && expr.Arguments.Count == 2) {
// Might be 'newobj(SomeDelegate, target, ldvirtftn(F, target))'.
ILVariable target;
if (expr.Arguments[0].Match(ILCode.Ldloc, out target)
&& expr.Arguments[1].Code == ILCode.Ldvirtftn
&& expr.Arguments[1].Arguments.Count == 1
&& expr.Arguments[1].Arguments[0].MatchLdloc(target))
{
// Remove the 'target' argument from the ldvirtftn instruction.
// It's not needed in the translation to C#, and needs to be eliminated so that the target expression
// can be inlined.
expr.Arguments[1].Arguments.Clear();
}
}
}
@ -409,7 +428,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -409,7 +428,7 @@ namespace ICSharpCode.Decompiler.ILAst
lastNode.IsUnconditionalControlFlow())
{
// Try to reuse the label
ILLabel label = currNode is ILLabel ? ((ILLabel)currNode) : new ILLabel() { Name = "Block_" + (nextLabelIndex++) };
ILLabel label = currNode as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++).ToString() };
// Terminate the last block
if (!lastNode.IsUnconditionalControlFlow()) {

18
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -141,6 +141,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -141,6 +141,10 @@ namespace ICSharpCode.Decompiler.ILAst
{
output.Write("catch ");
output.WriteReference(ExceptionType.FullName, ExceptionType);
if (ExceptionVariable != null) {
output.Write(' ');
output.Write(ExceptionVariable.Name);
}
output.WriteLine(" {");
output.Indent();
base.WriteTo(output);
@ -378,10 +382,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -378,10 +382,10 @@ namespace ICSharpCode.Decompiler.ILAst
output.Write(((ILVariable)Operand).Name);
if (this.InferredType != null) {
output.Write(':');
this.InferredType.WriteTo(output, true, true);
this.InferredType.WriteTo(output, ILNameSyntax.ShortTypeName);
if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) {
output.Write("[exp:");
this.ExpectedType.WriteTo(output, true, true);
this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write(']');
}
}
@ -399,15 +403,15 @@ namespace ICSharpCode.Decompiler.ILAst @@ -399,15 +403,15 @@ namespace ICSharpCode.Decompiler.ILAst
output.Write(Code.GetName());
if (this.InferredType != null) {
output.Write(':');
this.InferredType.WriteTo(output, true, true);
this.InferredType.WriteTo(output, ILNameSyntax.ShortTypeName);
if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) {
output.Write("[exp:");
this.ExpectedType.WriteTo(output, true, true);
this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write(']');
}
} else if (this.ExpectedType != null) {
output.Write("[exp:");
this.ExpectedType.WriteTo(output, true, true);
this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write(']');
}
output.Write('(');
@ -425,13 +429,13 @@ namespace ICSharpCode.Decompiler.ILAst @@ -425,13 +429,13 @@ namespace ICSharpCode.Decompiler.ILAst
} else if (Operand is MethodReference) {
MethodReference method = (MethodReference)Operand;
if (method.DeclaringType != null) {
method.DeclaringType.WriteTo(output, true, true);
method.DeclaringType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write("::");
}
output.WriteReference(method.Name, method);
} else if (Operand is FieldReference) {
FieldReference field = (FieldReference)Operand;
field.DeclaringType.WriteTo(output, true, true);
field.DeclaringType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write("::");
output.WriteReference(field.Name, field);
} else {

32
ICSharpCode.Decompiler/ILAst/ILInlining.cs

@ -47,7 +47,13 @@ namespace ICSharpCode.Decompiler.ILAst @@ -47,7 +47,13 @@ namespace ICSharpCode.Decompiler.ILAst
numLdloca.Clear();
// Analyse the whole method
foreach(ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
AnalyzeNode(method);
}
void AnalyzeNode(ILNode node)
{
ILExpression expr = node as ILExpression;
if (expr != null) {
ILVariable locVar = expr.Operand as ILVariable;
if (locVar != null) {
if (expr.Code == ILCode.Stloc) {
@ -60,6 +66,16 @@ namespace ICSharpCode.Decompiler.ILAst @@ -60,6 +66,16 @@ namespace ICSharpCode.Decompiler.ILAst
throw new NotSupportedException(expr.Code.ToString());
}
}
foreach (ILExpression child in expr.Arguments)
AnalyzeNode(child);
} else {
var catchBlock = node as ILTryCatchBlock.CatchBlock;
if (catchBlock != null && catchBlock.ExceptionVariable != null) {
numStloc[catchBlock.ExceptionVariable] = numStloc.GetOrDefault(catchBlock.ExceptionVariable) + 1;
}
foreach (ILNode child in node.GetChildren())
AnalyzeNode(child);
}
}
@ -76,6 +92,20 @@ namespace ICSharpCode.Decompiler.ILAst @@ -76,6 +92,20 @@ namespace ICSharpCode.Decompiler.ILAst
{
bool modified = false;
List<ILNode> body = block.Body;
if (block is ILTryCatchBlock.CatchBlock && body.Count > 1) {
ILVariable v = ((ILTryCatchBlock.CatchBlock)block).ExceptionVariable;
if (v != null && v.IsGenerated) {
if (numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 1 && numLdloc.GetOrDefault(v) == 1) {
ILVariable v2;
ILExpression ldException;
if (body[0].Match(ILCode.Stloc, out v2, out ldException) && ldException.MatchLdloc(v)) {
body.RemoveAt(0);
((ILTryCatchBlock.CatchBlock)block).ExceptionVariable = v2;
modified = true;
}
}
}
}
for(int i = 0; i < body.Count - 1;) {
ILVariable locVar;
ILExpression expr;

4
ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs

@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.ILAst
{
Dictionary<ILLabel, ControlFlowNode> labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>();
DecompilerContext context;
readonly DecompilerContext context;
uint nextLabelIndex = 0;
@ -286,7 +286,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -286,7 +286,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILLabel condLabel = caseLabels[i];
// Find or create new case block
ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.Where(b => b.EntryGoto.Operand == condLabel).FirstOrDefault();
ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.FirstOrDefault(b => b.EntryGoto.Operand == condLabel);
if (caseBlock == null) {
caseBlock = new ILSwitch.CaseBlock() {
Values = new List<int>(),

75
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -90,6 +90,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -90,6 +90,10 @@ namespace ICSharpCode.Decompiler.ILAst
expr.Code = ILCode.Stobj;
expr.Arguments.Add(new ILExpression(ILCode.DefaultValue, expr.Operand));
modified = true;
} else if (expr.Code == ILCode.Cpobj) {
expr.Code = ILCode.Stobj;
expr.Arguments[1] = new ILExpression(ILCode.Ldobj, expr.Operand, expr.Arguments[1]);
modified = true;
}
ILExpression arg, arg2;
TypeReference type;
@ -440,6 +444,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -440,6 +444,7 @@ namespace ICSharpCode.Decompiler.ILAst
#endregion
#region IntroducePostIncrement
bool IntroducePostIncrement(List<ILNode> body, ILExpression expr, int pos)
{
bool modified = IntroducePostIncrementForVariables(body, expr, pos);
@ -452,7 +457,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -452,7 +457,7 @@ namespace ICSharpCode.Decompiler.ILAst
}
return modified;
}
bool IntroducePostIncrementForVariables(List<ILNode> body, ILExpression expr, int pos)
{
// Works for variables and static fields/properties
@ -465,19 +470,50 @@ namespace ICSharpCode.Decompiler.ILAst @@ -465,19 +470,50 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression exprInit;
if (!(expr.Match(ILCode.Stloc, out exprVar, out exprInit) && exprVar.IsGenerated))
return false;
if (!(exprInit.Code == ILCode.Ldloc || exprInit.Code == ILCode.Ldsfld || (exprInit.Code == ILCode.CallGetter && exprInit.Arguments.Count == 0)))
return false;
//The next expression
ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression;
if (nextExpr == null)
return false;
if (exprInit.Code == ILCode.CallGetter) {
if (!(nextExpr.Code == ILCode.CallSetter && IsGetterSetterPair(exprInit.Operand, nextExpr.Operand)))
return false;
} else {
if (!(nextExpr.Code == (exprInit.Code == ILCode.Ldloc ? ILCode.Stloc : ILCode.Stsfld) && nextExpr.Operand == exprInit.Operand))
ILCode loadInstruction = exprInit.Code;
ILCode storeInstruction = nextExpr.Code;
bool recombineVariable = false;
// We only recognise local variables, static fields, and static getters with no arguments
switch (loadInstruction) {
case ILCode.Ldloc:
//Must be a matching store type
if (storeInstruction != ILCode.Stloc)
return false;
ILVariable loadVar = (ILVariable)exprInit.Operand;
ILVariable storeVar = (ILVariable)nextExpr.Operand;
if (loadVar != storeVar) {
if (loadVar.OriginalVariable != null && loadVar.OriginalVariable == storeVar.OriginalVariable)
recombineVariable = true;
else
return false;
}
break;
case ILCode.Ldsfld:
if (storeInstruction != ILCode.Stsfld)
return false;
if (exprInit.Operand != nextExpr.Operand)
return false;
break;
case ILCode.CallGetter:
// non-static getters would have the 'this' argument
if (exprInit.Arguments.Count != 0)
return false;
if (storeInstruction != ILCode.CallSetter)
return false;
if (!IsGetterSetterPair(exprInit.Operand, nextExpr.Operand))
return false;
break;
default:
return false;
}
ILExpression addExpr = nextExpr.Arguments[0];
int incrementAmount;
@ -485,12 +521,23 @@ namespace ICSharpCode.Decompiler.ILAst @@ -485,12 +521,23 @@ namespace ICSharpCode.Decompiler.ILAst
if (!(incrementAmount != 0 && addExpr.Arguments[0].MatchLdloc(exprVar)))
return false;
if (exprInit.Code == ILCode.Ldloc)
exprInit.Code = ILCode.Ldloca;
else if (exprInit.Code == ILCode.CallGetter)
exprInit = new ILExpression(ILCode.AddressOf, null, exprInit);
else
exprInit.Code = ILCode.Ldsflda;
if (recombineVariable) {
// Split local variable, unsplit these two instances
foreach (var ilExpression in method.GetSelfAndChildrenRecursive<ILExpression>(expression => expression.Operand == nextExpr.Operand))
ilExpression.Operand = exprInit.Operand;
}
switch (loadInstruction) {
case ILCode.Ldloc:
exprInit.Code = ILCode.Ldloca;
break;
case ILCode.Ldsfld:
exprInit.Code = ILCode.Ldsflda;
break;
case ILCode.CallGetter:
exprInit = new ILExpression(ILCode.AddressOf, null, exprInit);
break;
}
expr.Arguments[0] = new ILExpression(incrementCode, incrementAmount, exprInit);
body.RemoveAt(pos + 1); // TODO ILRanges
return true;

107
ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs

@ -115,7 +115,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -115,7 +115,7 @@ namespace ICSharpCode.Decompiler.ILAst
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, condExpr, trueExpr);
}
} else {
// Ternary operator tends to create long complicated return statements
// Ternary operator tends to create long complicated return statements
if (opCode == ILCode.Ret)
return false;
@ -156,7 +156,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -156,7 +156,7 @@ namespace ICSharpCode.Decompiler.ILAst
// ...
// v = NullCoalescing(ldloc(leftVar), rightExpr)
// br(endBBLabel)
ILVariable v, v2;
ILExpression leftExpr, leftExpr2;
ILVariable leftVar;
@ -165,7 +165,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -165,7 +165,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILBasicBlock rightBB;
ILExpression rightExpr;
if (head.Body.Count >= 3 &&
head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out v, out leftExpr) &&
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.MatchLdloc(leftVar) &&
@ -234,6 +234,107 @@ namespace ICSharpCode.Decompiler.ILAst @@ -234,6 +234,107 @@ namespace ICSharpCode.Decompiler.ILAst
return false;
}
public bool SimplifyCustomShortCircuit(List<ILNode> body, ILBasicBlock head, int pos)
{
Debug.Assert(body.Contains(head));
// --- looking for the following pattern ---
// stloc(targetVar, leftVar)
// brtrue(exitLabel, call(op_False, leftVar)
// br(followingBlock)
//
// FollowingBlock:
// stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression))
// br(exitLabel)
// ---
if (head.Body.Count < 3)
return false;
// looking for:
// stloc(targetVar, leftVar)
ILVariable targetVar;
ILExpression targetVarInitExpr;
if (!head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out targetVar, out targetVarInitExpr))
return false;
ILVariable leftVar;
if (!targetVarInitExpr.Match(ILCode.Ldloc, out leftVar))
return false;
// looking for:
// brtrue(exitLabel, call(op_False, leftVar)
// br(followingBlock)
ILExpression callExpr;
ILLabel exitLabel;
ILLabel followingBlock;
if(!head.MatchLastAndBr(ILCode.Brtrue, out exitLabel, out callExpr, out followingBlock))
return false;
if (labelGlobalRefCount[followingBlock] > 1)
return false;
MethodReference opFalse;
ILExpression opFalseArg;
if (!callExpr.Match(ILCode.Call, out opFalse, out opFalseArg))
return false;
// ignore operators other than op_False and op_True
if (opFalse.Name != "op_False" && opFalse.Name != "op_True")
return false;
if (!opFalseArg.MatchLdloc(leftVar))
return false;
ILBasicBlock followingBasicBlock = labelToBasicBlock[followingBlock];
// FollowingBlock:
// stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression))
// br(exitLabel)
ILVariable _targetVar;
ILExpression opBitwiseCallExpr;
ILLabel _exitLabel;
if (!followingBasicBlock.MatchSingleAndBr(ILCode.Stloc, out _targetVar, out opBitwiseCallExpr, out _exitLabel))
return false;
if (_targetVar != targetVar || exitLabel != _exitLabel)
return false;
MethodReference opBitwise;
ILExpression leftVarExpression;
ILExpression rightExpression;
if (!opBitwiseCallExpr.Match(ILCode.Call, out opBitwise, out leftVarExpression, out rightExpression))
return false;
if (!opFalseArg.MatchLdloc(leftVarExpression.Operand as ILVariable))
return false;
// ignore operators other than op_BitwiseAnd and op_BitwiseOr
if (opBitwise.Name != "op_BitwiseAnd" && opBitwise.Name != "op_BitwiseOr")
return false;
// insert:
// stloc(targetVar, LogicAnd(C::op_BitwiseAnd, leftVar, rightExpression)
// br(exitLabel)
ILCode op = opBitwise.Name == "op_BitwiseAnd" ? ILCode.LogicAnd : ILCode.LogicOr;
if (op == ILCode.LogicAnd && opFalse.Name != "op_False")
return false;
if (op == ILCode.LogicOr && opFalse.Name != "op_True")
return false;
ILExpression shortCircuitExpr = MakeLeftAssociativeShortCircuit(op, opFalseArg, rightExpression);
shortCircuitExpr.Operand = opBitwise;
head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br);
head.Body.Add(new ILExpression(ILCode.Stloc, targetVar, shortCircuitExpr));
head.Body.Add(new ILExpression(ILCode.Br, exitLabel));
body.Remove(followingBasicBlock);
return true;
}
ILExpression MakeLeftAssociativeShortCircuit(ILCode code, ILExpression left, ILExpression right)
{
// Assuming that the inputs are already left associative

4
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -267,6 +267,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -267,6 +267,10 @@ namespace ICSharpCode.Decompiler.ILAst
return typeSystem.Boolean;
case ILCode.LogicAnd:
case ILCode.LogicOr:
// if Operand is set the logic and/or expression is a custom operator
// we can deal with it the same as a normal invocation.
if (expr.Operand != null)
goto case ILCode.Call;
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean);
InferTypeForExpression(expr.Arguments[1], typeSystem.Boolean);

75
ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs

@ -0,0 +1,75 @@ @@ -0,0 +1,75 @@
// 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;
public static class CustomShortCircuitOperators
{
private class B
{
public static bool operator true(CustomShortCircuitOperators.B x)
{
return true;
}
public static bool operator false(CustomShortCircuitOperators.B x)
{
return false;
}
}
private class C : CustomShortCircuitOperators.B
{
public static CustomShortCircuitOperators.C operator &(CustomShortCircuitOperators.C x, CustomShortCircuitOperators.C y)
{
return null;
}
public static CustomShortCircuitOperators.C operator |(CustomShortCircuitOperators.C x, CustomShortCircuitOperators.C y)
{
return null;
}
public static bool operator !(CustomShortCircuitOperators.C x)
{
return false;
}
private static void Main()
{
CustomShortCircuitOperators.C c = new CustomShortCircuitOperators.C();
CustomShortCircuitOperators.C c2 = new CustomShortCircuitOperators.C();
CustomShortCircuitOperators.C c3 = c && c2;
CustomShortCircuitOperators.C c4 = c || c2;
Console.WriteLine(c3.ToString());
Console.WriteLine(c4.ToString());
}
private static void Test2()
{
CustomShortCircuitOperators.C c = new CustomShortCircuitOperators.C();
if (c && c)
{
Console.WriteLine(c.ToString());
}
if (!(c && c))
{
Console.WriteLine(c.ToString());
}
}
private static void Test3()
{
CustomShortCircuitOperators.C c = new CustomShortCircuitOperators.C();
if (c)
{
Console.WriteLine(c.ToString());
}
if (!c)
{
Console.WriteLine(c.ToString());
}
}
}
}

30
ICSharpCode.Decompiler/Tests/ExceptionHandling.cs

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Threading;
public class ExceptionHandling
{
@ -55,13 +56,38 @@ public class ExceptionHandling @@ -55,13 +56,38 @@ public class ExceptionHandling
{
Console.WriteLine(ex.Message);
}
catch (Exception ex)
catch (Exception ex2)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex2.Message);
}
catch
{
Console.WriteLine("other");
}
}
public void NoUsingStatementBecauseTheVariableIsAssignedTo()
{
CancellationTokenSource cancellationTokenSource = null;
try
{
cancellationTokenSource = new CancellationTokenSource();
}
finally
{
if (cancellationTokenSource != null)
{
cancellationTokenSource.Dispose();
}
}
}
public void UsingStatementThatChangesTheVariable()
{
CancellationTokenSource cancellationTokenSource = null;
using (cancellationTokenSource)
{
cancellationTokenSource = new CancellationTokenSource();
}
}
}

6
ICSharpCode.Decompiler/Tests/Generics.cs

@ -60,6 +60,12 @@ public static class Generics @@ -60,6 +60,12 @@ public static class Generics
}
}
private static Type type1 = typeof(List<>);
private static Type type2 = typeof(Generics.MyArray<>);
private static Type type3 = typeof(List<>.Enumerator);
private static Type type4 = typeof(Generics.MyArray<>.NestedClass<>);
private static Type type5 = typeof(List<int>[]);
public static void MethodWithConstraint<T, S>() where T : class, S where S : ICloneable, new()
{
}

2
ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs

@ -15,7 +15,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -15,7 +15,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
var section = (AttributeSection)attribute.Parent;
SimpleType type = attribute.Type as SimpleType;
if (section.AttributeTarget == "assembly" &&
(type.Identifier == "CompilationRelaxations" || type.Identifier == "RuntimeCompatibility"))
(type.Identifier == "CompilationRelaxations" || type.Identifier == "RuntimeCompatibility" || type.Identifier == "SecurityPermission" || type.Identifier == "AssemblyVersion"))
{
attribute.Remove();
if (section.Attributes.Count == 0)

2
ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

@ -57,8 +57,10 @@ @@ -57,8 +57,10 @@
<ItemGroup>
<Compile Include="CallOverloadedMethod.cs" />
<Compile Include="CheckedUnchecked.cs" />
<Compile Include="CustomShortCircuitOperators.cs" />
<Compile Include="Helpers\CodeAssert.cs" />
<Compile Include="IncrementDecrement.cs" />
<Compile Include="PInvoke.cs" />
<Compile Include="QueryExpressions.cs" />
<Compile Include="Switch.cs" />
<Compile Include="UndocumentedExpressions.cs" />

6
ICSharpCode.Decompiler/Tests/IncrementDecrement.cs

@ -176,6 +176,12 @@ public class IncrementDecrement @@ -176,6 +176,12 @@ public class IncrementDecrement
return i++ + j;
}
public void PostIncrementInlineLocalVariable(Func<int, int> f)
{
int num = 0;
f(num++);
}
public int PostIncrementArrayElement(int[] array, int pos)
{
return array[pos]--;

87
ICSharpCode.Decompiler/Tests/PInvoke.cs

@ -0,0 +1,87 @@ @@ -0,0 +1,87 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Runtime.InteropServices;
// P/Invoke and marshalling attribute tests
public class PInvoke
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 2)]
public struct MarshalAsTest
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public uint[] FixedArray;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.Bool)]
public int[] FixedBoolArray;
[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
public string[] SafeBStrArray;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
public string FixedString;
}
[StructLayout(LayoutKind.Explicit)]
public struct Rect
{
[FieldOffset(0)]
public int left;
[FieldOffset(4)]
public int top;
[FieldOffset(8)]
public int right;
[FieldOffset(12)]
public int bottom;
}
public static decimal MarshalAttributesOnPropertyAccessors
{
[return: MarshalAs(UnmanagedType.Currency)]
get
{
return 0m;
}
[param: MarshalAs(UnmanagedType.Currency)]
set
{
}
}
[DllImport("xyz.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Method([MarshalAs(UnmanagedType.LPStr)] string input);
[DllImport("xyz.dll")]
private static extern void New1(int ElemCnt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] ar);
[DllImport("xyz.dll")]
private static extern void New2([MarshalAs(UnmanagedType.LPArray, SizeConst = 128)] int[] ar);
[DllImport("xyz.dll")]
private static extern void New3([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Bool, SizeConst = 64, SizeParamIndex = 1)] int[] ar);
public void CustomMarshal1([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "MyCompany.MyMarshaler")] object o)
{
}
public void CustomMarshal2([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "MyCompany.MyMarshaler", MarshalCookie = "Cookie")] object o)
{
}
}

14
ICSharpCode.Decompiler/Tests/TestRunner.cs

@ -37,7 +37,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -37,7 +37,7 @@ namespace ICSharpCode.Decompiler.Tests
TestFile(@"..\..\Tests\DelegateConstruction.cs");
}
[Test, Ignore("arg-Variables in catch clauses")]
[Test]
public void ExceptionHandling()
{
TestFile(@"..\..\Tests\ExceptionHandling.cs");
@ -49,6 +49,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -49,6 +49,12 @@ namespace ICSharpCode.Decompiler.Tests
TestFile(@"..\..\Tests\Generics.cs");
}
[Test]
public void CustomShortCircuitOperators()
{
TestFile(@"..\..\Tests\CustomShortCircuitOperators.cs");
}
[Test]
public void IncrementDecrement()
{
@ -73,6 +79,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -73,6 +79,12 @@ namespace ICSharpCode.Decompiler.Tests
TestFile(@"..\..\Tests\MultidimensionalArray.cs");
}
[Test]
public void PInvoke()
{
TestFile(@"..\..\Tests\PInvoke.cs");
}
[Test]
public void PropertiesAndEvents()
{

4
ILSpy/AboutPage.cs

@ -93,7 +93,7 @@ namespace ICSharpCode.ILSpy @@ -93,7 +93,7 @@ namespace ICSharpCode.ILSpy
output.AddVisualLineElementGenerator(new MyLinkElementGenerator("SharpDevelop", "http://www.icsharpcode.net/opensource/sd/"));
output.AddVisualLineElementGenerator(new MyLinkElementGenerator("MIT License", "resource:license.txt"));
output.AddVisualLineElementGenerator(new MyLinkElementGenerator("LGPL", "resource:LGPL.txt"));
textView.Show(output);
textView.ShowText(output);
}
sealed class MyLinkElementGenerator : LinkElementGenerator
@ -130,7 +130,7 @@ namespace ICSharpCode.ILSpy @@ -130,7 +130,7 @@ namespace ICSharpCode.ILSpy
} catch (Exception ex) {
AvalonEditTextOutput exceptionOutput = new AvalonEditTextOutput();
exceptionOutput.WriteLine(ex.ToString());
textView.Show(exceptionOutput);
textView.ShowText(exceptionOutput);
}
}, TaskScheduler.FromCurrentSynchronizationContext());
};

2
ILSpy/App.xaml.cs

@ -165,7 +165,7 @@ namespace ICSharpCode.ILSpy @@ -165,7 +165,7 @@ namespace ICSharpCode.ILSpy
}
}
}
ILSpy.MainWindow.Instance.TextView.Show(output);
ILSpy.MainWindow.Instance.TextView.ShowText(output);
} else {
Process.Start(e.Uri.ToString());
}

2
ILSpy/BamlDecompiler.cs

@ -401,7 +401,7 @@ namespace ICSharpCode.ILSpy.Baml @@ -401,7 +401,7 @@ namespace ICSharpCode.ILSpy.Baml
}
return output;
}),
t => textView.Show(t.Result, highlighting)
t => textView.ShowNode(t.Result, this, highlighting)
);
return true;
}

200
ILSpy/CSharpLanguage.cs

@ -47,11 +47,11 @@ namespace ICSharpCode.ILSpy @@ -47,11 +47,11 @@ namespace ICSharpCode.ILSpy
string name = "C#";
bool showAllMembers = false;
Predicate<IAstTransform> transformAbortCondition = null;
public CSharpLanguage()
{
}
#if DEBUG
internal static IEnumerable<CSharpLanguage> GetDebugLanguages()
{
@ -72,27 +72,72 @@ namespace ICSharpCode.ILSpy @@ -72,27 +72,72 @@ namespace ICSharpCode.ILSpy
};
}
#endif
public override string Name {
public override string Name
{
get { return name; }
}
public override string FileExtension {
public override string FileExtension
{
get { return ".cs"; }
}
public override string ProjectFileExtension {
public override string ProjectFileExtension
{
get { return ".csproj"; }
}
public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, TypeToString(method.DeclaringType, includeNamespace: true));
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: method.DeclaringType, isSingleMember: true);
codeDomBuilder.AddMethod(method);
RunTransformsAndGenerateCode(codeDomBuilder, output, options);
if (method.IsConstructor && !method.IsStatic && !method.DeclaringType.IsValueType) {
// also fields and other ctors so that the field initializers can be shown as such
AddFieldsAndCtors(codeDomBuilder, method.DeclaringType, method.IsStatic);
RunTransformsAndGenerateCode(codeDomBuilder, output, options, new SelectCtorTransform(method));
} else {
codeDomBuilder.AddMethod(method);
RunTransformsAndGenerateCode(codeDomBuilder, output, options);
}
}
class SelectCtorTransform : IAstTransform
{
readonly MethodDefinition ctorDef;
public SelectCtorTransform(MethodDefinition ctorDef)
{
this.ctorDef = ctorDef;
}
public void Run(AstNode compilationUnit)
{
ConstructorDeclaration ctorDecl = null;
foreach (var node in compilationUnit.Children) {
ConstructorDeclaration ctor = node as ConstructorDeclaration;
if (ctor != null) {
if (ctor.Annotation<MethodDefinition>() == ctorDef) {
ctorDecl = ctor;
} else {
// remove other ctors
ctor.Remove();
}
}
// Remove any fields without initializers
FieldDeclaration fd = node as FieldDeclaration;
if (fd != null && fd.Variables.All(v => v.Initializer.IsNull))
fd.Remove();
}
if (ctorDecl.Initializer.ConstructorInitializerType == ConstructorInitializerType.This) {
// remove all fields
foreach (var node in compilationUnit.Children)
if (node is FieldDeclaration)
node.Remove();
}
}
}
public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, TypeToString(property.DeclaringType, includeNamespace: true));
@ -100,15 +145,55 @@ namespace ICSharpCode.ILSpy @@ -100,15 +145,55 @@ namespace ICSharpCode.ILSpy
codeDomBuilder.AddProperty(property);
RunTransformsAndGenerateCode(codeDomBuilder, output, options);
}
public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, TypeToString(field.DeclaringType, includeNamespace: true));
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: field.DeclaringType, isSingleMember: true);
codeDomBuilder.AddField(field);
RunTransformsAndGenerateCode(codeDomBuilder, output, options);
if (field.IsLiteral) {
codeDomBuilder.AddField(field);
} else {
// also decompile ctors so that the field initializer can be shown
AddFieldsAndCtors(codeDomBuilder, field.DeclaringType, field.IsStatic);
}
RunTransformsAndGenerateCode(codeDomBuilder, output, options, new SelectFieldTransform(field));
}
/// <summary>
/// Removes all top-level members except for the specified fields.
/// </summary>
sealed class SelectFieldTransform : IAstTransform
{
readonly FieldDefinition field;
public SelectFieldTransform(FieldDefinition field)
{
this.field = field;
}
public void Run(AstNode compilationUnit)
{
foreach (var child in compilationUnit.Children) {
if (child is AttributedNode) {
if (child.Annotation<FieldDefinition>() != field)
child.Remove();
}
}
}
}
void AddFieldsAndCtors(AstBuilder codeDomBuilder, TypeDefinition declaringType, bool isStatic)
{
foreach (var field in declaringType.Fields) {
if (field.IsStatic == isStatic)
codeDomBuilder.AddField(field);
}
foreach (var ctor in declaringType.Methods) {
if (ctor.IsConstructor && ctor.IsStatic == isStatic)
codeDomBuilder.AddMethod(ctor);
}
}
public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, TypeToString(ev.DeclaringType, includeNamespace: true));
@ -116,7 +201,7 @@ namespace ICSharpCode.ILSpy @@ -116,7 +201,7 @@ namespace ICSharpCode.ILSpy
codeDomBuilder.AddEvent(ev);
RunTransformsAndGenerateCode(codeDomBuilder, output, options);
}
public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options)
{
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: type);
@ -124,14 +209,18 @@ namespace ICSharpCode.ILSpy @@ -124,14 +209,18 @@ namespace ICSharpCode.ILSpy
RunTransformsAndGenerateCode(codeDomBuilder, output, options);
}
void RunTransformsAndGenerateCode(AstBuilder astBuilder, ITextOutput output, DecompilationOptions options)
void RunTransformsAndGenerateCode(AstBuilder astBuilder, ITextOutput output, DecompilationOptions options, IAstTransform additionalTransform = null)
{
astBuilder.RunTransformations(transformAbortCondition);
if (options.DecompilerSettings.ShowXmlDocumentation)
if (additionalTransform != null) {
additionalTransform.Run(astBuilder.CompilationUnit);
}
if (options.DecompilerSettings.ShowXmlDocumentation) {
AddXmlDocTransform.Run(astBuilder.CompilationUnit);
}
astBuilder.GenerateCode(output);
}
public override void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
{
if (options.FullDecompilation && options.SaveAsProjectDirectory != null) {
@ -150,7 +239,7 @@ namespace ICSharpCode.ILSpy @@ -150,7 +239,7 @@ namespace ICSharpCode.ILSpy
}
}
}
#region WriteProjectFile
void WriteProjectFile(TextWriter writer, IEnumerable<Tuple<string, string>> files, ModuleDefinition module)
{
@ -178,20 +267,20 @@ namespace ICSharpCode.ILSpy @@ -178,20 +267,20 @@ namespace ICSharpCode.ILSpy
w.WriteStartElement("Project", ns);
w.WriteAttributeString("ToolsVersion", "4.0");
w.WriteAttributeString("DefaultTargets", "Build");
w.WriteStartElement("PropertyGroup");
w.WriteElementString("ProjectGuid", Guid.NewGuid().ToString().ToUpperInvariant());
w.WriteStartElement("Configuration");
w.WriteAttributeString("Condition", " '$(Configuration)' == '' ");
w.WriteValue("Debug");
w.WriteEndElement(); // </Configuration>
w.WriteStartElement("Platform");
w.WriteAttributeString("Condition", " '$(Platform)' == '' ");
w.WriteValue(platformName);
w.WriteEndElement(); // </Platform>
switch (module.Kind) {
case ModuleKind.Windows:
w.WriteElementString("OutputType", "WinExe");
@ -203,7 +292,7 @@ namespace ICSharpCode.ILSpy @@ -203,7 +292,7 @@ namespace ICSharpCode.ILSpy
w.WriteElementString("OutputType", "Library");
break;
}
w.WriteElementString("AssemblyName", module.Assembly.Name.Name);
switch (module.Runtime) {
case TargetRuntime.Net_1_0:
@ -222,14 +311,14 @@ namespace ICSharpCode.ILSpy @@ -222,14 +311,14 @@ namespace ICSharpCode.ILSpy
break;
}
w.WriteElementString("WarningLevel", "4");
w.WriteEndElement(); // </PropertyGroup>
w.WriteStartElement("PropertyGroup"); // platform-specific
w.WriteAttributeString("Condition", " '$(Platform)' == '" + platformName + "' ");
w.WriteElementString("PlatformTarget", platformName);
w.WriteEndElement(); // </PropertyGroup> (platform-specific)
w.WriteStartElement("PropertyGroup"); // Debug
w.WriteAttributeString("Condition", " '$(Configuration)' == 'Debug' ");
w.WriteElementString("OutputPath", "bin\\Debug\\");
@ -237,7 +326,7 @@ namespace ICSharpCode.ILSpy @@ -237,7 +326,7 @@ namespace ICSharpCode.ILSpy
w.WriteElementString("DebugType", "full");
w.WriteElementString("Optimize", "false");
w.WriteEndElement(); // </PropertyGroup> (Debug)
w.WriteStartElement("PropertyGroup"); // Release
w.WriteAttributeString("Condition", " '$(Configuration)' == 'Release' ");
w.WriteElementString("OutputPath", "bin\\Release\\");
@ -245,8 +334,8 @@ namespace ICSharpCode.ILSpy @@ -245,8 +334,8 @@ namespace ICSharpCode.ILSpy
w.WriteElementString("DebugType", "pdbonly");
w.WriteElementString("Optimize", "true");
w.WriteEndElement(); // </PropertyGroup> (Release)
w.WriteStartElement("ItemGroup"); // References
foreach (AssemblyNameReference r in module.AssemblyReferences) {
if (r.Name != "mscorlib") {
@ -257,7 +346,7 @@ namespace ICSharpCode.ILSpy @@ -257,7 +346,7 @@ namespace ICSharpCode.ILSpy
}
}
w.WriteEndElement(); // </ItemGroup> (References)
foreach (IGrouping<string, string> gr in (from f in files group f.Item2 by f.Item1 into g orderby g.Key select g)) {
w.WriteStartElement("ItemGroup");
foreach (string file in gr.OrderBy(f => f, StringComparer.OrdinalIgnoreCase)) {
@ -267,16 +356,16 @@ namespace ICSharpCode.ILSpy @@ -267,16 +356,16 @@ namespace ICSharpCode.ILSpy
}
w.WriteEndElement();
}
w.WriteStartElement("Import");
w.WriteAttributeString("Project", "$(MSBuildToolsPath)\\Microsoft.CSharp.targets");
w.WriteEndElement();
w.WriteEndDocument();
}
}
#endregion
#region WriteCodeFilesInProject
bool IncludeTypeWhenDecompilingProject(TypeDefinition type, DecompilationOptions options)
{
@ -286,11 +375,11 @@ namespace ICSharpCode.ILSpy @@ -286,11 +375,11 @@ namespace ICSharpCode.ILSpy
return false;
return true;
}
IEnumerable<Tuple<string, string>> WriteCodeFilesInProject(AssemblyDefinition assembly, DecompilationOptions options, HashSet<string> directories)
{
var files = assembly.MainModule.Types.Where(t => IncludeTypeWhenDecompilingProject(t, options)).GroupBy(
delegate (TypeDefinition type) {
delegate(TypeDefinition type) {
string file = TextView.DecompilerTextView.CleanUpName(type.Name) + this.FileExtension;
if (string.IsNullOrEmpty(type.Namespace)) {
return file;
@ -305,7 +394,7 @@ namespace ICSharpCode.ILSpy @@ -305,7 +394,7 @@ namespace ICSharpCode.ILSpy
Parallel.ForEach(
files,
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
delegate (IGrouping<string, TypeDefinition> file) {
delegate(IGrouping<string, TypeDefinition> file) {
using (StreamWriter w = new StreamWriter(Path.Combine(options.SaveAsProjectDirectory, file.Key))) {
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentModule: assembly.MainModule);
foreach (TypeDefinition type in file) {
@ -319,7 +408,7 @@ namespace ICSharpCode.ILSpy @@ -319,7 +408,7 @@ namespace ICSharpCode.ILSpy
return files.Select(f => Tuple.Create("Compile", f.Key));
}
#endregion
#region WriteResourceFilesInProject
IEnumerable<Tuple<string, string>> WriteResourceFilesInProject(LoadedAssembly assembly, DecompilationOptions options, HashSet<string> directories)
{
@ -333,7 +422,8 @@ namespace ICSharpCode.ILSpy @@ -333,7 +422,8 @@ namespace ICSharpCode.ILSpy
IEnumerable<DictionaryEntry> rs = null;
try {
rs = new ResourceSet(s).Cast<DictionaryEntry>();
} catch (ArgumentException) {
}
catch (ArgumentException) {
}
if (rs != null && rs.All(e => e.Value is Stream)) {
foreach (var pair in rs) {
@ -351,7 +441,8 @@ namespace ICSharpCode.ILSpy @@ -351,7 +441,8 @@ namespace ICSharpCode.ILSpy
string xaml = null;
try {
xaml = decompiler.DecompileBaml(ms, assembly.FileName, new ConnectMethodDecompiler(assembly), new AssemblyResolver(assembly));
} catch (XamlXmlWriterException) {} // ignore XAML writer exceptions
}
catch (XamlXmlWriterException) { } // ignore XAML writer exceptions
if (xaml != null) {
File.WriteAllText(Path.Combine(options.SaveAsProjectDirectory, Path.ChangeExtension(fileName, ".xaml")), xaml);
yield return Tuple.Create("Page", Path.ChangeExtension(fileName, ".xaml"));
@ -372,12 +463,13 @@ namespace ICSharpCode.ILSpy @@ -372,12 +463,13 @@ namespace ICSharpCode.ILSpy
}
yield return Tuple.Create("EmbeddedResource", fileName);
}
} finally {
}
finally {
if (bamlDecompilerAppDomain != null)
AppDomain.Unload(bamlDecompilerAppDomain);
}
}
string GetFileNameForResource(string fullName, HashSet<string> directories)
{
string[] splitName = fullName.Split('.');
@ -393,7 +485,7 @@ namespace ICSharpCode.ILSpy @@ -393,7 +485,7 @@ namespace ICSharpCode.ILSpy
return fileName;
}
#endregion
AstBuilder CreateAstBuilder(DecompilationOptions options, ModuleDefinition currentModule = null, TypeDefinition currentType = null, bool isSingleMember = false)
{
if (currentModule == null)
@ -417,7 +509,7 @@ namespace ICSharpCode.ILSpy @@ -417,7 +509,7 @@ namespace ICSharpCode.ILSpy
if (includeNamespace)
options |= ConvertTypeOptions.IncludeNamespace;
AstType astType = AstBuilder.ConvertType(type, typeAttributes, options);
StringWriter w = new StringWriter();
if (type.IsByReference) {
ParameterDefinition pd = typeAttributes as ParameterDefinition;
@ -425,11 +517,11 @@ namespace ICSharpCode.ILSpy @@ -425,11 +517,11 @@ namespace ICSharpCode.ILSpy
w.Write("out ");
else
w.Write("ref ");
if (astType is ComposedType && ((ComposedType)astType).PointerRank > 0)
((ComposedType)astType).PointerRank--;
}
astType.AcceptVisitor(new OutputVisitor(w, new CSharpFormattingOptions()), null);
return w.ToString();
}
@ -464,12 +556,20 @@ namespace ICSharpCode.ILSpy @@ -464,12 +556,20 @@ namespace ICSharpCode.ILSpy
} else
return property.Name;
}
public override bool ShowMember(MemberReference member)
{
return showAllMembers || !AstBuilder.MemberIsHidden(member, new DecompilationOptions().DecompilerSettings);
}
public override MemberReference GetOriginalCodeLocation(MemberReference member)
{
if (showAllMembers || !DecompilerSettingsPanel.CurrentDecompilerSettings.AnonymousMethods)
return member;
else
return ICSharpCode.ILSpy.TreeNodes.Analyzer.Helpers.GetOriginalCodeLocation(member);
}
public override string GetTooltip(MemberReference member)
{
MethodDefinition md = member as MethodDefinition;
@ -490,12 +590,12 @@ namespace ICSharpCode.ILSpy @@ -490,12 +590,12 @@ namespace ICSharpCode.ILSpy
b.RunTransformations();
foreach (var attribute in b.CompilationUnit.Descendants.OfType<AttributeSection>())
attribute.Remove();
StringWriter w = new StringWriter();
b.GenerateCode(new PlainTextOutput(w));
return Regex.Replace(w.ToString(), @"\s+", " ").TrimEnd();
}
return base.GetTooltip(member);
}
}

2
ILSpy/Commands.cs

@ -122,7 +122,7 @@ namespace ICSharpCode.ILSpy @@ -122,7 +122,7 @@ namespace ICSharpCode.ILSpy
return output;
}
),
task => MainWindow.Instance.TextView.Show(task.Result));
task => MainWindow.Instance.TextView.ShowText(task.Result));
}
}
#endif

19
ILSpy/ConnectMethodDecompiler.cs

@ -1,5 +1,20 @@ @@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;

93
ILSpy/DisplaySettings.cs

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.ComponentModel;
using System.Windows.Media;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// Description of DisplaySettings.
/// </summary>
public class DisplaySettings : INotifyPropertyChanged
{
public DisplaySettings()
{
}
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null) {
PropertyChanged(this, e);
}
}
protected void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
#endregion
FontFamily selectedFont;
public FontFamily SelectedFont {
get { return selectedFont; }
set {
if (selectedFont != value) {
selectedFont = value;
OnPropertyChanged("SelectedFont");
}
}
}
double selectedFontSize;
public double SelectedFontSize {
get { return selectedFontSize; }
set {
if (selectedFontSize != value) {
selectedFontSize = value;
OnPropertyChanged("SelectedFontSize");
}
}
}
bool showLineNumbers;
public bool ShowLineNumbers {
get { return showLineNumbers; }
set {
if (showLineNumbers != value) {
showLineNumbers = value;
OnPropertyChanged("ShowLineNumbers");
}
}
}
public void CopyValues(DisplaySettings s)
{
this.SelectedFont = s.selectedFont;
this.SelectedFontSize = s.selectedFontSize;
this.ShowLineNumbers = s.showLineNumbers;
}
}
}

64
ILSpy/DisplaySettingsPanel.xaml

@ -0,0 +1,64 @@ @@ -0,0 +1,64 @@
<UserControl x:Class="ICSharpCode.ILSpy.DisplaySettingsPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ICSharpCode.ILSpy">
<UserControl.Resources>
<local:FontSizeConverter x:Key="fontSizeConv" />
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<GroupBox Header="Font">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Label Margin="3,0">Font:</Label>
<ComboBox x:Name="fontSelector" VerticalContentAlignment="Center" SelectedItem="{Binding SelectedFont}" Grid.Column="1">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Source}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Label Grid.Column="2" Margin="3,0">Size:</Label>
<ComboBox Grid.Column="3" Text="{Binding SelectedFontSize, Converter={StaticResource fontSizeConv}}" IsEditable="True" Margin="3,0">
<ComboBoxItem>6</ComboBoxItem>
<ComboBoxItem>7</ComboBoxItem>
<ComboBoxItem>8</ComboBoxItem>
<ComboBoxItem>9</ComboBoxItem>
<ComboBoxItem>10</ComboBoxItem>
<ComboBoxItem>11</ComboBoxItem>
<ComboBoxItem>12</ComboBoxItem>
<ComboBoxItem>13</ComboBoxItem>
<ComboBoxItem>14</ComboBoxItem>
<ComboBoxItem>15</ComboBoxItem>
<ComboBoxItem>16</ComboBoxItem>
<ComboBoxItem>17</ComboBoxItem>
<ComboBoxItem>18</ComboBoxItem>
<ComboBoxItem>19</ComboBoxItem>
<ComboBoxItem>20</ComboBoxItem>
<ComboBoxItem>21</ComboBoxItem>
<ComboBoxItem>22</ComboBoxItem>
<ComboBoxItem>23</ComboBoxItem>
<ComboBoxItem>24</ComboBoxItem>
</ComboBox>
<Border Grid.Row="1" Grid.ColumnSpan="4" BorderBrush="Black" BorderThickness="1" Background="White" Margin="3,5">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="AaBbCcXxYyZz" FontFamily="{Binding SelectedFont}" FontSize="{Binding SelectedFontSize}" />
</Border>
</Grid>
</GroupBox>
<GroupBox Header="Other options" Grid.Row="1">
<CheckBox Margin="3,3" IsChecked="{Binding ShowLineNumbers}">Show line numbers</CheckBox>
</GroupBox>
</Grid>
</UserControl>

154
ILSpy/DisplaySettingsPanel.xaml.cs

@ -0,0 +1,154 @@ @@ -0,0 +1,154 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using System.Xml.Linq;
using ICSharpCode.Decompiler;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// Interaction logic for DisplaySettingsPanel.xaml
/// </summary>
[ExportOptionPage("Display")]
public partial class DisplaySettingsPanel : UserControl, IOptionPage
{
public DisplaySettingsPanel()
{
InitializeComponent();
Task<FontFamily[]> task = new Task<FontFamily[]>(FontLoader);
task.Start();
task.ContinueWith(
delegate(Task continuation) {
App.Current.Dispatcher.Invoke(
DispatcherPriority.Normal,
(Action)(
() => {
fontSelector.ItemsSource = task.Result;
if (continuation.Exception != null) {
foreach (var ex in continuation.Exception.InnerExceptions) {
MessageBox.Show(ex.ToString());
}
}
})
);
}
);
}
public void Load(ILSpySettings settings)
{
this.DataContext = LoadDisplaySettings(settings);
}
static DisplaySettings currentDisplaySettings;
public static DisplaySettings CurrentDisplaySettings {
get {
return currentDisplaySettings ?? (currentDisplaySettings = LoadDisplaySettings(ILSpySettings.Load()));
}
}
static bool IsSymbolFont(FontFamily fontFamily)
{
foreach (var tf in fontFamily.GetTypefaces()) {
GlyphTypeface glyph;
try {
if (tf.TryGetGlyphTypeface(out glyph))
return glyph.Symbol;
} catch (Exception) {
return true;
}
}
return false;
}
static FontFamily[] FontLoader()
{
return Fonts.SystemFontFamilies
.Where(ff => !IsSymbolFont(ff))
.OrderBy(ff => ff.Source)
.ToArray();
}
public static DisplaySettings LoadDisplaySettings(ILSpySettings settings)
{
XElement e = settings["DisplaySettings"];
DisplaySettings s = new DisplaySettings();
s.SelectedFont = new FontFamily((string)e.Attribute("Font") ?? "Consolas");
s.SelectedFontSize = (double?)e.Attribute("FontSize") ?? 10.0 * 4 / 3;
s.ShowLineNumbers = (bool?)e.Attribute("ShowLineNumbers") ?? false;
return s;
}
public void Save(XElement root)
{
DisplaySettings s = (DisplaySettings)this.DataContext;
currentDisplaySettings.CopyValues(s);
XElement section = new XElement("DisplaySettings");
section.SetAttributeValue("Font", s.SelectedFont.Source);
section.SetAttributeValue("FontSize", s.SelectedFontSize);
section.SetAttributeValue("ShowLineNumbers", s.ShowLineNumbers);
XElement existingElement = root.Element("DisplaySettings");
if (existingElement != null)
existingElement.ReplaceWith(section);
else
root.Add(section);
}
}
public class FontSizeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is double) {
return Math.Round((double)value / 4 * 3);
}
throw new NotImplementedException();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is string) {
double d;
if (double.TryParse((string)value, out d))
return d * 4 / 3;
return 11 * 4 / 3;
}
throw new NotImplementedException();
}
}
}

4
ILSpy/ILAstLanguage.cs

@ -68,7 +68,7 @@ namespace ICSharpCode.ILSpy @@ -68,7 +68,7 @@ namespace ICSharpCode.ILSpy
output.Write(" : ");
if (v.IsPinned)
output.Write("pinned ");
v.Type.WriteTo(output, true, true);
v.Type.WriteTo(output, ILNameSyntax.ShortTypeName);
}
output.WriteLine();
}
@ -100,7 +100,7 @@ namespace ICSharpCode.ILSpy @@ -100,7 +100,7 @@ namespace ICSharpCode.ILSpy
public override string TypeToString(TypeReference t, bool includeNamespace, ICustomAttributeProvider attributeProvider)
{
PlainTextOutput output = new PlainTextOutput();
t.WriteTo(output, true, shortName: !includeNamespace);
t.WriteTo(output, includeNamespace ? ILNameSyntax.TypeName : ILNameSyntax.ShortTypeName);
return output.ToString();
}
}

44
ILSpy/ILLanguage.cs

@ -62,12 +62,38 @@ namespace ICSharpCode.ILSpy @@ -62,12 +62,38 @@ namespace ICSharpCode.ILSpy
public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options)
{
new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).DisassembleProperty(property);
ReflectionDisassembler rd = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken);
rd.DisassembleProperty(property);
if (property.GetMethod != null) {
output.WriteLine();
rd.DisassembleMethod(property.GetMethod);
}
if (property.SetMethod != null) {
output.WriteLine();
rd.DisassembleMethod(property.SetMethod);
}
foreach (var m in property.OtherMethods) {
output.WriteLine();
rd.DisassembleMethod(m);
}
}
public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options)
{
new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).DisassembleEvent(ev);
ReflectionDisassembler rd = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken);
rd.DisassembleEvent(ev);
if (ev.AddMethod != null) {
output.WriteLine();
rd.DisassembleMethod(ev.AddMethod);
}
if (ev.RemoveMethod != null) {
output.WriteLine();
rd.DisassembleMethod(ev.RemoveMethod);
}
foreach (var m in ev.OtherMethods) {
output.WriteLine();
rd.DisassembleMethod(m);
}
}
public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options)
@ -85,13 +111,23 @@ namespace ICSharpCode.ILSpy @@ -85,13 +111,23 @@ namespace ICSharpCode.ILSpy
output.WriteLine("// " + assembly.FileName);
output.WriteLine();
new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).WriteAssemblyHeader(assembly.AssemblyDefinition);
ReflectionDisassembler rd = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken);
if (options.FullDecompilation)
rd.WriteAssemblyReferences(assembly.AssemblyDefinition.MainModule);
rd.WriteAssemblyHeader(assembly.AssemblyDefinition);
output.WriteLine();
rd.WriteModuleHeader(assembly.AssemblyDefinition.MainModule);
if (options.FullDecompilation) {
output.WriteLine();
output.WriteLine();
rd.WriteModuleContents(assembly.AssemblyDefinition.MainModule);
}
}
public override string TypeToString(TypeReference t, bool includeNamespace, ICustomAttributeProvider attributeProvider)
{
PlainTextOutput output = new PlainTextOutput();
t.WriteTo(output, true, shortName: !includeNamespace);
t.WriteTo(output, includeNamespace ? ILNameSyntax.TypeName : ILNameSyntax.ShortTypeName);
return output.ToString();
}
}

13
ILSpy/ILSpy.csproj

@ -102,6 +102,7 @@ @@ -102,6 +102,7 @@
<DependentUpon>DecompilerSettingsPanel.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="DisplaySettings.cs" />
<Compile Include="ExportCommandAttribute.cs" />
<Compile Include="Controls\SearchBox.cs" />
<Compile Include="Controls\SortableGridViewColumn.cs" />
@ -138,22 +139,27 @@ @@ -138,22 +139,27 @@
<Compile Include="SearchPane.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="DisplaySettingsPanel.xaml.cs">
<DependentUpon>DisplaySettingsPanel.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="TreeNodes\Analyzer\AnalyzeContextMenuEntry.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedEventAccessorsTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedEventOverridesTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedEventTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedInterfacePropertyImplementedByTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedInterfaceMethodImplementedByTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedInterfaceEventImplementedByTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedPropertyAccessorTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedTypeExposedByTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedTypeExtensionMethodsTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedTypeInstantiationsTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedTypeTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedEventAccessorTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedVirtualMethodUsedByTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\Helpers.cs" />
<Compile Include="TreeNodes\Analyzer\ScopedWhereUsedAnalyzer.cs" />
<Compile Include="TreeNodes\IMemberTreeNode.cs" />
<Compile Include="TreeNodes\XamlResourceNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedPropertyAccessorsTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedPropertyOverridesTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedPropertyTreeNode.cs" />
<Compile Include="XmlDoc\AddXmlDocTransform.cs" />
@ -230,6 +236,7 @@ @@ -230,6 +236,7 @@
<Page Include="SearchPane.xaml">
<DependentUpon>SearchPane.cs</DependentUpon>
</Page>
<Page Include="DisplaySettingsPanel.xaml" />
<Page Include="TextView\DecompilerTextView.xaml">
<DependentUpon>DecompilerTextView.cs</DependentUpon>
</Page>
@ -237,6 +244,7 @@ @@ -237,6 +244,7 @@
</ItemGroup>
<ItemGroup>
<Resource Include="Images\Class.png" />
<Resource Include="Images\StaticClass.png" />
<Resource Include="Images\Delegate.png" />
<Resource Include="Images\Enum.png" />
<Resource Include="Images\Field.png" />
@ -304,6 +312,5 @@ @@ -304,6 +312,5 @@
<Name>ICSharpCode.TreeView</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
</Project>

21
ILSpy/ILSpySettings.cs

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Xml;
using System.Xml.Linq;
@ -58,7 +59,7 @@ namespace ICSharpCode.ILSpy @@ -58,7 +59,7 @@ namespace ICSharpCode.ILSpy
{
using (new MutexProtector(ConfigFileMutex)) {
try {
XDocument doc = XDocument.Load(GetConfigFile());
XDocument doc = LoadWithoutCheckingCharacters(GetConfigFile());
return new ILSpySettings(doc.Root);
} catch (IOException) {
return new ILSpySettings();
@ -68,6 +69,15 @@ namespace ICSharpCode.ILSpy @@ -68,6 +69,15 @@ namespace ICSharpCode.ILSpy
}
}
static XDocument LoadWithoutCheckingCharacters(string fileName)
{
// XDocument.Load(fileName) validates that no invalid characters appear (not even in escaped form),
// but we need those characters for some obfuscated assemblies.
using (XmlTextReader r = new XmlTextReader(fileName)) {
return XDocument.Load(r);
}
}
/// <summary>
/// Saves a setting section.
/// </summary>
@ -94,7 +104,7 @@ namespace ICSharpCode.ILSpy @@ -94,7 +104,7 @@ namespace ICSharpCode.ILSpy
string config = GetConfigFile();
XDocument doc;
try {
doc = XDocument.Load(config);
doc = LoadWithoutCheckingCharacters(config);
} catch (IOException) {
// ensure the directory exists
Directory.CreateDirectory(Path.GetDirectoryName(config));
@ -104,7 +114,12 @@ namespace ICSharpCode.ILSpy @@ -104,7 +114,12 @@ namespace ICSharpCode.ILSpy
}
doc.Root.SetAttributeValue("version", RevisionClass.Major + "." + RevisionClass.Minor + "." + RevisionClass.Build + "." + RevisionClass.Revision);
action(doc.Root);
doc.Save(config);
// We can't use XDocument.Save(filename) because that checks for invalid characters, but those can appear
// in obfuscated assemblies.
using (XmlTextWriter writer = new XmlTextWriter(config, Encoding.UTF8)) {
writer.Formatting = Formatting.Indented;
doc.Save(writer);
}
}
}

5
ILSpy/Images/Images.cs

@ -65,6 +65,7 @@ namespace ICSharpCode.ILSpy @@ -65,6 +65,7 @@ namespace ICSharpCode.ILSpy
public static readonly BitmapImage Interface = LoadBitmap("Interface");
public static readonly BitmapImage Delegate = LoadBitmap("Delegate");
public static readonly BitmapImage Enum = LoadBitmap("Enum");
public static readonly BitmapImage StaticClass = LoadBitmap("StaticClass");
public static readonly BitmapImage Field = LoadBitmap("Field");
@ -133,6 +134,7 @@ namespace ICSharpCode.ILSpy @@ -133,6 +134,7 @@ namespace ICSharpCode.ILSpy
PreloadPublicIconToCache(TypeIcon.Struct, Images.Struct);
PreloadPublicIconToCache(TypeIcon.Interface, Images.Interface);
PreloadPublicIconToCache(TypeIcon.Delegate, Images.Delegate);
PreloadPublicIconToCache(TypeIcon.StaticClass, Images.StaticClass);
}
protected override ImageSource GetBaseImage(TypeIcon icon)
@ -154,6 +156,9 @@ namespace ICSharpCode.ILSpy @@ -154,6 +156,9 @@ namespace ICSharpCode.ILSpy
case TypeIcon.Delegate:
baseImage = Images.Delegate;
break;
case TypeIcon.StaticClass:
baseImage = Images.StaticClass;
break;
default:
throw new NotSupportedException();
}

BIN
ILSpy/Images/StaticClass.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 B

3
ILSpy/Images/TypeIcon.cs

@ -26,6 +26,7 @@ namespace ICSharpCode.ILSpy @@ -26,6 +26,7 @@ namespace ICSharpCode.ILSpy
Enum,
Struct,
Interface,
Delegate
Delegate,
StaticClass
}
}

69
ILSpy/Language.cs

@ -35,66 +35,69 @@ namespace ICSharpCode.ILSpy @@ -35,66 +35,69 @@ namespace ICSharpCode.ILSpy
/// Gets the name of the language (as shown in the UI)
/// </summary>
public abstract string Name { get; }
/// <summary>
/// Gets the file extension used by source code files in this language.
/// </summary>
public abstract string FileExtension { get; }
public virtual string ProjectFileExtension {
public virtual string ProjectFileExtension
{
get { return null; }
}
/// <summary>
/// Gets the syntax highlighting used for this language.
/// </summary>
public virtual ICSharpCode.AvalonEdit.Highlighting.IHighlightingDefinition SyntaxHighlighting {
get {
public virtual ICSharpCode.AvalonEdit.Highlighting.IHighlightingDefinition SyntaxHighlighting
{
get
{
return ICSharpCode.AvalonEdit.Highlighting.HighlightingManager.Instance.GetDefinitionByExtension(this.FileExtension);
}
}
public virtual void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, TypeToString(method.DeclaringType, true) + "." + method.Name);
}
public virtual void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, TypeToString(property.DeclaringType, true) + "." + property.Name);
}
public virtual void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, TypeToString(field.DeclaringType, true) + "." + field.Name);
}
public virtual void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, TypeToString(ev.DeclaringType, true) + "." + ev.Name);
}
public virtual void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, TypeToString(type, true));
}
public virtual void DecompileNamespace(string nameSpace, IEnumerable<TypeDefinition> types, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, nameSpace);
}
public virtual void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, assembly.FileName);
WriteCommentLine(output, assembly.AssemblyDefinition.FullName);
}
public virtual void WriteCommentLine(ITextOutput output, string comment)
{
output.WriteLine("// " + comment);
}
/// <summary>
/// Converts a type reference into a string. This method is used by the member tree node for parameter and return types.
/// </summary>
@ -105,7 +108,7 @@ namespace ICSharpCode.ILSpy @@ -105,7 +108,7 @@ namespace ICSharpCode.ILSpy
else
return type.Name;
}
/// <summary>
/// Converts a member signature to a string.
/// This is used for displaying the tooltip on a member reference.
@ -117,14 +120,14 @@ namespace ICSharpCode.ILSpy @@ -117,14 +120,14 @@ namespace ICSharpCode.ILSpy
else
return member.ToString();
}
public virtual string FormatPropertyName(PropertyDefinition property, bool? isIndexer = null)
{
if (property == null)
throw new ArgumentNullException("property");
return property.Name;
}
/// <summary>
/// Used for WPF keyboard navigation.
/// </summary>
@ -132,39 +135,49 @@ namespace ICSharpCode.ILSpy @@ -132,39 +135,49 @@ namespace ICSharpCode.ILSpy
{
return Name;
}
public virtual bool ShowMember(MemberReference member)
{
return true;
}
/// <summary>
/// Used by the analyzer to map compiler generated code back to the original code's location
/// </summary>
public virtual MemberReference GetOriginalCodeLocation(MemberReference member)
{
return member;
}
}
public static class Languages
{
static ReadOnlyCollection<Language> allLanguages;
/// <summary>
/// A list of all languages.
/// </summary>
public static ReadOnlyCollection<Language> AllLanguages {
get {
public static ReadOnlyCollection<Language> AllLanguages
{
get
{
return allLanguages;
}
}
internal static void Initialize(CompositionContainer composition)
{
List<Language> languages = new List<Language>();
languages.AddRange(composition.GetExportedValues<Language>());
languages.Add(new ILLanguage(true));
#if DEBUG
#if DEBUG
languages.AddRange(ILAstLanguage.GetDebugLanguages());
languages.AddRange(CSharpLanguage.GetDebugLanguages());
#endif
#endif
allLanguages = languages.AsReadOnly();
}
/// <summary>
/// Gets a language using its name.
/// If the language is not found, C# is returned instead.

15
ILSpy/MainWindow.xaml.cs

@ -242,7 +242,7 @@ namespace ICSharpCode.ILSpy @@ -242,7 +242,7 @@ namespace ICSharpCode.ILSpy
if (!found) {
AvalonEditTextOutput output = new AvalonEditTextOutput();
output.Write("Cannot find " + args.NavigateTo);
decompilerTextView.Show(output);
decompilerTextView.ShowText(output);
}
}
commandLineLoadedAssemblies.Clear(); // clear references once we don't need them anymore
@ -511,8 +511,12 @@ namespace ICSharpCode.ILSpy @@ -511,8 +511,12 @@ namespace ICSharpCode.ILSpy
if (ignoreDecompilationRequests)
return;
if (recordHistory)
history.Record(new NavigationState(treeView.SelectedItems.OfType<SharpTreeNode>(), decompilerTextView.GetState()));
if (recordHistory) {
var dtState = decompilerTextView.GetState();
if(dtState != null)
history.UpdateCurrent(new NavigationState(dtState));
history.Record(new NavigationState(treeView.SelectedItems.OfType<SharpTreeNode>()));
}
if (treeView.SelectedItems.Count == 1) {
ILSpyTreeNode node = treeView.SelectedItem as ILSpyTreeNode;
@ -586,8 +590,9 @@ namespace ICSharpCode.ILSpy @@ -586,8 +590,9 @@ namespace ICSharpCode.ILSpy
void NavigateHistory(bool forward)
{
var combinedState = new NavigationState(treeView.SelectedItems.OfType<SharpTreeNode>(), decompilerTextView.GetState());
history.Record(combinedState, replace: true, clearForward: false);
var dtState = decompilerTextView.GetState();
if(dtState != null)
history.UpdateCurrent(new NavigationState(dtState));
var newState = forward ? history.GoForward() : history.GoBack();
ignoreDecompilationRequests = true;

12
ILSpy/NavigationHistory.cs

@ -71,13 +71,18 @@ namespace ICSharpCode.ILSpy @@ -71,13 +71,18 @@ namespace ICSharpCode.ILSpy
back.Clear();
forward.Clear();
}
public void UpdateCurrent(T node)
{
current = node;
}
public void Record(T node, bool replace = false, bool clearForward = true)
public void Record(T node)
{
var navigationTime = DateTime.Now;
var period = navigationTime - lastNavigationTime;
if (period.TotalSeconds < NavigationSecondsBeforeNewEntry || replace) {
if (period.TotalSeconds < NavigationSecondsBeforeNewEntry) {
current = node;
} else {
if (current != null)
@ -88,8 +93,7 @@ namespace ICSharpCode.ILSpy @@ -88,8 +93,7 @@ namespace ICSharpCode.ILSpy
current = node;
}
if (clearForward)
forward.Clear();
forward.Clear();
lastNavigationTime = navigationTime;
}

10
ILSpy/NavigationState.cs

@ -32,12 +32,18 @@ namespace ICSharpCode.ILSpy @@ -32,12 +32,18 @@ namespace ICSharpCode.ILSpy
public IEnumerable<SharpTreeNode> TreeNodes { get { return treeNodes; } }
public DecompilerTextViewState ViewState { get; private set; }
public NavigationState(IEnumerable<SharpTreeNode> treeNodes, DecompilerTextViewState viewState)
public NavigationState(DecompilerTextViewState viewState)
{
this.treeNodes = new HashSet<SharpTreeNode>(treeNodes);
this.treeNodes = new HashSet<SharpTreeNode>(viewState.DecompiledNodes);
ViewState = viewState;
}
public NavigationState(IEnumerable<SharpTreeNode> treeNodes)
{
this.treeNodes = new HashSet<SharpTreeNode>(treeNodes);
}
public bool Equals(NavigationState other)
{
// TODO: should this care about the view state as well?

29
ILSpy/TextView/DecompilerTextView.cs

@ -29,6 +29,7 @@ using System.Threading; @@ -29,6 +29,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
@ -60,6 +61,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -60,6 +61,7 @@ namespace ICSharpCode.ILSpy.TextView
readonly UIElementGenerator uiElementGenerator;
List<VisualLineElementGenerator> activeCustomElementGenerators = new List<VisualLineElementGenerator>();
FoldingManager foldingManager;
ILSpyTreeNode[] decompiledNodes;
DefinitionLookup definitionLookup;
CancellationTokenSource currentCancellationTokenSource;
@ -85,6 +87,9 @@ namespace ICSharpCode.ILSpy.TextView @@ -85,6 +87,9 @@ namespace ICSharpCode.ILSpy.TextView
textEditor.Options.RequireControlModifierForHyperlinkClick = false;
textEditor.TextArea.TextView.MouseHover += TextViewMouseHover;
textEditor.TextArea.TextView.MouseHoverStopped += TextViewMouseHoverStopped;
textEditor.SetBinding(TextEditor.FontFamilyProperty, new Binding { Source = DisplaySettingsPanel.CurrentDisplaySettings, Path = new PropertyPath("SelectedFont") });
textEditor.SetBinding(TextEditor.FontSizeProperty, new Binding { Source = DisplaySettingsPanel.CurrentDisplaySettings, Path = new PropertyPath("SelectedFontSize") });
textEditor.SetBinding(TextEditor.ShowLineNumbersProperty, new Binding { Source = DisplaySettingsPanel.CurrentDisplaySettings, Path = new PropertyPath("ShowLineNumbers") });
}
#endregion
@ -207,11 +212,21 @@ namespace ICSharpCode.ILSpy.TextView @@ -207,11 +212,21 @@ namespace ICSharpCode.ILSpy.TextView
#endregion
#region ShowOutput
public void ShowText(AvalonEditTextOutput textOutput)
{
ShowNodes(textOutput, null);
}
public void ShowNode(AvalonEditTextOutput textOutput, ILSpyTreeNode node, IHighlightingDefinition highlighting = null)
{
ShowNodes(textOutput, new[] { node }, highlighting);
}
/// <summary>
/// Shows the given output in the text view.
/// Cancels any currently running decompilation tasks.
/// </summary>
public void Show(AvalonEditTextOutput textOutput, IHighlightingDefinition highlighting = null)
public void ShowNodes(AvalonEditTextOutput textOutput, ILSpyTreeNode[] nodes, IHighlightingDefinition highlighting = null)
{
// Cancel the decompilation task:
if (currentCancellationTokenSource != null) {
@ -220,6 +235,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -220,6 +235,7 @@ namespace ICSharpCode.ILSpy.TextView
}
this.nextDecompilationRun = null; // remove scheduled decompilation run
ShowOutput(textOutput, highlighting);
decompiledNodes = nodes;
}
/// <summary>
@ -340,6 +356,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -340,6 +356,7 @@ namespace ICSharpCode.ILSpy.TextView
}
ShowOutput(output);
}
decompiledNodes = context.TreeNodes;
});
}
@ -513,6 +530,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -513,6 +530,7 @@ namespace ICSharpCode.ILSpy.TextView
output.WriteLine(ex.ToString());
ShowOutput(output);
}
decompiledNodes = context.TreeNodes;
});
}
@ -575,11 +593,15 @@ namespace ICSharpCode.ILSpy.TextView @@ -575,11 +593,15 @@ namespace ICSharpCode.ILSpy.TextView
public DecompilerTextViewState GetState()
{
if (decompiledNodes == null)
return null;
var state = new DecompilerTextViewState();
if (foldingManager != null)
state.SaveFoldingsState(foldingManager.AllFoldings);
state.VerticalOffset = textEditor.VerticalOffset;
state.HorizontalOffset = textEditor.HorizontalOffset;
state.DecompiledNodes = decompiledNodes;
return state;
}
}
@ -590,16 +612,17 @@ namespace ICSharpCode.ILSpy.TextView @@ -590,16 +612,17 @@ namespace ICSharpCode.ILSpy.TextView
private int FoldingsChecksum;
public double VerticalOffset;
public double HorizontalOffset;
public ILSpyTreeNode[] DecompiledNodes;
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);
FoldingsChecksum = unchecked(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);
var checksum = unchecked(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);

1
ILSpy/TextView/ILAsm-Mode.xshd

@ -348,6 +348,7 @@ @@ -348,6 +348,7 @@
<Word>tls</Word>
<Word>true</Word>
<Word>false</Word>
<Word>strict</Word>
</Keywords>
<Keywords color="Directives">
<Word>.class</Word>

43
ILSpy/TreeNodes/Analyzer/AnalyzedEventAccessorsTreeNode.cs → ILSpy/TreeNodes/Analyzer/AnalyzedEventAccessorTreeNode.cs

@ -21,50 +21,19 @@ using Mono.Cecil; @@ -21,50 +21,19 @@ using Mono.Cecil;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
internal sealed class AnalyzedEventAccessorsTreeNode : AnalyzerTreeNode
internal class AnalyzedEventAccessorTreeNode : AnalyzedMethodTreeNode
{
public AnalyzedEventAccessorsTreeNode(EventDefinition analyzedEvent)
{
if (analyzedEvent == null)
throw new ArgumentNullException("analyzedEvent");
if (analyzedEvent.AddMethod != null)
this.Children.Add(new AnalyzedEventAccessorTreeNode(analyzedEvent.AddMethod, "add"));
if (analyzedEvent.RemoveMethod != null)
this.Children.Add(new AnalyzedEventAccessorTreeNode(analyzedEvent.RemoveMethod, "remove"));
foreach (var accessor in analyzedEvent.OtherMethods)
this.Children.Add(new AnalyzedEventAccessorTreeNode(accessor, null));
}
private string name;
public override object Icon
public AnalyzedEventAccessorTreeNode(MethodDefinition analyzedMethod, string name)
: base(analyzedMethod)
{
get { return Images.Search; }
this.name = name;
}
public override object Text
{
get { return "Accessors"; }
}
public static bool CanShow(EventDefinition property)
{
return !MainWindow.Instance.CurrentLanguage.ShowMember(property.AddMethod ?? property.RemoveMethod);
}
internal class AnalyzedEventAccessorTreeNode : AnalyzedMethodTreeNode
{
private string name;
public AnalyzedEventAccessorTreeNode(MethodDefinition analyzedMethod, string name)
: base(analyzedMethod)
{
this.name = name;
}
public override object Text
{
get { return name ?? base.Text; }
}
get { return name ?? base.Text; }
}
}
}

33
ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs

@ -68,36 +68,25 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -68,36 +68,25 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
private IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct)
{
return FindReferences(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), ct);
}
private IEnumerable<SharpTreeNode> FindReferences(IEnumerable<LoadedAssembly> assemblies, CancellationToken ct)
{
assemblies = assemblies.Where(asm => asm.AssemblyDefinition != null);
ScopedWhereUsedAnalyzer<SharpTreeNode> analyzer;
// use parallelism only on the assembly level (avoid locks within Cecil)
return assemblies.AsParallel().WithCancellation(ct).SelectMany((LoadedAssembly asm) => FindReferences(asm, ct));
analyzer = new ScopedWhereUsedAnalyzer<SharpTreeNode>(analyzedEvent, FindReferencesInType);
return analyzer.PerformAnalysis(ct);
}
private IEnumerable<SharpTreeNode> FindReferences(LoadedAssembly asm, CancellationToken ct)
private IEnumerable<SharpTreeNode> FindReferencesInType(TypeDefinition type)
{
string asmName = asm.AssemblyDefinition.Name.Name;
string name = analyzedEvent.Name;
string declTypeName = analyzedEvent.DeclaringType.FullName;
foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes)) {
ct.ThrowIfCancellationRequested();
if (!TypesHierarchyHelpers.IsBaseType(analyzedEvent.DeclaringType, type, resolveTypeArguments: false))
continue;
foreach (EventDefinition eventDef in type.Events) {
ct.ThrowIfCancellationRequested();
if (!TypesHierarchyHelpers.IsBaseType(analyzedEvent.DeclaringType, type, resolveTypeArguments: false))
yield break;
if (TypesHierarchyHelpers.IsBaseEvent(analyzedEvent, eventDef)) {
MethodDefinition anyAccessor = eventDef.AddMethod ?? eventDef.RemoveMethod;
bool hidesParent = !anyAccessor.IsVirtual ^ anyAccessor.IsNewSlot;
yield return new AnalyzedEventTreeNode(eventDef, hidesParent ? "(hides) " : "");
}
foreach (EventDefinition eventDef in type.Events) {
if (TypesHierarchyHelpers.IsBaseEvent(analyzedEvent, eventDef)) {
MethodDefinition anyAccessor = eventDef.AddMethod ?? eventDef.RemoveMethod;
bool hidesParent = !anyAccessor.IsVirtual ^ anyAccessor.IsNewSlot;
yield return new AnalyzedEventTreeNode(eventDef, hidesParent ? "(hides) " : "");
}
}
}

17
ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs

@ -57,8 +57,13 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -57,8 +57,13 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
protected override void LoadChildren()
{
if (AnalyzedEventAccessorsTreeNode.CanShow(analyzedEvent))
this.Children.Add(new AnalyzedEventAccessorsTreeNode(analyzedEvent));
if (analyzedEvent.AddMethod != null)
this.Children.Add(new AnalyzedEventAccessorTreeNode(analyzedEvent.AddMethod, "add"));
if (analyzedEvent.RemoveMethod != null)
this.Children.Add(new AnalyzedEventAccessorTreeNode(analyzedEvent.RemoveMethod, "remove"));
foreach (var accessor in analyzedEvent.OtherMethods)
this.Children.Add(new AnalyzedEventAccessorTreeNode(accessor, null));
if (AnalyzedEventOverridesTreeNode.CanShow(analyzedEvent))
this.Children.Add(new AnalyzedEventOverridesTreeNode(analyzedEvent));
if (AnalyzedInterfaceEventImplementedByTreeNode.CanShow(analyzedEvent))
@ -75,12 +80,12 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -75,12 +80,12 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
public static bool CanShow(MemberReference member)
{
var property = member as EventDefinition;
if (property == null)
var eventDef = member as EventDefinition;
if (eventDef == null)
return false;
return AnalyzedEventAccessorsTreeNode.CanShow(property)
|| AnalyzedEventOverridesTreeNode.CanShow(property);
return !MainWindow.Instance.CurrentLanguage.ShowMember(eventDef.AddMethod ?? eventDef.RemoveMethod)
|| AnalyzedEventOverridesTreeNode.CanShow(eventDef);
}
}
}

34
ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessTreeNode.cs

@ -22,6 +22,7 @@ using System.Threading; @@ -22,6 +22,7 @@ using System.Threading;
using ICSharpCode.TreeView;
using Mono.Cecil;
using Mono.Cecil.Cil;
using System.Collections;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
@ -30,6 +31,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -30,6 +31,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
private readonly bool showWrites; // true: show writes; false: show read access
private readonly FieldDefinition analyzedField;
private readonly ThreadingSupport threading;
private Lazy<Hashtable> foundMethods;
private object hashLock = new object();
public AnalyzedFieldAccessTreeNode(FieldDefinition analyzedField, bool showWrites)
{
@ -68,8 +71,14 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -68,8 +71,14 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
private IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct)
{
var analyzer = new ScopedWhereUsedScopeAnalyzer<SharpTreeNode>(analyzedField, FindReferencesInType);
return analyzer.PerformAnalysis(ct);
foundMethods = new Lazy<Hashtable>(LazyThreadSafetyMode.ExecutionAndPublication);
var analyzer = new ScopedWhereUsedAnalyzer<SharpTreeNode>(analyzedField, FindReferencesInType);
foreach (var child in analyzer.PerformAnalysis(ct)) {
yield return child;
}
foundMethods = null;
}
private IEnumerable<SharpTreeNode> FindReferencesInType(TypeDefinition type)
@ -95,8 +104,12 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -95,8 +104,12 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
method.Body = null;
if (found)
yield return new AnalyzedMethodTreeNode(method);
if (found) {
MethodDefinition codeLocation = this.Language.GetOriginalCodeLocation(method) as MethodDefinition;
if (codeLocation != null && !HasAlreadyBeenFound(codeLocation)) {
yield return new AnalyzedMethodTreeNode(codeLocation);
}
}
}
}
@ -116,5 +129,18 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -116,5 +129,18 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
return false;
}
}
private bool HasAlreadyBeenFound(MethodDefinition method)
{
Hashtable hashtable = foundMethods.Value;
lock (hashLock) {
if (hashtable.Contains(method)) {
return true;
} else {
hashtable.Add(method, null);
return false;
}
}
}
}
}

4
ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceEventImplementedByTreeNode.cs

@ -69,8 +69,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -69,8 +69,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
private IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct)
{
ScopedWhereUsedScopeAnalyzer<SharpTreeNode> analyzer;
analyzer = new ScopedWhereUsedScopeAnalyzer<SharpTreeNode>(analyzedMethod, FindReferencesInType);
ScopedWhereUsedAnalyzer<SharpTreeNode> analyzer;
analyzer = new ScopedWhereUsedAnalyzer<SharpTreeNode>(analyzedMethod, FindReferencesInType);
return analyzer.PerformAnalysis(ct);
}

4
ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceMethodImplementedByTreeNode.cs

@ -67,8 +67,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -67,8 +67,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
private IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct)
{
ScopedWhereUsedScopeAnalyzer<SharpTreeNode> analyzer;
analyzer = new ScopedWhereUsedScopeAnalyzer<SharpTreeNode>(analyzedMethod, FindReferencesInType);
ScopedWhereUsedAnalyzer<SharpTreeNode> analyzer;
analyzer = new ScopedWhereUsedAnalyzer<SharpTreeNode>(analyzedMethod, FindReferencesInType);
return analyzer.PerformAnalysis(ct);
}

4
ILSpy/TreeNodes/Analyzer/AnalyzedInterfacePropertyImplementedByTreeNode.cs

@ -69,8 +69,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -69,8 +69,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
private IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct)
{
ScopedWhereUsedScopeAnalyzer<SharpTreeNode> analyzer;
analyzer = new ScopedWhereUsedScopeAnalyzer<SharpTreeNode>(analyzedMethod, FindReferencesInType);
ScopedWhereUsedAnalyzer<SharpTreeNode> analyzer;
analyzer = new ScopedWhereUsedAnalyzer<SharpTreeNode>(analyzedMethod, FindReferencesInType);
return analyzer.PerformAnalysis(ct);
}

51
ILSpy/TreeNodes/Analyzer/AnalyzedMethodOverridesTreeNode.cs

@ -73,45 +73,32 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -73,45 +73,32 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
private IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct)
{
return FindReferences(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), ct);
}
private IEnumerable<SharpTreeNode> FindReferences(IEnumerable<LoadedAssembly> assemblies, CancellationToken ct)
{
assemblies = assemblies.Where(asm => asm.AssemblyDefinition != null);
ScopedWhereUsedAnalyzer<SharpTreeNode> analyzer;
// use parallelism only on the assembly level (avoid locks within Cecil)
return assemblies.AsParallel().WithCancellation(ct).SelectMany((LoadedAssembly asm) => FindReferences(asm, ct));
analyzer = new ScopedWhereUsedAnalyzer<SharpTreeNode>(analyzedMethod, FindReferencesInType);
return analyzer.PerformAnalysis(ct);
}
private IEnumerable<SharpTreeNode> FindReferences(LoadedAssembly asm, CancellationToken ct)
private IEnumerable<SharpTreeNode> FindReferencesInType(TypeDefinition type)
{
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();
SharpTreeNode newNode = null;
try {
if (!TypesHierarchyHelpers.IsBaseType(analyzedMethod.DeclaringType, type, resolveTypeArguments: false))
continue;
foreach (MethodDefinition method in type.Methods) {
ct.ThrowIfCancellationRequested();
if (TypesHierarchyHelpers.IsBaseMethod(analyzedMethod, method)) {
bool hidesParent = !method.IsVirtual ^ method.IsNewSlot;
newNode = new AnalyzedMethodTreeNode(method, hidesParent ? "(hides) " : "");
}
SharpTreeNode newNode = null;
try {
if (!TypesHierarchyHelpers.IsBaseType(analyzedMethod.DeclaringType, type, resolveTypeArguments: false))
yield break;
foreach (MethodDefinition method in type.Methods) {
if (TypesHierarchyHelpers.IsBaseMethod(analyzedMethod, method)) {
bool hidesParent = !method.IsVirtual ^ method.IsNewSlot;
newNode = new AnalyzedMethodTreeNode(method, hidesParent ? "(hides) " : "");
}
}
catch (ReferenceResolvingException) {
// ignore this type definition. maybe add a notification about such cases.
}
if (newNode != null)
yield return newNode;
}
catch (ReferenceResolvingException) {
// ignore this type definition. maybe add a notification about such cases.
}
if (newNode != null)
yield return newNode;
}
public static bool CanShow(MethodDefinition method)

8
ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs

@ -58,9 +58,15 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -58,9 +58,15 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
if (analyzedMethod.HasBody)
this.Children.Add(new AnalyzedMethodUsesTreeNode(analyzedMethod));
this.Children.Add(new AnalyzedMethodUsedByTreeNode(analyzedMethod));
if (analyzedMethod.IsVirtual && !(analyzedMethod.IsNewSlot && analyzedMethod.IsFinal))
this.Children.Add(new AnalyzedVirtualMethodUsedByTreeNode(analyzedMethod));
else
this.Children.Add(new AnalyzedMethodUsedByTreeNode(analyzedMethod));
if (AnalyzedMethodOverridesTreeNode.CanShow(analyzedMethod))
this.Children.Add(new AnalyzedMethodOverridesTreeNode(analyzedMethod));
if (AnalyzedInterfaceMethodImplementedByTreeNode.CanShow(analyzedMethod))
this.Children.Add(new AnalyzedInterfaceMethodImplementedByTreeNode(analyzedMethod));
}

28
ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using ICSharpCode.TreeView;
@ -29,6 +30,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -29,6 +30,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
private readonly MethodDefinition analyzedMethod;
private readonly ThreadingSupport threading;
private ConcurrentDictionary<MethodDefinition, int> foundMethods;
public AnalyzedMethodUsedByTreeNode(MethodDefinition analyzedMethod)
{
@ -66,10 +68,14 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -66,10 +68,14 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
private IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct)
{
ScopedWhereUsedScopeAnalyzer<SharpTreeNode> analyzer;
foundMethods = new ConcurrentDictionary<MethodDefinition, int>();
analyzer = new ScopedWhereUsedScopeAnalyzer<SharpTreeNode>(analyzedMethod, FindReferencesInType);
return analyzer.PerformAnalysis(ct);
var analyzer = new ScopedWhereUsedAnalyzer<SharpTreeNode>(analyzedMethod, FindReferencesInType);
foreach (var child in analyzer.PerformAnalysis(ct)) {
yield return child;
}
foundMethods = null;
}
private IEnumerable<SharpTreeNode> FindReferencesInType(TypeDefinition type)
@ -81,8 +87,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -81,8 +87,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
continue;
foreach (Instruction instr in method.Body.Instructions) {
MethodReference mr = instr.Operand as MethodReference;
if (mr != null &&
mr.Name == name &&
if (mr != null && mr.Name == name &&
Helpers.IsReferencedBy(analyzedMethod.DeclaringType, mr.DeclaringType) &&
mr.Resolve() == analyzedMethod) {
found = true;
@ -92,9 +97,18 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -92,9 +97,18 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
method.Body = null;
if (found)
yield return new AnalyzedMethodTreeNode(method);
if (found) {
MethodDefinition codeLocation = this.Language.GetOriginalCodeLocation(method) as MethodDefinition;
if (codeLocation != null && !HasAlreadyBeenFound(codeLocation)) {
yield return new AnalyzedMethodTreeNode(codeLocation);
}
}
}
}
private bool HasAlreadyBeenFound(MethodDefinition method)
{
return !foundMethods.TryAdd(method, 0);
}
}
}

43
ILSpy/TreeNodes/Analyzer/AnalyzedPropertyAccessorsTreeNode.cs → ILSpy/TreeNodes/Analyzer/AnalyzedPropertyAccessorTreeNode.cs

@ -21,50 +21,19 @@ using Mono.Cecil; @@ -21,50 +21,19 @@ using Mono.Cecil;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
internal sealed class AnalyzedPropertyAccessorsTreeNode : AnalyzerTreeNode
internal class AnalyzedPropertyAccessorTreeNode : AnalyzedMethodTreeNode
{
public AnalyzedPropertyAccessorsTreeNode(PropertyDefinition analyzedProperty)
{
if (analyzedProperty == null)
throw new ArgumentNullException("analyzedProperty");
if (analyzedProperty.GetMethod != null)
this.Children.Add(new AnalyzedPropertyAccessorTreeNode(analyzedProperty.GetMethod, "get"));
if (analyzedProperty.SetMethod != null)
this.Children.Add(new AnalyzedPropertyAccessorTreeNode(analyzedProperty.SetMethod, "set"));
foreach (var accessor in analyzedProperty.OtherMethods)
this.Children.Add(new AnalyzedPropertyAccessorTreeNode(accessor, null));
}
private readonly string name;
public override object Icon
public AnalyzedPropertyAccessorTreeNode(MethodDefinition analyzedMethod, string name)
: base(analyzedMethod)
{
get { return Images.Search; }
this.name = name;
}
public override object Text
{
get { return "Accessors"; }
}
public static bool CanShow(PropertyDefinition property)
{
return !MainWindow.Instance.CurrentLanguage.ShowMember(property.GetMethod ?? property.SetMethod);
}
private class AnalyzedPropertyAccessorTreeNode : AnalyzedMethodTreeNode
{
private readonly string name;
public AnalyzedPropertyAccessorTreeNode(MethodDefinition analyzedMethod, string name)
: base(analyzedMethod)
{
this.name = name;
}
public override object Text
{
get { return name ?? base.Text; }
}
get { return name ?? base.Text; }
}
}
}

40
ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs

@ -69,45 +69,27 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -69,45 +69,27 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
private IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct)
{
return FindReferences(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), ct);
}
private IEnumerable<SharpTreeNode> FindReferences(IEnumerable<LoadedAssembly> assemblies, CancellationToken ct)
{
assemblies = assemblies.Where(asm => asm.AssemblyDefinition != null);
ScopedWhereUsedAnalyzer<SharpTreeNode> analyzer;
// use parallelism only on the assembly level (avoid locks within Cecil)
return assemblies.AsParallel().WithCancellation(ct).SelectMany((LoadedAssembly asm) => FindReferences(asm, ct));
analyzer = new ScopedWhereUsedAnalyzer<SharpTreeNode>(analyzedProperty, FindReferencesInType);
return analyzer.PerformAnalysis(ct);
}
private IEnumerable<SharpTreeNode> FindReferences(LoadedAssembly asm, CancellationToken ct)
private IEnumerable<SharpTreeNode> FindReferencesInType(TypeDefinition type)
{
string asmName = asm.AssemblyDefinition.Name.Name;
string name = analyzedProperty.Name;
string declTypeName = analyzedProperty.DeclaringType.FullName;
foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes)) {
ct.ThrowIfCancellationRequested();
SharpTreeNode newNode = null;
try {
if (!TypesHierarchyHelpers.IsBaseType(analyzedProperty.DeclaringType, type, resolveTypeArguments: false))
continue;
if (!TypesHierarchyHelpers.IsBaseType(analyzedProperty.DeclaringType, type, resolveTypeArguments: false))
yield break;
foreach (PropertyDefinition property in type.Properties) {
ct.ThrowIfCancellationRequested();
foreach (PropertyDefinition property in type.Properties) {
if (TypesHierarchyHelpers.IsBaseProperty(analyzedProperty, property)) {
MethodDefinition anyAccessor = property.GetMethod ?? property.SetMethod;
bool hidesParent = !anyAccessor.IsVirtual ^ anyAccessor.IsNewSlot;
newNode = new AnalyzedPropertyTreeNode(property, hidesParent ? "(hides) " : "");
}
}
}
catch (ReferenceResolvingException) {
// ignore this type definition.
if (TypesHierarchyHelpers.IsBaseProperty(analyzedProperty, property)) {
MethodDefinition anyAccessor = property.GetMethod ?? property.SetMethod;
bool hidesParent = !anyAccessor.IsVirtual ^ anyAccessor.IsNewSlot;
yield return new AnalyzedPropertyTreeNode(property, hidesParent ? "(hides) " : "");
}
if (newNode != null)
yield return newNode;
}
}

13
ILSpy/TreeNodes/Analyzer/AnalyzedPropertyTreeNode.cs

@ -60,8 +60,13 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -60,8 +60,13 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
protected override void LoadChildren()
{
if (AnalyzedPropertyAccessorsTreeNode.CanShow(analyzedProperty))
this.Children.Add(new AnalyzedPropertyAccessorsTreeNode(analyzedProperty));
if (analyzedProperty.GetMethod != null)
this.Children.Add(new AnalyzedPropertyAccessorTreeNode(analyzedProperty.GetMethod, "get"));
if (analyzedProperty.SetMethod != null)
this.Children.Add(new AnalyzedPropertyAccessorTreeNode(analyzedProperty.SetMethod, "set"));
foreach (var accessor in analyzedProperty.OtherMethods)
this.Children.Add(new AnalyzedPropertyAccessorTreeNode(accessor, null));
if (AnalyzedPropertyOverridesTreeNode.CanShow(analyzedProperty))
this.Children.Add(new AnalyzedPropertyOverridesTreeNode(analyzedProperty));
if (AnalyzedInterfacePropertyImplementedByTreeNode.CanShow(analyzedProperty))
@ -82,8 +87,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -82,8 +87,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
if (property == null)
return false;
return AnalyzedPropertyAccessorsTreeNode.CanShow(property)
|| AnalyzedPropertyOverridesTreeNode.CanShow(property);
return !MainWindow.Instance.CurrentLanguage.ShowMember(property.GetMethod ?? property.SetMethod)
|| AnalyzedPropertyOverridesTreeNode.CanShow(property);
}
}
}

4
ILSpy/TreeNodes/Analyzer/AnalyzedTypeExposedByTreeNode.cs

@ -65,9 +65,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -65,9 +65,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
private IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct)
{
ScopedWhereUsedScopeAnalyzer<SharpTreeNode> analyzer;
ScopedWhereUsedAnalyzer<SharpTreeNode> analyzer;
analyzer = new ScopedWhereUsedScopeAnalyzer<SharpTreeNode>(analyzedType, FindReferencesInType);
analyzer = new ScopedWhereUsedAnalyzer<SharpTreeNode>(analyzedType, FindReferencesInType);
return analyzer.PerformAnalysis(ct);
}

4
ILSpy/TreeNodes/Analyzer/AnalyzedTypeExtensionMethodsTreeNode.cs

@ -66,9 +66,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -66,9 +66,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
private IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct)
{
ScopedWhereUsedScopeAnalyzer<SharpTreeNode> analyzer;
ScopedWhereUsedAnalyzer<SharpTreeNode> analyzer;
analyzer = new ScopedWhereUsedScopeAnalyzer<SharpTreeNode>(analyzedType, FindReferencesInType);
analyzer = new ScopedWhereUsedAnalyzer<SharpTreeNode>(analyzedType, FindReferencesInType);
return analyzer.PerformAnalysis(ct);
}

4
ILSpy/TreeNodes/Analyzer/AnalyzedTypeInstantiationsTreeNode.cs

@ -71,9 +71,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -71,9 +71,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
private IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct)
{
ScopedWhereUsedScopeAnalyzer<SharpTreeNode> analyzer;
ScopedWhereUsedAnalyzer<SharpTreeNode> analyzer;
analyzer = new ScopedWhereUsedScopeAnalyzer<SharpTreeNode>(analyzedType, FindReferencesInType);
analyzer = new ScopedWhereUsedAnalyzer<SharpTreeNode>(analyzedType, FindReferencesInType);
return analyzer.PerformAnalysis(ct);
}

151
ILSpy/TreeNodes/Analyzer/AnalyzedVirtualMethodUsedByTreeNode.cs

@ -0,0 +1,151 @@ @@ -0,0 +1,151 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using ICSharpCode.TreeView;
using Mono.Cecil;
using Mono.Cecil.Cil;
using ICSharpCode.Decompiler.Ast;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
internal sealed class AnalyzedVirtualMethodUsedByTreeNode : AnalyzerTreeNode
{
private readonly MethodDefinition analyzedMethod;
private readonly ThreadingSupport threading;
private ConcurrentDictionary<MethodDefinition, int> foundMethods;
private MethodDefinition baseMethod;
private List<TypeReference> possibleTypes;
public AnalyzedVirtualMethodUsedByTreeNode(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 "Used 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();
}
}
private IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct)
{
InitializeAnalyzer();
var analyzer = new ScopedWhereUsedAnalyzer<SharpTreeNode>(analyzedMethod, FindReferencesInType);
foreach (var child in analyzer.PerformAnalysis(ct)) {
yield return child;
}
ReleaseAnalyzer();
}
private void InitializeAnalyzer()
{
foundMethods = new ConcurrentDictionary<MethodDefinition, int>();
var BaseMethods = TypesHierarchyHelpers.FindBaseMethods(analyzedMethod).ToArray();
if (BaseMethods.Length > 0) {
baseMethod = BaseMethods[BaseMethods.Length - 1];
}
possibleTypes = new List<TypeReference>();
TypeReference type = analyzedMethod.DeclaringType.BaseType;
while (type !=null)
{
possibleTypes.Add(type);
type = type.Resolve().BaseType;
}
}
private void ReleaseAnalyzer()
{
foundMethods = null;
baseMethod = null;
}
private IEnumerable<SharpTreeNode> FindReferencesInType(TypeDefinition type)
{
string name = analyzedMethod.Name;
foreach (MethodDefinition method in type.Methods) {
bool found = false;
string prefix = string.Empty;
if (!method.HasBody)
continue;
foreach (Instruction instr in method.Body.Instructions) {
MethodReference mr = instr.Operand as MethodReference;
if (mr != null && mr.Name == name) {
// explicit call to the requested method
if (Helpers.IsReferencedBy(analyzedMethod.DeclaringType, mr.DeclaringType) && mr.Resolve() == analyzedMethod) {
found = true;
prefix = "(as base) ";
break;
}
// virtual call to base method
if (instr.OpCode.Code == Code.Callvirt && Helpers.IsReferencedBy(baseMethod.DeclaringType, mr.DeclaringType) && mr.Resolve() == baseMethod) {
found = true;
break;
}
}
}
method.Body = null;
if (found) {
MethodDefinition codeLocation = this.Language.GetOriginalCodeLocation(method) as MethodDefinition;
if (codeLocation != null && !HasAlreadyBeenFound(codeLocation)) {
yield return new AnalyzedMethodTreeNode(codeLocation, prefix);
}
}
}
}
private bool HasAlreadyBeenFound(MethodDefinition method)
{
return !foundMethods.TryAdd(method, 0);
}
}
}

63
ILSpy/TreeNodes/Analyzer/Helpers.cs

@ -20,7 +20,10 @@ using System; @@ -20,7 +20,10 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICSharpCode.Decompiler;
using Mono.Cecil;
using ICSharpCode.Decompiler.ILAst;
using Mono.Cecil.Cil;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
@ -50,5 +53,65 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -50,5 +53,65 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
return true;
}
public static MemberReference GetOriginalCodeLocation(MemberReference member)
{
if (member is MethodDefinition)
return GetOriginalCodeLocation((MethodDefinition)member);
return member;
}
public static MethodDefinition GetOriginalCodeLocation(MethodDefinition method)
{
if (method.IsCompilerGenerated()) {
return FindMethodUsageInType(method.DeclaringType, method) ?? method;
}
var typeUsage = GetOriginalCodeLocation(method.DeclaringType, method);
return typeUsage ?? method;
}
public static MethodDefinition GetOriginalCodeLocation(TypeDefinition type, MethodDefinition method)
{
if (type != null && type.DeclaringType != null && type.IsCompilerGenerated()) {
MethodDefinition constructor = GetTypeConstructor(type);
return FindMethodUsageInType(type.DeclaringType, constructor);
}
return null;
}
private static MethodDefinition GetTypeConstructor(TypeDefinition type)
{
foreach (MethodDefinition method in type.Methods) {
if (method.Name == ".ctor")
return method;
}
return null;
}
private static MethodDefinition FindMethodUsageInType(TypeDefinition type, MethodDefinition analyzedMethod)
{
string name = analyzedMethod.Name;
foreach (MethodDefinition method in type.Methods) {
bool found = false;
if (!method.HasBody)
continue;
foreach (Instruction instr in method.Body.Instructions) {
MethodReference mr = instr.Operand as MethodReference;
if (mr != null && mr.Name == name &&
Helpers.IsReferencedBy(analyzedMethod.DeclaringType, mr.DeclaringType) &&
mr.Resolve() == analyzedMethod) {
found = true;
break;
}
}
method.Body = null;
if (found)
return method;
}
return null;
}
}
}

89
ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs

@ -28,7 +28,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -28,7 +28,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
/// <summary>
/// Determines the accessibility domain of a member for where-used analysis.
/// </summary>
internal class ScopedWhereUsedScopeAnalyzer<T>
internal class ScopedWhereUsedAnalyzer<T>
{
private AssemblyDefinition assemblyScope;
private TypeDefinition typeScope;
@ -37,40 +37,36 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -37,40 +37,36 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
private Accessibility typeAccessibility = Accessibility.Public;
private Func<TypeDefinition, IEnumerable<T>> typeAnalysisFunction;
public ScopedWhereUsedScopeAnalyzer(TypeDefinition type, Func<TypeDefinition, IEnumerable<T>> typeAnalysisFunction)
public ScopedWhereUsedAnalyzer(TypeDefinition type, Func<TypeDefinition, IEnumerable<T>> typeAnalysisFunction)
{
this.typeScope = type;
this.assemblyScope = type.Module.Assembly;
this.typeAnalysisFunction = typeAnalysisFunction;
}
public ScopedWhereUsedScopeAnalyzer(MethodDefinition method, Func<TypeDefinition, IEnumerable<T>> typeAnalysisFunction)
public ScopedWhereUsedAnalyzer(MethodDefinition method, Func<TypeDefinition, IEnumerable<T>> typeAnalysisFunction)
: this(method.DeclaringType, typeAnalysisFunction)
{
switch (method.Attributes & MethodAttributes.MemberAccessMask) {
case MethodAttributes.Private:
default:
memberAccessibility = Accessibility.Private;
break;
case MethodAttributes.FamANDAssem:
memberAccessibility = Accessibility.FamilyAndInternal;
break;
case MethodAttributes.Family:
memberAccessibility = Accessibility.Family;
break;
case MethodAttributes.Assembly:
memberAccessibility = Accessibility.Internal;
break;
case MethodAttributes.FamORAssem:
memberAccessibility = Accessibility.FamilyOrInternal;
break;
case MethodAttributes.Public:
memberAccessibility = Accessibility.Public;
break;
}
this.memberAccessibility = GetMethodAccessibility(method);
}
public ScopedWhereUsedAnalyzer(PropertyDefinition property, Func<TypeDefinition, IEnumerable<T>> typeAnalysisFunction)
: this(property.DeclaringType, typeAnalysisFunction)
{
Accessibility getterAccessibility = (property.GetMethod == null) ? Accessibility.Private : GetMethodAccessibility(property.GetMethod);
Accessibility setterAccessibility = (property.SetMethod == null) ? Accessibility.Private : GetMethodAccessibility(property.SetMethod);
this.memberAccessibility = (Accessibility)Math.Max((int)getterAccessibility, (int)setterAccessibility);
}
public ScopedWhereUsedScopeAnalyzer(FieldDefinition field, Func<TypeDefinition, IEnumerable<T>> typeAnalysisFunction)
public ScopedWhereUsedAnalyzer(EventDefinition eventDef, Func<TypeDefinition, IEnumerable<T>> typeAnalysisFunction)
: this(eventDef.DeclaringType, typeAnalysisFunction)
{
// we only have to check the accessibility of the the get method
// [CLS Rule 30: The accessibility of an event and of its accessors shall be identical.]
this.memberAccessibility = GetMethodAccessibility(eventDef.AddMethod);
}
public ScopedWhereUsedAnalyzer(FieldDefinition field, Func<TypeDefinition, IEnumerable<T>> typeAnalysisFunction)
: this(field.DeclaringType, typeAnalysisFunction)
{
switch (field.Attributes & FieldAttributes.FieldAccessMask) {
@ -96,6 +92,33 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -96,6 +92,33 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
}
}
private Accessibility GetMethodAccessibility(MethodDefinition method)
{
Accessibility accessibility;
switch (method.Attributes & MethodAttributes.MemberAccessMask) {
case MethodAttributes.Private:
default:
accessibility = Accessibility.Private;
break;
case MethodAttributes.FamANDAssem:
accessibility = Accessibility.FamilyAndInternal;
break;
case MethodAttributes.Family:
accessibility = Accessibility.Family;
break;
case MethodAttributes.Assembly:
accessibility = Accessibility.Internal;
break;
case MethodAttributes.FamORAssem:
accessibility = Accessibility.FamilyOrInternal;
break;
case MethodAttributes.Public:
accessibility = Accessibility.Public;
break;
}
return accessibility;
}
public IEnumerable<T> PerformAnalysis(CancellationToken ct)
{
if (memberAccessibility == Accessibility.Private) {
@ -231,7 +254,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -231,7 +254,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
break;
}
}
if (found)
if (found && AssemblyReferencesScopeType(assembly.AssemblyDefinition))
yield return assembly.AssemblyDefinition;
}
}
@ -255,12 +278,24 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -255,12 +278,24 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
foreach (var assembly in assemblies) {
ct.ThrowIfCancellationRequested();
if (friendAssemblies.Contains(assembly.ShortName)) {
if (friendAssemblies.Contains(assembly.ShortName) && AssemblyReferencesScopeType(assembly.AssemblyDefinition)) {
yield return assembly.AssemblyDefinition;
}
}
}
}
}
private bool AssemblyReferencesScopeType(AssemblyDefinition asm)
{
bool hasRef = false;
foreach (var typeref in asm.MainModule.GetTypeReferences()) {
if (typeref.Name == typeScope.Name && typeref.Namespace == typeScope.Namespace) {
hasRef = true;
break;
}
}
return hasRef;
}
}
}

4
ILSpy/TreeNodes/AssemblyTreeNode.cs

@ -134,6 +134,10 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -134,6 +134,10 @@ namespace ICSharpCode.ILSpy.TreeNodes
this.Children.Add(ns);
}
}
public override bool CanExpandRecursively {
get { return true; }
}
public TypeTreeNode FindTypeNode(TypeDefinition def)
{

2
ILSpy/TreeNodes/ResourceEntryNode.cs

@ -144,7 +144,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -144,7 +144,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
output.AddUIElement(() => new Image { Source = image });
output.WriteLine();
output.AddButton(Images.Save, "Save", delegate { Save(null); });
textView.Show(output, null);
textView.ShowNode(output, this, null);
return true;
}
catch (Exception) {

2
ILSpy/TreeNodes/ResourceTreeNode.cs

@ -95,7 +95,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -95,7 +95,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
ext = ".xml";
else
ext = Path.GetExtension(DecompilerTextView.CleanUpName(er.Name));
textView.Show(output, HighlightingManager.Instance.GetDefinitionByExtension(ext));
textView.ShowNode(output, this, HighlightingManager.Instance.GetDefinitionByExtension(ext));
return true;
}
}

19
ILSpy/TreeNodes/TypeTreeNode.cs

@ -117,6 +117,10 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -117,6 +117,10 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
public override bool CanExpandRecursively {
get { return true; }
}
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.DecompileType(type, output, options);
@ -146,8 +150,10 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -146,8 +150,10 @@ namespace ICSharpCode.ILSpy.TreeNodes
} else {
if (type.IsInterface)
return TypeIcon.Interface;
else if (type.BaseType != null && type.BaseType.FullName == typeof(MulticastDelegate).FullName)
else if (IsDelegate(type))
return TypeIcon.Delegate;
else if (IsStaticClass(type))
return TypeIcon.StaticClass;
else
return TypeIcon.Class;
}
@ -178,6 +184,17 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -178,6 +184,17 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
return overlay;
}
private static bool IsDelegate(TypeDefinition type)
{
return type.BaseType != null && type.BaseType.FullName == typeof(MulticastDelegate).FullName;
}
private static bool IsStaticClass(TypeDefinition type)
{
return type.IsSealed && type.IsAbstract;
}
#endregion
MemberReference IMemberTreeNode.Member {

2
ILSpy/TreeNodes/XamlResourceNode.cs

@ -74,7 +74,7 @@ namespace ICSharpCode.ILSpy.Xaml @@ -74,7 +74,7 @@ namespace ICSharpCode.ILSpy.Xaml
}
return output;
}),
t => textView.Show(t.Result, highlighting)
t => textView.ShowNode(t.Result, this, highlighting)
);
return true;
}

4
ILSpy/XmlDoc/XmlDocKeyProvider.cs

@ -79,6 +79,10 @@ namespace ICSharpCode.ILSpy.XmlDoc @@ -79,6 +79,10 @@ namespace ICSharpCode.ILSpy.XmlDoc
static void AppendTypeName(StringBuilder b, TypeReference type)
{
if (type == null) {
// could happen when a TypeSpecification has no ElementType; e.g. function pointers in C++/CLI assemblies
return;
}
if (type is GenericInstanceType) {
GenericInstanceType giType = (GenericInstanceType)type;
if (type.DeclaringType != null) {

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

@ -167,12 +167,14 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -167,12 +167,14 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// This method can be used to restrict the analysis to only a part of the method.
/// Only the control flow paths that are fully contained within the selected part will be analyzed.
/// </summary>
/// <remarks>Both 'start' and 'end' are inclusive.</remarks>
public void SetAnalyzedRange(Statement start, Statement end)
/// <remarks>By default, both 'start' and 'end' are inclusive.</remarks>
public void SetAnalyzedRange(Statement start, Statement end, bool startInclusive = true, bool endInclusive = true)
{
Debug.Assert(beginNodeDict.ContainsKey(start) && endNodeDict.ContainsKey(end));
int startIndex = beginNodeDict[start].Index;
int endIndex = endNodeDict[end].Index;
var dictForStart = startInclusive ? beginNodeDict : endNodeDict;
var dictForEnd = endInclusive ? endNodeDict : beginNodeDict;
Debug.Assert(dictForStart.ContainsKey(start) && dictForEnd.ContainsKey(end));
int startIndex = dictForStart[start].Index;
int endIndex = dictForEnd[end].Index;
if (startIndex > endIndex)
throw new ArgumentException("The start statement must be lexically preceding the end statement");
this.analyzedRangeStart = startIndex;

4
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs

@ -52,7 +52,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -52,7 +52,7 @@ namespace ICSharpCode.NRefactory.CSharp
public int PointerRank {
get {
return GetChildrenByRole(PointerRole).Count();
return GetChildrenByRole(PointerRole).Count;
}
set {
if (value < 0)
@ -141,7 +141,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -141,7 +141,7 @@ namespace ICSharpCode.NRefactory.CSharp
}
public int Dimensions {
get { return 1 + GetChildrenByRole(Roles.Comma).Count(); }
get { return 1 + GetChildrenByRole(Roles.Comma).Count; }
set {
int d = this.Dimensions;
while (d > value) {

2
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Identifier.cs

@ -96,7 +96,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -96,7 +96,7 @@ namespace ICSharpCode.NRefactory.CSharp
{
if (name == null)
throw new ArgumentNullException("name");
IsVerbatim = name.StartsWith ("@");
IsVerbatim = name.Length > 0 && name[0] == '@';
this.Name = IsVerbatim ? name.Substring (1) : name;
this.startLocation = location;
}

17
NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs

@ -941,7 +941,11 @@ namespace ICSharpCode.NRefactory.CSharp @@ -941,7 +941,11 @@ namespace ICSharpCode.NRefactory.CSharp
return ConvertChar(ch);
}
static string ConvertChar(char ch)
/// <summary>
/// Gets the escape sequence for the specified character.
/// </summary>
/// <remarks>This method does not convert ' or ".</remarks>
public static string ConvertChar(char ch)
{
switch (ch) {
case '\\':
@ -963,7 +967,9 @@ namespace ICSharpCode.NRefactory.CSharp @@ -963,7 +967,9 @@ namespace ICSharpCode.NRefactory.CSharp
case '\v':
return "\\v";
default:
if (char.IsControl(ch) || char.IsSurrogate(ch)) {
if (char.IsControl(ch) || char.IsSurrogate(ch) ||
// print all uncommon white spaces as numbers
(char.IsWhiteSpace(ch) && ch != ' ')) {
return "\\u" + ((int)ch).ToString("x4");
} else {
return ch.ToString();
@ -971,7 +977,10 @@ namespace ICSharpCode.NRefactory.CSharp @@ -971,7 +977,10 @@ namespace ICSharpCode.NRefactory.CSharp
}
}
static string ConvertString(string str)
/// <summary>
/// Converts special characters to escape sequences within the given string.
/// </summary>
public static string ConvertString(string str)
{
StringBuilder sb = new StringBuilder();
foreach (char ch in str) {
@ -2145,7 +2154,9 @@ namespace ICSharpCode.NRefactory.CSharp @@ -2145,7 +2154,9 @@ namespace ICSharpCode.NRefactory.CSharp
// "1.0 / /*comment*/a", then we need to insert a space in front of the comment.
formatter.Space();
}
formatter.StartNode(comment);
formatter.WriteComment(comment.CommentType, comment.Content);
formatter.EndNode(comment);
lastWritten = LastWritten.Whitespace;
return null;
}

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

@ -263,7 +263,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -263,7 +263,7 @@ namespace ICSharpCode.NRefactory.CSharp
#region Fields
public override IEntity VisitFieldDeclaration(FieldDeclaration fieldDeclaration, object data)
{
bool isSingleField = fieldDeclaration.Variables.Count() == 1;
bool isSingleField = fieldDeclaration.Variables.Count == 1;
Modifiers modifiers = fieldDeclaration.Modifiers;
DefaultField field = null;
foreach (VariableInitializer vi in fieldDeclaration.Variables) {
@ -476,7 +476,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -476,7 +476,7 @@ namespace ICSharpCode.NRefactory.CSharp
#region Events
public override IEntity VisitEventDeclaration(EventDeclaration eventDeclaration, object data)
{
bool isSingleEvent = eventDeclaration.Variables.Count() == 1;
bool isSingleEvent = eventDeclaration.Variables.Count == 1;
Modifiers modifiers = eventDeclaration.Modifiers;
DefaultEvent ev = null;
foreach (VariableInitializer vi in eventDeclaration.Variables) {

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

@ -241,7 +241,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -241,7 +241,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
ResolveResult VisitFieldOrEventDeclaration(AttributedNode fieldOrEventDeclaration)
{
int initializerCount = fieldOrEventDeclaration.GetChildrenByRole(FieldDeclaration.Roles.Variable).Count();
int initializerCount = fieldOrEventDeclaration.GetChildrenByRole(FieldDeclaration.Roles.Variable).Count;
ResolveResult result = null;
for (AstNode node = fieldOrEventDeclaration.FirstChild; node != null; node = node.NextSibling) {
if (node.Role == FieldDeclaration.Roles.Variable) {
@ -939,7 +939,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -939,7 +939,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
firstInitializer != null ? firstInitializer.Initializer : null,
false);
int initializerCount = variableDeclarationStatement.Variables.Count();
int initializerCount = variableDeclarationStatement.Variables.Count;
ResolveResult result = null;
for (AstNode node = variableDeclarationStatement.FirstChild; node != null; node = node.NextSibling) {
if (node.Role == FieldDeclaration.Roles.Variable) {

2
NRefactory/ICSharpCode.NRefactory/Documentation/XmlDocumentationProvider.cs

@ -120,7 +120,7 @@ namespace ICSharpCode.NRefactory.Documentation @@ -120,7 +120,7 @@ namespace ICSharpCode.NRefactory.Documentation
static string GetRedirectionTarget(string target)
{
string programFilesDir = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
string programFilesDir = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
programFilesDir = AppendDirectorySeparator(programFilesDir);
string corSysDir = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();

14
SharpTreeView/SharpTreeNode.cs

@ -267,12 +267,26 @@ namespace ICSharpCode.TreeView @@ -267,12 +267,26 @@ namespace ICSharpCode.TreeView
lazyLoading = value;
if (lazyLoading) {
IsExpanded = false;
if (canExpandRecursively) {
canExpandRecursively = false;
RaisePropertyChanged("CanExpandRecursively");
}
}
RaisePropertyChanged("LazyLoading");
RaisePropertyChanged("ShowExpander");
}
}
bool canExpandRecursively = true;
/// <summary>
/// Gets whether this node can be expanded recursively.
/// If not overridden, this property returns false if the node is using lazy-loading, and true otherwise.
/// </summary>
public virtual bool CanExpandRecursively {
get { return canExpandRecursively; }
}
public virtual bool ShowIcon
{
get { return Icon != null; }

17
SharpTreeView/SharpTreeView.cs

@ -227,11 +227,28 @@ namespace ICSharpCode.TreeView @@ -227,11 +227,28 @@ namespace ICSharpCode.TreeView
e.Handled = true;
}
break;
case Key.Multiply:
if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) {
container.Node.IsExpanded = true;
ExpandRecursively(container.Node);
e.Handled = true;
}
break;
}
if (!e.Handled)
base.OnKeyDown(e);
}
void ExpandRecursively(SharpTreeNode node)
{
if (node.CanExpandRecursively) {
node.IsExpanded = true;
foreach (SharpTreeNode child in node.Children) {
ExpandRecursively(child);
}
}
}
/// <summary>
/// Scrolls the specified node in view and sets keyboard focus on it.
/// </summary>

Loading…
Cancel
Save