diff --git a/Debugger/ILSpy.Debugger/Models/TreeModel/ExpressionNode.cs b/Debugger/ILSpy.Debugger/Models/TreeModel/ExpressionNode.cs index 45947351e..3a9fd740b 100644 --- a/Debugger/ILSpy.Debugger/Models/TreeModel/ExpressionNode.cs +++ b/Debugger/ILSpy.Debugger/Models/TreeModel/ExpressionNode.cs @@ -10,7 +10,7 @@ using System.Windows.Media; using Debugger; using Debugger.MetaData; -using Decompiler; +using ICSharpCode.Decompiler.ILAst; using ICSharpCode.NRefactory.Ast; using ICSharpCode.NRefactory.CSharp; using ILSpy.Debugger.Services; diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 334e8a917..d0927f732 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -1,20 +1,25 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; -using Decompiler.Transforms; using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Ast.Transforms; +using ICSharpCode.Decompiler.ILAst; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.Utils; using Mono.Cecil; using Mono.Cecil.Cil; -using Ast = ICSharpCode.NRefactory.CSharp; -using ClassType = ICSharpCode.NRefactory.TypeSystem.ClassType; -using VarianceModifier = ICSharpCode.NRefactory.TypeSystem.VarianceModifier; -namespace Decompiler +namespace ICSharpCode.Decompiler.Ast { + using Ast = ICSharpCode.NRefactory.CSharp; + using ClassType = ICSharpCode.NRefactory.TypeSystem.ClassType; + using VarianceModifier = ICSharpCode.NRefactory.TypeSystem.VarianceModifier; + public class AstBuilder { DecompilerContext context = new DecompilerContext(); @@ -209,7 +214,7 @@ namespace Decompiler AddTypeMembers(astType, typeDef); } - ConvertCustomAttributes(astType, typeDef); + ConvertAttributes(astType, typeDef); return astType; } @@ -534,8 +539,7 @@ namespace Decompiler astMethod.Modifiers = ConvertModifiers(methodDef); astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, context); } - ConvertCustomAttributes(astMethod, methodDef); - ConvertCustomAttributes(astMethod, methodDef.MethodReturnType, AttributeTarget.Return); + ConvertAttributes(astMethod, methodDef); return astMethod; } @@ -594,6 +598,7 @@ namespace Decompiler astMethod.Name = CleanName(methodDef.DeclaringType.Name); astMethod.Parameters.AddRange(MakeParameters(methodDef.Parameters)); astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, context); + ConvertAttributes(astMethod, methodDef); return astMethod; } @@ -611,8 +616,7 @@ namespace Decompiler astProp.Getter = new Accessor { Body = AstMethodBodyBuilder.CreateMethodBody(propDef.GetMethod, context) }.WithAnnotation(propDef.GetMethod); - ConvertCustomAttributes(astProp.Getter, propDef.GetMethod); - ConvertCustomAttributes(astProp.Getter, propDef.GetMethod.MethodReturnType, AttributeTarget.Return); + ConvertAttributes(astProp.Getter, propDef.GetMethod); if (methodMapping != null) astProp.Getter.AddAnnotation(methodMapping); @@ -624,8 +628,7 @@ namespace Decompiler astProp.Setter = new Accessor { Body = AstMethodBodyBuilder.CreateMethodBody(propDef.SetMethod, context) }.WithAnnotation(propDef.SetMethod); - ConvertCustomAttributes(astProp.Setter, propDef.SetMethod); - ConvertCustomAttributes(astProp.Setter, propDef.SetMethod.MethodReturnType, AttributeTarget.Return); + ConvertAttributes(astProp.Setter, propDef.SetMethod); ConvertCustomAttributes(astProp.Setter, propDef.SetMethod.Parameters.Last(), AttributeTarget.Param); if (methodMapping != null) @@ -649,6 +652,7 @@ namespace Decompiler astEvent.AddAccessor = new Accessor { Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.AddMethod, context) }.WithAnnotation(eventDef.AddMethod); + ConvertAttributes(astEvent.AddAccessor, eventDef.AddMethod); if (methodMapping != null) astEvent.AddAccessor.AddAnnotation(methodMapping); @@ -660,6 +664,7 @@ namespace Decompiler astEvent.RemoveAccessor = new Accessor { Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.RemoveMethod, context) }.WithAnnotation(eventDef.RemoveMethod); + ConvertAttributes(astEvent.RemoveAccessor, eventDef.RemoveMethod); if (methodMapping != null) astEvent.RemoveAccessor.AddAnnotation(methodMapping); @@ -681,7 +686,7 @@ namespace Decompiler else initializer.Initializer = new PrimitiveExpression(fieldDef.Constant); } - ConvertCustomAttributes(astField, fieldDef); + ConvertAttributes(astField, fieldDef); return astField; } @@ -693,15 +698,227 @@ namespace Decompiler astParam.Name = paramDef.Name; if (paramDef.ParameterType is ByReferenceType) { - astParam.ParameterModifier = paramDef.IsOut ? ParameterModifier.Out : ParameterModifier.Ref; + astParam.ParameterModifier = (!paramDef.IsIn && paramDef.IsOut) ? ParameterModifier.Out : ParameterModifier.Ref; } // TODO: params, this ConvertCustomAttributes(astParam, paramDef); + ModuleDefinition module = ((MethodDefinition)paramDef.Method).Module; + if (paramDef.HasMarshalInfo) { + astParam.Attributes.Add(new AttributeSection(ConvertMarshalInfo(paramDef, module))); + } + if (astParam.ParameterModifier != ParameterModifier.Out) { + if (paramDef.IsIn) + astParam.Attributes.Add(new AttributeSection(CreateNonCustomAttribute(typeof(InAttribute), module))); + if (paramDef.IsOut) + astParam.Attributes.Add(new AttributeSection(CreateNonCustomAttribute(typeof(OutAttribute), module))); + } yield return astParam; } } - + + #region ConvertAttributes + void ConvertAttributes(AttributedNode attributedNode, TypeDefinition typeDefinition) + { + ConvertCustomAttributes(attributedNode, typeDefinition); + + // Handle the non-custom attributes: + #region SerializableAttribute + if (typeDefinition.IsSerializable) + attributedNode.Attributes.Add(new AttributeSection(CreateNonCustomAttribute(typeof(SerializableAttribute)))); + #endregion + + #region StructLayoutAttribute + LayoutKind layoutKind = LayoutKind.Auto; + switch (typeDefinition.Attributes & TypeAttributes.LayoutMask) { + case TypeAttributes.SequentialLayout: + layoutKind = LayoutKind.Sequential; + break; + case TypeAttributes.ExplicitLayout: + layoutKind = LayoutKind.Explicit; + break; + } + CharSet charSet = CharSet.None; + switch (typeDefinition.Attributes & TypeAttributes.StringFormatMask) { + case TypeAttributes.AnsiClass: + charSet = CharSet.Ansi; + break; + case TypeAttributes.AutoClass: + charSet = CharSet.Auto; + break; + case TypeAttributes.UnicodeClass: + charSet = CharSet.Unicode; + break; + } + LayoutKind defaultLayoutKind = (typeDefinition.IsValueType && !typeDefinition.IsEnum) ? LayoutKind.Sequential: LayoutKind.Auto; + if (layoutKind != defaultLayoutKind || charSet != CharSet.Ansi || typeDefinition.PackingSize > 0 || typeDefinition.ClassSize > 0) { + var structLayout = CreateNonCustomAttribute(typeof(StructLayoutAttribute)); + structLayout.Arguments.Add(new IdentifierExpression("LayoutKind").Member(layoutKind.ToString())); + if (charSet != CharSet.Ansi) { + structLayout.AddNamedArgument("CharSet", new IdentifierExpression("CharSet").Member(charSet.ToString())); + } + if (typeDefinition.PackingSize > 0) { + structLayout.AddNamedArgument("Pack", new PrimitiveExpression((int)typeDefinition.PackingSize)); + } + if (typeDefinition.ClassSize > 0) { + structLayout.AddNamedArgument("Size", new PrimitiveExpression((int)typeDefinition.ClassSize)); + } + attributedNode.Attributes.Add(new AttributeSection(structLayout)); + } + #endregion + } + + void ConvertAttributes(AttributedNode attributedNode, MethodDefinition methodDefinition) + { + ConvertCustomAttributes(attributedNode, methodDefinition); + + MethodImplAttributes implAttributes = methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask; + + #region DllImportAttribute + if (methodDefinition.HasPInvokeInfo) { + PInvokeInfo info = methodDefinition.PInvokeInfo; + Ast.Attribute dllImport = CreateNonCustomAttribute(typeof(DllImportAttribute)); + dllImport.Arguments.Add(new PrimitiveExpression(info.Module.Name)); + + if (info.IsBestFitDisabled) + dllImport.AddNamedArgument("BestFitMapping", new PrimitiveExpression(false)); + if (info.IsBestFitEnabled) + dllImport.AddNamedArgument("BestFitMapping", new PrimitiveExpression(true)); + + CallingConvention callingConvention; + switch (info.Attributes & PInvokeAttributes.CallConvMask) { + case PInvokeAttributes.CallConvCdecl: + callingConvention = CallingConvention.Cdecl; + break; + case PInvokeAttributes.CallConvFastcall: + callingConvention = CallingConvention.FastCall; + break; + case PInvokeAttributes.CallConvStdCall: + callingConvention = CallingConvention.StdCall; + break; + case PInvokeAttributes.CallConvThiscall: + callingConvention = CallingConvention.ThisCall; + break; + case PInvokeAttributes.CallConvWinapi: + callingConvention = CallingConvention.Winapi; + break; + default: + throw new NotSupportedException("unknown calling convention"); + } + if (callingConvention != CallingConvention.Winapi) + dllImport.AddNamedArgument("CallingConvention", new IdentifierExpression("CallingConvention").Member(callingConvention.ToString())); + + CharSet charSet = CharSet.None; + switch (info.Attributes & PInvokeAttributes.CharSetMask) { + case PInvokeAttributes.CharSetAnsi: + charSet = CharSet.Ansi; + break; + case PInvokeAttributes.CharSetAuto: + charSet = CharSet.Auto; + break; + case PInvokeAttributes.CharSetUnicode: + charSet = CharSet.Unicode; + break; + } + if (charSet != CharSet.None) + dllImport.AddNamedArgument("CharSet", new IdentifierExpression("CharSet").Member(charSet.ToString())); + + if (!string.IsNullOrEmpty(info.EntryPoint) && info.EntryPoint != methodDefinition.Name) + dllImport.AddNamedArgument("EntryPoint", new PrimitiveExpression(info.EntryPoint)); + + if (info.IsNoMangle) + dllImport.AddNamedArgument("ExactSpelling", new PrimitiveExpression(true)); + + if ((implAttributes & MethodImplAttributes.PreserveSig) == MethodImplAttributes.PreserveSig) + implAttributes &= ~MethodImplAttributes.PreserveSig; + else + dllImport.AddNamedArgument("PreserveSig", new PrimitiveExpression(false)); + + if (info.SupportsLastError) + dllImport.AddNamedArgument("SetLastError", new PrimitiveExpression(true)); + + if (info.IsThrowOnUnmappableCharDisabled) + dllImport.AddNamedArgument("ThrowOnUnmappableChar", new PrimitiveExpression(false)); + if (info.IsThrowOnUnmappableCharEnabled) + dllImport.AddNamedArgument("ThrowOnUnmappableChar", new PrimitiveExpression(true)); + + attributedNode.Attributes.Add(new AttributeSection(dllImport)); + } + #endregion + + #region PreserveSigAttribute + if (implAttributes == MethodImplAttributes.PreserveSig) { + attributedNode.Attributes.Add(new AttributeSection(CreateNonCustomAttribute(typeof(PreserveSigAttribute)))); + implAttributes = 0; + } + #endregion + + #region MethodImplAttribute + if (implAttributes != 0) { + Ast.Attribute methodImpl = CreateNonCustomAttribute(typeof(MethodImplAttribute)); + TypeReference methodImplOptions = new TypeReference( + "System.Runtime.CompilerServices", "MethodImplOptions", + methodDefinition.Module, methodDefinition.Module.TypeSystem.Corlib); + methodImpl.Arguments.Add(MakePrimitive((long)implAttributes, methodImplOptions)); + attributedNode.Attributes.Add(new AttributeSection(methodImpl)); + } + #endregion + + ConvertCustomAttributes(attributedNode, methodDefinition.MethodReturnType, AttributeTarget.Return); + if (methodDefinition.MethodReturnType.HasMarshalInfo) { + var marshalInfo = ConvertMarshalInfo(methodDefinition.MethodReturnType, methodDefinition.Module); + attributedNode.Attributes.Add(new AttributeSection(marshalInfo) { AttributeTarget = AttributeTarget.Return }); + } + } + + void ConvertAttributes(AttributedNode attributedNode, FieldDefinition fieldDefinition) + { + ConvertCustomAttributes(attributedNode, fieldDefinition); + + #region FieldOffsetAttribute + if (fieldDefinition.HasLayoutInfo) { + Ast.Attribute fieldOffset = CreateNonCustomAttribute(typeof(FieldOffsetAttribute)); + fieldOffset.Arguments.Add(new PrimitiveExpression(fieldDefinition.Offset)); + attributedNode.Attributes.Add(new AttributeSection(fieldOffset)); + } + #endregion + + if (fieldDefinition.HasMarshalInfo) { + attributedNode.Attributes.Add(new AttributeSection(ConvertMarshalInfo(fieldDefinition, fieldDefinition.Module))); + } + } + + #region MarshalAsAttribute (ConvertMarshalInfo) + static Ast.Attribute ConvertMarshalInfo(IMarshalInfoProvider marshalInfoProvider, ModuleDefinition module) + { + MarshalInfo marshalInfo = marshalInfoProvider.MarshalInfo; + Ast.Attribute attr = CreateNonCustomAttribute(typeof(MarshalAsAttribute), module); + string memberName; + if (marshalInfo.NativeType == NativeType.Boolean) + memberName = "Bool"; + else + memberName = marshalInfo.NativeType.ToString(); + attr.Arguments.Add(new IdentifierExpression("UnmanagedType").Member(memberName)); + return attr; + } + #endregion + + Ast.Attribute CreateNonCustomAttribute(Type attributeType) + { + return CreateNonCustomAttribute(attributeType, context.CurrentType != null ? context.CurrentType.Module : null); + } + + static Ast.Attribute CreateNonCustomAttribute(Type attributeType, ModuleDefinition module) + { + Debug.Assert(attributeType.Name.EndsWith("Attribute", StringComparison.Ordinal)); + Ast.Attribute attr = new Ast.Attribute(); + attr.Type = new SimpleType(attributeType.Name.Substring(0, attributeType.Name.Length - "Attribute".Length)); + if (module != null) { + attr.Type.AddAnnotation(new TypeReference(attributeType.Namespace, attributeType.Name, module, module.TypeSystem.Corlib)); + } + return attr; + } + static void ConvertCustomAttributes(AstNode attributedNode, ICustomAttributeProvider customAttributeProvider, AttributeTarget target = AttributeTarget.None) { if (customAttributeProvider.HasCustomAttributes) { @@ -723,8 +940,9 @@ namespace Decompiler } } if (customAttribute.HasProperties) { + TypeDefinition resolvedAttributeType = customAttribute.AttributeType.Resolve(); foreach (var propertyNamedArg in customAttribute.Properties) { - var propertyReference = customAttribute.AttributeType.Resolve().Properties.First(pr => pr.Name == propertyNamedArg.Name); + 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)); @@ -732,8 +950,9 @@ namespace Decompiler } if (customAttribute.HasFields) { + TypeDefinition resolvedAttributeType = customAttribute.AttributeType.Resolve(); foreach (var fieldNamedArg in customAttribute.Fields) { - var fieldReference = customAttribute.AttributeType.Resolve().Fields.First(f => f.Name == fieldNamedArg.Name); + 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)); @@ -758,7 +977,7 @@ namespace Decompiler } } } - + private static Expression ConvertArgumentValue(CustomAttributeArgument parameter) { var type = parameter.Type.Resolve(); @@ -780,7 +999,7 @@ namespace Decompiler } return parameterValue; } - + #endregion internal static Expression MakePrimitive(long val, TypeReference type) { diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 93f6c1d1c..fc5eb754d 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -4,16 +4,18 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; -using ICSharpCode.NRefactory.Utils; -using Ast = ICSharpCode.NRefactory.CSharp; + +using ICSharpCode.Decompiler.ILAst; using ICSharpCode.NRefactory.CSharp; -using Cecil = Mono.Cecil; +using ICSharpCode.NRefactory.Utils; using Mono.Cecil; using Mono.Cecil.Cil; -using Decompiler.ControlFlow; -namespace Decompiler +namespace ICSharpCode.Decompiler.Ast { + using Ast = ICSharpCode.NRefactory.CSharp; + using Cecil = Mono.Cecil; + public class AstMethodBodyBuilder { MethodDefinition methodDef; diff --git a/ICSharpCode.Decompiler/Ast/CSharpCodeMapping.cs b/ICSharpCode.Decompiler/Ast/CSharpCodeMapping.cs index 91d530b8a..51f50e072 100644 --- a/ICSharpCode.Decompiler/Ast/CSharpCodeMapping.cs +++ b/ICSharpCode.Decompiler/Ast/CSharpCodeMapping.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using ICSharpCode.Decompiler; -namespace Decompiler +namespace ICSharpCode.Decompiler.Ast { /// /// Stores the C# code mappings. diff --git a/ICSharpCode.Decompiler/Ast/CommentStatement.cs b/ICSharpCode.Decompiler/Ast/CommentStatement.cs index 6f0135c72..bca46c4a3 100644 --- a/ICSharpCode.Decompiler/Ast/CommentStatement.cs +++ b/ICSharpCode.Decompiler/Ast/CommentStatement.cs @@ -6,7 +6,7 @@ using System.Linq; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.PatternMatching; -namespace Decompiler +namespace ICSharpCode.Decompiler.Ast { /// /// Allows storing comments inside IEnumerable{Statement}. Used in the AstMethodBuilder. diff --git a/ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs b/ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs index fe6a15d51..1bc37a624 100644 --- a/ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs +++ b/ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs @@ -6,7 +6,7 @@ using System.Linq; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.PatternMatching; -namespace Decompiler +namespace ICSharpCode.Decompiler.Ast { /// /// Helper class for declaring variables. diff --git a/ICSharpCode.Decompiler/Ast/DecompilerContext.cs b/ICSharpCode.Decompiler/Ast/DecompilerContext.cs index 108032e40..7ea5c0b02 100644 --- a/ICSharpCode.Decompiler/Ast/DecompilerContext.cs +++ b/ICSharpCode.Decompiler/Ast/DecompilerContext.cs @@ -5,7 +5,7 @@ using System; using System.Threading; using Mono.Cecil; -namespace Decompiler +namespace ICSharpCode.Decompiler { public class DecompilerContext { diff --git a/ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs b/ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs index 8657037ad..7a8258a98 100644 --- a/ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs +++ b/ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs @@ -4,7 +4,7 @@ using System; using ICSharpCode.NRefactory.CSharp; -namespace Decompiler +namespace ICSharpCode.Decompiler.Ast { static class NRefactoryExtensions { @@ -28,5 +28,10 @@ namespace Decompiler node.Remove(); return node; } + + public static void AddNamedArgument(this NRefactory.CSharp.Attribute attribute, string name, Expression argument) + { + attribute.Arguments.Add(new AssignmentExpression(new IdentifierExpression(name), argument)); + } } } diff --git a/ICSharpCode.Decompiler/Ast/NameVariables.cs b/ICSharpCode.Decompiler/Ast/NameVariables.cs index bea32a6a2..465be539a 100644 --- a/ICSharpCode.Decompiler/Ast/NameVariables.cs +++ b/ICSharpCode.Decompiler/Ast/NameVariables.cs @@ -4,9 +4,11 @@ using System; using System.Collections.Generic; using System.Linq; + +using ICSharpCode.Decompiler.ILAst; using Mono.Cecil; -namespace Decompiler +namespace ICSharpCode.Decompiler.Ast { public class NameVariables { diff --git a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs index 448cd150e..0db9aec88 100644 --- a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs +++ b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs @@ -4,11 +4,13 @@ using System; using System.Collections.Generic; using System.Linq; + using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.ILAst; using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; -namespace Decompiler +namespace ICSharpCode.Decompiler.Ast { public class TextOutputFormatter : IOutputFormatter { diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs b/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs index 276c44d79..a9b3f3fc6 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs @@ -6,7 +6,7 @@ using System.Diagnostics; using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; -namespace Decompiler.Transforms +namespace ICSharpCode.Decompiler.Ast.Transforms { /// /// Base class for AST visitors that need the current type/method context info. diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs b/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs index ee2225073..7acab4d21 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs @@ -6,7 +6,7 @@ using System.Linq; using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; -namespace Decompiler.Transforms +namespace ICSharpCode.Decompiler.Ast.Transforms { /// /// If the first element of a constructor is a chained constructor call, convert it into a constructor initializer. diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index 87df7aca8..c0fcabab3 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -10,7 +10,7 @@ using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.PatternMatching; using Mono.Cecil; -namespace Decompiler.Transforms +namespace ICSharpCode.Decompiler.Ast.Transforms { /// /// Converts "new Action(obj, ldftn(func))" into "new Action(obj.func)". @@ -94,13 +94,20 @@ namespace Decompiler.Transforms return base.VisitObjectCreateExpression(objectCreateExpression, data); } + internal static bool IsAnonymousMethod(DecompilerContext context, MethodDefinition method) + { + if (method == null || !method.Name.StartsWith("<", StringComparison.Ordinal)) + return false; + if (!(method.IsCompilerGenerated() || IsPotentialClosure(context, method.DeclaringType))) + return false; + return true; + } + bool HandleAnonymousMethod(ObjectCreateExpression objectCreateExpression, Expression target, MethodReference methodRef) { // Anonymous methods are defined in the same assembly, so there's no need to Resolve(). MethodDefinition method = methodRef as MethodDefinition; - if (method == null || !method.Name.StartsWith("<", StringComparison.Ordinal)) - return false; - if (!(method.IsCompilerGenerated() || IsPotentialClosure(method.DeclaringType))) + if (!IsAnonymousMethod(context, method)) return false; // Decompile the anonymous method: @@ -143,7 +150,7 @@ namespace Decompiler.Transforms return true; } - bool IsPotentialClosure(TypeDefinition potentialDisplayClass) + static bool IsPotentialClosure(DecompilerContext context, TypeDefinition potentialDisplayClass) { if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGenerated()) return false; @@ -164,7 +171,7 @@ namespace Decompiler.Transforms continue; var variable = stmt.Variables.Single(); TypeDefinition type = stmt.Type.Annotation(); - if (!IsPotentialClosure(type)) + if (!IsPotentialClosure(context, type)) continue; ObjectCreateExpression oce = variable.Initializer as ObjectCreateExpression; if (oce == null || oce.Type.Annotation() != type || oce.Arguments.Any() || !oce.Initializer.IsNull) diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index bdc4b1d37..f41fe43ec 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -3,12 +3,11 @@ using System; using System.Linq; -using Decompiler.Transforms; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.PatternMatching; using Mono.Cecil; -namespace Decompiler.Transforms +namespace ICSharpCode.Decompiler.Ast.Transforms { /// /// Finds the expanded form of using statements using pattern matching and replaces it with a UsingStatement. diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs b/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs index 1d04bd941..409784cba 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs @@ -4,7 +4,7 @@ using System.Linq; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.PatternMatching; -namespace Decompiler.Transforms +namespace ICSharpCode.Decompiler.Ast.Transforms { public class PushNegation: DepthFirstAstVisitor, IAstTransform { diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs index 0a7cb2655..21d5efb55 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs @@ -6,7 +6,7 @@ using Mono.Cecil; using Ast = ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp; -namespace Decompiler.Transforms +namespace ICSharpCode.Decompiler.Ast.Transforms { /// /// Replaces method calls with the appropriate operator expressions. diff --git a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs index a559930cb..d240c7583 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs @@ -5,7 +5,7 @@ using System; using System.Threading; using ICSharpCode.NRefactory.CSharp; -namespace Decompiler.Transforms +namespace ICSharpCode.Decompiler.Ast.Transforms { public interface IAstTransform { diff --git a/ICSharpCode.Decompiler/CecilExtensions.cs b/ICSharpCode.Decompiler/CecilExtensions.cs index a97ed2427..1ef980d1f 100644 --- a/ICSharpCode.Decompiler/CecilExtensions.cs +++ b/ICSharpCode.Decompiler/CecilExtensions.cs @@ -169,5 +169,14 @@ namespace ICSharpCode.Decompiler } return false; } + + public static bool IsCompilerGeneratedOrIsInCompilerGeneratedClass(this IMemberDefinition member) + { + if (member == null) + return false; + if (member.IsCompilerGenerated()) + return true; + return IsCompilerGeneratedOrIsInCompilerGeneratedClass(member.DeclaringType); + } } } diff --git a/ICSharpCode.Decompiler/CodeMappings.cs b/ICSharpCode.Decompiler/CodeMappings.cs index 72f799236..b6bc80253 100644 --- a/ICSharpCode.Decompiler/CodeMappings.cs +++ b/ICSharpCode.Decompiler/CodeMappings.cs @@ -6,8 +6,9 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using Decompiler; +using ICSharpCode.Decompiler.Ast; using ICSharpCode.Decompiler.Disassembler; +using ICSharpCode.Decompiler.ILAst; using Mono.Cecil; namespace ICSharpCode.Decompiler diff --git a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs index 017e97c69..6361f122e 100644 --- a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs @@ -21,9 +21,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading; -using Decompiler; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.FlowAnalysis; +using ICSharpCode.Decompiler.ILAst; using Mono.Cecil; using Mono.Cecil.Cil; diff --git a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs index 600c7230e..bfab810cf 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs @@ -21,7 +21,7 @@ using System; namespace ICSharpCode.Decompiler.FlowAnalysis { /// - /// + /// Describes the type of a control flow egde. /// public enum JumpType { diff --git a/ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs b/ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs index 4c80e21d6..08c2aaca9 100644 --- a/ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs +++ b/ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs @@ -7,7 +7,7 @@ using System.Linq; using Mono.Cecil; -namespace Decompiler +namespace ICSharpCode.Decompiler.ILAst { /// /// IL AST transformation that introduces array initializers. diff --git a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs index cd30046e4..c5ea44ab6 100644 --- a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs +++ b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs @@ -3,9 +3,8 @@ using System.Diagnostics; using System.IO; using System.Collections.Generic; using System.Linq; -using Decompiler.ControlFlow; -namespace Decompiler +namespace ICSharpCode.Decompiler.ILAst { public class GotoRemoval { diff --git a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index 09a82bad9..0dd715afb 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs @@ -8,7 +8,7 @@ using Mono.Cecil; using Mono.Cecil.Cil; using Cecil = Mono.Cecil; -namespace Decompiler +namespace ICSharpCode.Decompiler.ILAst { public class ILAstBuilder { @@ -17,54 +17,59 @@ namespace Decompiler /// public static ConcurrentDictionary> MemberLocalVariables = new ConcurrentDictionary>(); + static ByteCode[] EmptyByteCodeArray = new ByteCode[] {}; + + /// Immutable class StackSlot { - public List PushedBy; // One of those - public ILVariable LoadFrom; // Where can we get the value from in AST + public readonly ByteCode[] PushedBy; // One of those + public readonly ILVariable LoadFrom; // Where can we get the value from in AST - public StackSlot() + public StackSlot(ByteCode[] pushedBy, ILVariable loadFrom) { + this.PushedBy = pushedBy; + this.LoadFrom = loadFrom; } public StackSlot(ByteCode pushedBy) { - this.PushedBy = new List(1); - this.PushedBy.Add(pushedBy); + this.PushedBy = new[] { pushedBy }; + this.LoadFrom = null; } public static List CloneStack(List stack, int? popCount) { - List clone = new List(); if (popCount.HasValue) { - if (popCount.Value > stack.Count) { - throw new Exception("Can not pop - the stack is empty"); - } - for(int i = 0; i < stack.Count - popCount.Value; i++) { - clone.Add(new StackSlot() { PushedBy = new List(stack[i].PushedBy) }); - } + return stack.GetRange(0, stack.Count - popCount.Value); + } else { + return new List(0); } - return clone; } } + /// Immutable class VariableSlot - { - public static List Empty = new List(); + { + public readonly ByteCode[] StoredBy; // One of those + public readonly bool StoredByAll; // Overestimate which is useful for exceptional control flow. + + public VariableSlot(ByteCode[] storedBy, bool storedByAll) + { + this.StoredBy = storedBy; + this.StoredByAll = storedByAll; + } - public List StoredBy = Empty; // One of those - public bool StoredByAll; // Overestimate which is useful for exceptional control flow. + public VariableSlot(ByteCode storedBy) + { + this.StoredBy = new[] { storedBy }; + this.StoredByAll = false; + } public static VariableSlot[] CloneVariableState(VariableSlot[] state) { - VariableSlot[] clone = new ILAstBuilder.VariableSlot[state.Length]; - if (VariableSlot.Empty.Count > 0) - throw new Exception("Constant data corrupted"); + VariableSlot[] clone = new VariableSlot[state.Length]; for (int i = 0; i < clone.Length; i++) { - VariableSlot varSlot = state[i]; - clone[i] = new VariableSlot() { - StoredBy = varSlot.StoredBy.Count == 0 ? VariableSlot.Empty : new List(varSlot.StoredBy), - StoredByAll = varSlot.StoredByAll - }; + clone[i] = state[i]; } return clone; } @@ -73,7 +78,7 @@ namespace Decompiler { VariableSlot[] emptyVariableState = new VariableSlot[varCount]; for (int i = 0; i < emptyVariableState.Length; i++) { - emptyVariableState[i] = new VariableSlot(); + emptyVariableState[i] = new VariableSlot(EmptyByteCodeArray, false); } return emptyVariableState; } @@ -82,7 +87,7 @@ namespace Decompiler { VariableSlot[] unknownVariableState = new VariableSlot[varCount]; for (int i = 0; i < unknownVariableState.Length; i++) { - unknownVariableState[i] = new VariableSlot() { StoredByAll = true }; + unknownVariableState[i] = new VariableSlot(EmptyByteCodeArray, true); } return unknownVariableState; } @@ -100,9 +105,9 @@ namespace Decompiler public string Name { get { return "IL_" + this.Offset.ToString("X2"); } } public ByteCode Next; public Instruction[] Prefixes; // Non-null only if needed - public List StackBefore; + public List StackBefore; // Unique per bytecode; not shared public List StoreTo; // Store result of instruction to those AST variables - public VariableSlot[] VariablesBefore; + public VariableSlot[] VariablesBefore; // Unique per bytecode; not shared public VariableDefinition OperandAsVariable { get { return (VariableDefinition)this.Operand; } } @@ -181,7 +186,7 @@ namespace Decompiler if (!first) sb.Append(","); if (varSlot.StoredByAll) { sb.Append("*"); - } else if (varSlot.StoredBy.Count == 0) { + } else if (varSlot.StoredBy.Length == 0) { sb.Append("_"); } else { bool first2 = true; @@ -304,8 +309,7 @@ namespace Decompiler VariableSlot[] newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore); if (byteCode.Code == ILCode.Stloc) { int varIndex = ((VariableReference)byteCode.Operand).Index; - newVariableState[varIndex].StoredBy = new List(1) { byteCode }; - newVariableState[varIndex].StoredByAll = false; + newVariableState[varIndex] = new VariableSlot(byteCode); } // After the leave, finally block might have touched the variables @@ -360,10 +364,10 @@ namespace Decompiler // Merge stacks - modify the target for (int i = 0; i < newStack.Count; i++) { - List oldPushedBy = branchTarget.StackBefore[i].PushedBy; - List newPushedBy = oldPushedBy.Union(newStack[i].PushedBy).ToList(); - if (newPushedBy.Count > oldPushedBy.Count) { - branchTarget.StackBefore[i].PushedBy = newPushedBy; + ByteCode[] oldPushedBy = branchTarget.StackBefore[i].PushedBy; + ByteCode[] newPushedBy = oldPushedBy.Union(newStack[i].PushedBy); + if (newPushedBy.Length > oldPushedBy.Length) { + branchTarget.StackBefore[i] = new StackSlot(newPushedBy, null); modified = true; } } @@ -375,13 +379,13 @@ namespace Decompiler // All can not be unioned further if (!oldSlot.StoredByAll) { if (newSlot.StoredByAll) { - oldSlot.StoredByAll = true; + branchTarget.VariablesBefore[i] = newSlot; modified = true; } else { - List oldStoredBy = oldSlot.StoredBy; - List newStoredBy = oldStoredBy.Union(newSlot.StoredBy).ToList(); - if (newStoredBy.Count > oldStoredBy.Count) { - oldSlot.StoredBy = newStoredBy; + ByteCode[] oldStoredBy = oldSlot.StoredBy; + ByteCode[] newStoredBy = oldStoredBy.Union(newSlot.StoredBy); + if (newStoredBy.Length > oldStoredBy.Length) { + branchTarget.VariablesBefore[i] = new VariableSlot(newStoredBy, false); modified = true; } } @@ -403,16 +407,15 @@ namespace Decompiler int argIdx = 0; int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count; for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) { - StackSlot arg = byteCode.StackBefore[i]; ILVariable tmpVar = new ILVariable() { Name = string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), IsGenerated = true }; - arg.LoadFrom = tmpVar; - foreach(ByteCode pushedBy in arg.PushedBy) { + byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].PushedBy, tmpVar); + foreach(ByteCode pushedBy in byteCode.StackBefore[i].PushedBy) { if (pushedBy.StoreTo == null) { pushedBy.StoreTo = new List(1); } pushedBy.StoreTo.Add(tmpVar); } - if (arg.PushedBy.Count == 1) { + if (byteCode.StackBefore[i].PushedBy.Length == 1) { allowInline[tmpVar] = true; } argIdx++; @@ -489,10 +492,10 @@ namespace Decompiler // Add loads to the data structure; merge variables if necessary foreach(ByteCode load in loads) { - List storedBy = load.VariablesBefore[variableIndex].StoredBy; - if (storedBy.Count == 0) { + ByteCode[] storedBy = load.VariablesBefore[variableIndex].StoredBy; + if (storedBy.Length == 0) { throw new Exception("Load of uninitialized variable"); - } else if (storedBy.Count == 1) { + } else if (storedBy.Length == 1) { VariableInfo newVar = newVars.Where(v => v.Stores.Contains(storedBy[0])).Single(); newVar.Loads.Add(load); } else { @@ -738,5 +741,16 @@ namespace Decompiler list.RemoveRange(start, count); return ret; } + + public static T[] Union(this T[] a, T[] b) + { + if (a.Length == 0) + return b; + if (b.Length == 0) + return a; + if (a.Length == 1 && b.Length == 1 && a[0].Equals(b[0])) + return a; + return Enumerable.Union(a, b).ToArray(); + } } } diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 58f54d05f..ef2c1a4b9 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -7,7 +7,7 @@ using Mono.Cecil; using Mono.Cecil.Cil; using Mono.CSharp; -namespace Decompiler.ControlFlow +namespace ICSharpCode.Decompiler.ILAst { public enum ILAstOptimizationStep { diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index ec5aeb2b1..be7b7b763 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; -using Decompiler.ControlFlow; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.NRefactory.Utils; @@ -12,7 +11,7 @@ using Mono.Cecil.Cil; using Mono.CSharp; using Cecil = Mono.Cecil; -namespace Decompiler +namespace ICSharpCode.Decompiler.ILAst { public abstract class ILNode { diff --git a/ICSharpCode.Decompiler/ILAst/ILCodes.cs b/ICSharpCode.Decompiler/ILAst/ILCodes.cs index 076fa0627..0304c3555 100644 --- a/ICSharpCode.Decompiler/ILAst/ILCodes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILCodes.cs @@ -27,7 +27,7 @@ using System; using Mono.Cecil; using Mono.Cecil.Cil; -namespace Decompiler +namespace ICSharpCode.Decompiler.ILAst { public enum ILCode { diff --git a/ICSharpCode.Decompiler/ILAst/ILInlining.cs b/ICSharpCode.Decompiler/ILAst/ILInlining.cs index 6ca8d90db..7ec19e7a2 100644 --- a/ICSharpCode.Decompiler/ILAst/ILInlining.cs +++ b/ICSharpCode.Decompiler/ILAst/ILInlining.cs @@ -5,7 +5,7 @@ using System; using System.Diagnostics; using System.Linq; -namespace Decompiler +namespace ICSharpCode.Decompiler.ILAst { /// /// Performs inlining transformations. diff --git a/ICSharpCode.Decompiler/ILAst/Pattern.cs b/ICSharpCode.Decompiler/ILAst/Pattern.cs index 075300766..72774823f 100644 --- a/ICSharpCode.Decompiler/ILAst/Pattern.cs +++ b/ICSharpCode.Decompiler/ILAst/Pattern.cs @@ -4,7 +4,7 @@ using System; using Mono.Cecil; -namespace Decompiler +namespace ICSharpCode.Decompiler.ILAst { public interface IVariablePattern { diff --git a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs index 50d8ae898..5dd6f98d4 100644 --- a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs +++ b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs @@ -2,23 +2,32 @@ // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; +using System.Diagnostics; using System.Linq; using ICSharpCode.NRefactory.Utils; using Mono.Cecil; -namespace Decompiler +namespace ICSharpCode.Decompiler.ILAst { public delegate void PeepholeTransform(ILBlock block, ref int i); /// /// Handles peephole transformations on the ILAst. /// - public static class PeepholeTransforms + public class PeepholeTransforms { + DecompilerContext context; + ILBlock method; + public static void Run(DecompilerContext context, ILBlock method) { + PeepholeTransforms transforms = new PeepholeTransforms(); + transforms.context = context; + transforms.method = method; + PeepholeTransform[] blockTransforms = { - ArrayInitializers.Transform(method) + ArrayInitializers.Transform(method), + transforms.CachedDelegateInitialization }; Func[] exprTransforms = { EliminateDups, @@ -43,6 +52,9 @@ namespace Decompiler // apply block transforms foreach (var t in blockTransforms) { t(block, ref i); + Debug.Assert(i <= block.Body.Count && i >= 0); + if (i == block.Body.Count) // special case: retry all transforms + break; } } } @@ -67,6 +79,7 @@ namespace Decompiler return expr; } + #region HandleDecimalConstants static ILExpression HandleDecimalConstants(ILExpression expr) { if (expr.Code == ILCode.Newobj) { @@ -105,5 +118,69 @@ namespace Decompiler else return null; } + #endregion + + #region CachedDelegateInitialization + void CachedDelegateInitialization(ILBlock block, ref int i) + { + // if (logicnot(brtrue(ldsfld(field)))) { + // stsfld(field, newobj(Action::.ctor, ldnull(), ldftn(method))) + // } else { + // } + // ...(..., ldsfld(field), ...) + + ILCondition c = block.Body[i] as ILCondition; + if (c == null || c.Condition == null && c.TrueBlock == null || c.FalseBlock == null) + return; + if (!(c.TrueBlock.Body.Count == 1 && c.FalseBlock.Body.Count == 0)) + return; + ILExpression condition = UnpackBrFalse(c.Condition); + if (condition == null || condition.Code != ILCode.Ldsfld) + return; + FieldDefinition field = condition.Operand as FieldDefinition; // field is defined in current assembly + if (field == null || !field.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) + return; + ILExpression stsfld = c.TrueBlock.Body[0] as ILExpression; + if (!(stsfld != null && stsfld.Code == ILCode.Stsfld && stsfld.Operand == field)) + return; + ILExpression newObj = stsfld.Arguments[0]; + if (!(newObj.Code == ILCode.Newobj && newObj.Arguments.Count == 2)) + return; + if (newObj.Arguments[0].Code != ILCode.Ldnull) + return; + if (newObj.Arguments[1].Code != ILCode.Ldftn) + return; + MethodDefinition anonymousMethod = newObj.Arguments[1].Operand as MethodDefinition; // method is defined in current assembly + if (!Ast.Transforms.DelegateConstruction.IsAnonymousMethod(context, anonymousMethod)) + return; + + ILExpression expr = block.Body.ElementAtOrDefault(i + 1) as ILExpression; + if (expr != null && expr.GetSelfAndChildrenRecursive().Count(e => e.Code == ILCode.Ldsfld && e.Operand == field) == 1) { + foreach (ILExpression parent in expr.GetSelfAndChildrenRecursive()) { + for (int j = 0; j < parent.Arguments.Count; j++) { + if (parent.Arguments[j].Code == ILCode.Ldsfld && parent.Arguments[j].Operand == field) { + parent.Arguments[j] = newObj; + block.Body.RemoveAt(i); + i -= ILInlining.InlineInto(block, i, method); + return; + } + } + } + } + } + + /// + /// Returns 'result' in brfalse(result) or logicnot(brtrue(result)). + /// + static ILExpression UnpackBrFalse(ILExpression condition) + { + if (condition.Code == ILCode.Brfalse) { + return condition.Arguments.Single(); + } else if (condition.Code == ILCode.LogicNot && condition.Arguments.Single().Code == ILCode.Brtrue) { + return condition.Arguments.Single().Arguments.Single(); + } + return null; + } + #endregion } } diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index 8f234ba7b..2a1adf314 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -5,11 +5,10 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using Decompiler; using Mono.Cecil; using Mono.Cecil.Cil; -namespace Decompiler +namespace ICSharpCode.Decompiler.ILAst { /// /// Assigns C# types to IL expressions. diff --git a/ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs b/ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs index 97d66e431..bec57df5f 100644 --- a/ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs +++ b/ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs @@ -11,7 +11,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("IL decompiler engine")] [assembly: AssemblyCompany("ic#code")] [assembly: AssemblyProduct("ILSpy")] -[assembly: AssemblyCopyright("Copyright 2011 AlphaSierraPara for the SharpDevelop Team")] +[assembly: AssemblyCopyright("Copyright 2011 AlphaSierraPapa for the SharpDevelop Team")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs b/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs index c1a13b925..792088222 100644 --- a/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs +++ b/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs @@ -1,12 +1,13 @@ using System; +using System.CodeDom.Compiler; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; -using Mono.Cecil; -using System.IO; -using Decompiler; + +using ICSharpCode.Decompiler.Ast; using Microsoft.CSharp; -using System.CodeDom.Compiler; +using Mono.Cecil; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests diff --git a/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs b/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs index 3267ac149..223247bf5 100644 --- a/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs @@ -59,4 +59,9 @@ public static class DelegateConstruction } return list; } + + public static Action StaticAnonymousMethodNoClosure() + { + return delegate { Console.WriteLine(); }; + } } diff --git a/ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs b/ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs index 00b5b4dba..1cec73efc 100644 --- a/ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs +++ b/ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs @@ -2,8 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Text; + +using ICSharpCode.Decompiler.Ast.Transforms; using ICSharpCode.NRefactory.CSharp; -using Decompiler.Transforms; namespace ICSharpCode.Decompiler.Tests.Helpers { diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs index 98e3daad7..f61a6f558 100644 --- a/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -6,7 +6,8 @@ using System.CodeDom.Compiler; using System.Collections.Generic; using System.IO; using System.Text; -using Decompiler; + +using ICSharpCode.Decompiler.Ast; using Microsoft.CSharp; using Mono.Cecil; diff --git a/ILSpy.sln b/ILSpy.sln index ff4c88aa1..74641ca8e 100644 --- a/ILSpy.sln +++ b/ILSpy.sln @@ -1,15 +1,15 @@  Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 -# SharpDevelop 4.1.0.7322-alpha +# SharpDevelop 4.1.0.7312-alpha Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Debugger", "Debugger", "{1DEB3B4E-03AC-437C-821D-B09FBFCC3E5B}" ProjectSection(SolutionItems) = postProject EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Debugger.Core", "Debugger\Debugger.Core\Debugger.Core.csproj", "{1D18D788-F7EE-4585-A23B-34DC8EC63CB8}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.Debugger", "Debugger\ILSpy.Debugger\ILSpy.Debugger.csproj", "{6D3D0F0D-348D-456A-A6ED-E9BD5EFABB6A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Debugger.Core", "Debugger\Debugger.Core\Debugger.Core.csproj", "{1D18D788-F7EE-4585-A23B-34DC8EC63CB8}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy", "ILSpy\ILSpy.csproj", "{1E85EFF9-E370-4683-83E4-8A3D063FF791}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.TreeView", "SharpTreeView\ICSharpCode.TreeView.csproj", "{DDE2A481-8271-4EAC-A330-8FA6A38D13D1}" @@ -109,7 +109,7 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {6D3D0F0D-348D-456A-A6ED-E9BD5EFABB6A} = {1DEB3B4E-03AC-437C-821D-B09FBFCC3E5B} {1D18D788-F7EE-4585-A23B-34DC8EC63CB8} = {1DEB3B4E-03AC-437C-821D-B09FBFCC3E5B} + {6D3D0F0D-348D-456A-A6ED-E9BD5EFABB6A} = {1DEB3B4E-03AC-437C-821D-B09FBFCC3E5B} EndGlobalSection EndGlobal diff --git a/ILSpy/AboutPage.cs b/ILSpy/AboutPage.cs index de8f7edd0..4eb58209d 100644 --- a/ILSpy/AboutPage.cs +++ b/ILSpy/AboutPage.cs @@ -3,6 +3,7 @@ using System; using System.ComponentModel; +using System.ComponentModel.Composition; using System.Diagnostics; using System.IO; using System.Linq; @@ -14,13 +15,23 @@ using System.Windows.Data; using System.Windows.Input; using System.Xml; using System.Xml.Linq; + using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.TextView; namespace ICSharpCode.ILSpy { - static class AboutPage + [ExportMainMenuCommand(Menu = "_Help", Header = "_About", MenuOrder = 99999)] + sealed class AboutPage : SimpleCommand { + [Import] + DecompilerTextView decompilerTextView = null; + + public override void Execute(object parameter) + { + Display(decompilerTextView); + } + static readonly Uri UpdateUrl = new Uri("http://www.ilspy.net/updates.xml"); static AvailableVersionInfo latestAvailableVersion; @@ -52,6 +63,8 @@ namespace ICSharpCode.ILSpy }; }); output.WriteLine(); + foreach (var plugin in App.CompositionContainer.GetExportedValues()) + plugin.Write(output); output.WriteLine(); using (Stream s = typeof(AboutPage).Assembly.GetManifestResourceStream(typeof(AboutPage), "README.txt")) { using (StreamReader r = new StreamReader(s)) { @@ -256,4 +269,12 @@ namespace ICSharpCode.ILSpy return tcs.Task; } } + + /// + /// Interface that allows plugins to extend the about page. + /// + public interface IAboutPageAddition + { + void Write(ISmartTextOutput textOutput); + } } diff --git a/ILSpy/App.xaml.cs b/ILSpy/App.xaml.cs index 39af423cb..9f9cac6eb 100644 --- a/ILSpy/App.xaml.cs +++ b/ILSpy/App.xaml.cs @@ -17,6 +17,8 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Generic; +using System.ComponentModel.Composition.Hosting; using System.Diagnostics; using System.Threading; using System.Windows; @@ -31,10 +33,24 @@ namespace ICSharpCode.ILSpy /// public partial class App : Application { + static CompositionContainer compositionContainer; + + public static CompositionContainer CompositionContainer { + get { return compositionContainer; } + } + public App() { InitializeComponent(); + var catalog = new AggregateCatalog(); + catalog.Catalogs.Add(new AssemblyCatalog(typeof(App).Assembly)); + catalog.Catalogs.Add(new DirectoryCatalog(".", "*.Plugin.dll")); + + compositionContainer = new CompositionContainer(catalog); + + Languages.Initialize(compositionContainer); + if (!Debugger.IsAttached) { AppDomain.CurrentDomain.UnhandledException += ShowErrorBox; Dispatcher.CurrentDispatcher.UnhandledException += Dispatcher_UnhandledException; diff --git a/ILSpy/AssemblyList.cs b/ILSpy/AssemblyList.cs index 7fa82293b..336d7c4f3 100644 --- a/ILSpy/AssemblyList.cs +++ b/ILSpy/AssemblyList.cs @@ -32,7 +32,7 @@ namespace ICSharpCode.ILSpy /// /// A list of assemblies. /// - sealed class AssemblyList + public sealed class AssemblyList { readonly string listName; @@ -81,7 +81,7 @@ namespace ICSharpCode.ILSpy /// /// Saves this assembly list to XML. /// - public XElement SaveAsXml() + internal XElement SaveAsXml() { return new XElement( "List", diff --git a/ILSpy/BamlDecompiler.cs b/ILSpy/BamlDecompiler.cs index df52eda97..66a38bbae 100644 --- a/ILSpy/BamlDecompiler.cs +++ b/ILSpy/BamlDecompiler.cs @@ -2,16 +2,23 @@ // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; +using System.ComponentModel.Composition; using System.IO; +using System.Linq; using System.Reflection; +using System.Threading.Tasks; using System.Windows.Baml2006; using System.Xaml; using System.Xml; -namespace ICSharpCode.ILSpy +using ICSharpCode.AvalonEdit.Highlighting; +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; + +namespace ICSharpCode.ILSpy.Baml { /// Caution: use in separate AppDomain only! - public class BamlDecompiler : MarshalByRefObject + sealed class BamlDecompiler : MarshalByRefObject { public BamlDecompiler() { @@ -60,4 +67,85 @@ namespace ICSharpCode.ILSpy return w.ToString(); } } + + [Export(typeof(IResourceNodeFactory))] + sealed class BamlResourceNodeFactory : IResourceNodeFactory + { + public ILSpyTreeNode CreateNode(Mono.Cecil.Resource resource) + { + return null; + } + + public ILSpyTreeNode CreateNode(string key, Stream data) + { + if (key.EndsWith(".baml", StringComparison.OrdinalIgnoreCase)) + return new BamlResourceEntryNode(key, data); + else + return null; + } + } + + sealed class BamlResourceEntryNode : ResourceEntryNode + { + public BamlResourceEntryNode(string key, Stream data) : base(key, data) + { + } + + internal override bool View(DecompilerTextView textView) + { + AvalonEditTextOutput output = new AvalonEditTextOutput(); + IHighlightingDefinition highlighting = null; + + textView.RunWithCancellation( + token => Task.Factory.StartNew( + () => { + try { + if (LoadBaml(output)) + highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml"); + } catch (Exception ex) { + output.Write(ex.ToString()); + } + return output; + }), + t => textView.Show(t.Result, highlighting) + ); + return true; + } + + bool LoadBaml(AvalonEditTextOutput output) + { + var asm = this.Ancestors().OfType().FirstOrDefault().LoadedAssembly; + + AppDomain bamlDecompilerAppDomain = null; + try { + BamlDecompiler decompiler = CreateBamlDecompilerInAppDomain(ref bamlDecompilerAppDomain, asm.FileName); + + MemoryStream bamlStream = new MemoryStream(); + data.Position = 0; + data.CopyTo(bamlStream); + + output.Write(decompiler.DecompileBaml(bamlStream, asm.FileName)); + return true; + } finally { + if (bamlDecompilerAppDomain != null) + AppDomain.Unload(bamlDecompilerAppDomain); + } + } + + public static BamlDecompiler CreateBamlDecompilerInAppDomain(ref AppDomain appDomain, string assemblyFileName) + { + if (appDomain == null) { + // Construct and initialize settings for a second AppDomain. + AppDomainSetup bamlDecompilerAppDomainSetup = new AppDomainSetup(); + bamlDecompilerAppDomainSetup.ApplicationBase = "file:///" + Path.GetDirectoryName(assemblyFileName); + bamlDecompilerAppDomainSetup.DisallowBindingRedirects = false; + bamlDecompilerAppDomainSetup.DisallowCodeDownload = true; + bamlDecompilerAppDomainSetup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile; + + // Create the second AppDomain. + appDomain = AppDomain.CreateDomain("BamlDecompiler AD", null, bamlDecompilerAppDomainSetup); + } + return (BamlDecompiler)appDomain.CreateInstanceFromAndUnwrap(typeof(BamlDecompiler).Assembly.Location, typeof(BamlDecompiler).FullName); + } + } } \ No newline at end of file diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/CSharpLanguage.cs index 4c6adbd2f..c01caaf1c 100644 --- a/ILSpy/CSharpLanguage.cs +++ b/ILSpy/CSharpLanguage.cs @@ -19,16 +19,16 @@ using System; using System.Collections; using System.Collections.Generic; +using System.ComponentModel.Composition; using System.IO; using System.Linq; using System.Resources; using System.Threading.Tasks; using System.Xaml; using System.Xml; - -using Decompiler; -using Decompiler.Transforms; using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Ast; +using ICSharpCode.Decompiler.Ast.Transforms; using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; @@ -37,6 +37,7 @@ namespace ICSharpCode.ILSpy /// /// Decompiler logic for C#. /// + [Export(typeof(Language))] public class CSharpLanguage : Language { string name = "C#"; @@ -323,7 +324,7 @@ namespace ICSharpCode.ILSpy if (fileName.EndsWith(".baml", StringComparison.OrdinalIgnoreCase)) { MemoryStream ms = new MemoryStream(); entryStream.CopyTo(ms); - BamlDecompiler decompiler = TreeNodes.ResourceEntryNode.CreateBamlDecompilerInAppDomain(ref bamlDecompilerAppDomain, assemblyFileName); + var decompiler = Baml.BamlResourceEntryNode.CreateBamlDecompilerInAppDomain(ref bamlDecompilerAppDomain, assemblyFileName); string xaml = null; try { xaml = decompiler.DecompileBaml(ms, assemblyFileName); diff --git a/ILSpy/Commands.cs b/ILSpy/Commands.cs new file mode 100644 index 000000000..462fa1602 --- /dev/null +++ b/ILSpy/Commands.cs @@ -0,0 +1,122 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.ComponentModel.Composition; +using System.Linq; +using System.Windows.Input; + +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; + +namespace ICSharpCode.ILSpy +{ + [ExportMainMenuCommand(Menu = "_File", Header = "E_xit", MenuOrder = 99999, MenuCategory = "Exit")] + sealed class ExitCommand : SimpleCommand + { + public override void Execute(object parameter) + { + MainWindow.Instance.Close(); + } + } + + [ExportToolbarCommand(ToolTip = "Back", ToolbarIcon = "Images/Back.png", ToolbarCategory = "Navigation", ToolbarOrder = 0)] + sealed class BrowseBackCommand : CommandWrapper { + public BrowseBackCommand() : base(NavigationCommands.BrowseBack) {} + } + + [ExportToolbarCommand(ToolTip = "Forward", ToolbarIcon = "Images/Forward.png", ToolbarCategory = "Navigation", ToolbarOrder = 1)] + sealed class BrowseForwardCommand : CommandWrapper { + public BrowseForwardCommand() : base(NavigationCommands.BrowseForward) {} + } + + [ExportToolbarCommand(ToolTip = "Open", ToolbarIcon = "Images/Open.png", ToolbarCategory = "Open", ToolbarOrder = 0)] + [ExportMainMenuCommand(Menu = "_File", MenuIcon = "Images/Open.png", MenuCategory = "Open", MenuOrder = 0)] + sealed class OpenCommand : CommandWrapper { + public OpenCommand() : base(ApplicationCommands.Open) {} + } + + [ExportMainMenuCommand(Menu = "_File", Header = "Open from _GAC", MenuCategory = "Open", MenuOrder = 1)] + sealed class OpenFromGacCommand : SimpleCommand + { + public override void Execute(object parameter) + { + OpenFromGacDialog dlg = new OpenFromGacDialog(); + dlg.Owner = MainWindow.Instance; + if (dlg.ShowDialog() == true) { + MainWindow.Instance.OpenFiles(dlg.SelectedFileNames); + } + } + } + + [ExportToolbarCommand(ToolTip = "Reload all assemblies", ToolbarIcon = "Images/Refresh.png", ToolbarCategory = "Open", ToolbarOrder = 2)] + [ExportMainMenuCommand(Menu = "_File", Header = "Reload", MenuIcon = "Images/Refresh.png", MenuCategory = "Open", MenuOrder = 2)] + sealed class RefreshCommand : CommandWrapper { + public RefreshCommand() : base(NavigationCommands.Refresh) {} + } + + [ExportMainMenuCommand(Menu = "_File", Header = "_Save Code...", MenuIcon = "Images/SaveFile.png", MenuCategory = "Save", MenuOrder = 0)] + sealed class SaveCommand : SimpleCommand + { + public override void Execute(object parameter) + { + MainWindow mainWindow = MainWindow.Instance; + if (mainWindow.SelectedNodes.Count() == 1) { + if (mainWindow.SelectedNodes.Single().Save(mainWindow.TextView)) + return; + } + mainWindow.TextView.SaveToDisk(mainWindow.CurrentLanguage, + mainWindow.SelectedNodes, + new DecompilationOptions() { FullDecompilation = true }); + } + } + + class CommandWrapper : ICommand + { + ICommand wrappedCommand; + + public CommandWrapper(ICommand wrappedCommand) + { + this.wrappedCommand = wrappedCommand; + } + + public static ICommand Unwrap(ICommand command) + { + CommandWrapper w = command as CommandWrapper; + if (w != null) + return w.wrappedCommand; + else + return command; + } + + public event EventHandler CanExecuteChanged { + add { wrappedCommand.CanExecuteChanged += value; } + remove { wrappedCommand.CanExecuteChanged -= value; } + } + + public void Execute(object parameter) + { + wrappedCommand.Execute(parameter); + } + + public bool CanExecute(object parameter) + { + return wrappedCommand.CanExecute(parameter); + } + } + + public abstract class SimpleCommand : ICommand + { + public event EventHandler CanExecuteChanged { + add { CommandManager.RequerySuggested += value; } + remove { CommandManager.RequerySuggested -= value; } + } + + public abstract void Execute(object parameter); + + public virtual bool CanExecute(object parameter) + { + return true; + } + } +} diff --git a/ILSpy/Commands/RoutedUICommands.cs b/ILSpy/Commands/RoutedUICommands.cs deleted file mode 100644 index 31b71f029..000000000 --- a/ILSpy/Commands/RoutedUICommands.cs +++ /dev/null @@ -1,56 +0,0 @@ -// 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.Windows.Input; - -namespace ICSharpCode.ILSpy.Commands -{ - public static class RoutedUICommands - { - static RoutedUICommands() - { - AttachToProcess = new RoutedUICommand("Attach to running process...", "AttachToProcess", typeof(RoutedUICommands)); - DetachFromProcess = new RoutedUICommand("Detach from process...", "DetachFromProcess", typeof(RoutedUICommands)); - ContinueDebugging = new RoutedUICommand("Continue debugging", "ContinueDebugging", typeof(RoutedUICommands)); - StepInto = new RoutedUICommand("Step into", "StepInto", typeof(RoutedUICommands)); - StepOver = new RoutedUICommand("Step over", "StepOver", typeof(RoutedUICommands)); - StepOut = new RoutedUICommand("Step out", "StepOut", typeof(RoutedUICommands)); - - RemoveAllBreakpoint = new RoutedUICommand("Remove all breakpoints", "RemoveAllBreakpoint", typeof(RoutedUICommands)); - - DebugExecutable = new RoutedUICommand("Debug an executable", "DebugExecutable", typeof(RoutedUICommands)); - } - - public static RoutedUICommand AttachToProcess { get; private set; } - - public static RoutedUICommand DetachFromProcess { get; private set; } - - public static RoutedUICommand ContinueDebugging { get; private set; } - - public static RoutedUICommand StepInto { get; private set; } - - public static RoutedUICommand StepOver { get; private set; } - - public static RoutedUICommand StepOut { get; private set; } - - public static RoutedUICommand RemoveAllBreakpoint { get; private set; } - - public static RoutedUICommand DebugExecutable { get; private set; } - } -} diff --git a/ILSpy/ExportCommandAttribute.cs b/ILSpy/ExportCommandAttribute.cs new file mode 100644 index 000000000..b83fd698f --- /dev/null +++ b/ILSpy/ExportCommandAttribute.cs @@ -0,0 +1,63 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.ComponentModel.Composition; +using System.Windows.Input; + +namespace ICSharpCode.ILSpy +{ + #region Toolbar + public interface IToolbarCommandMetadata + { + string ToolbarIcon { get; } + string ToolTip { get; } + string ToolbarCategory { get; } + + double ToolbarOrder { get; } + } + + [MetadataAttribute] + [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)] + public class ExportToolbarCommandAttribute : ExportAttribute + { + public ExportToolbarCommandAttribute() + : base("ToolbarCommand", typeof(ICommand)) + { + } + + public string ToolTip { get; set; } + public string ToolbarIcon { get; set; } + public string ToolbarCategory { get; set; } + public double ToolbarOrder { get; set; } + } + #endregion + + #region Main Menu + public interface IMainMenuCommandMetadata + { + string MenuIcon { get; } + string Header { get; } + string Menu { get; } + string MenuCategory { get; } + + double MenuOrder { get; } + } + + [MetadataAttribute] + [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)] + public class ExportMainMenuCommandAttribute : ExportAttribute + { + public ExportMainMenuCommandAttribute() + : base("MainMenuCommand", typeof(ICommand)) + { + } + + public string MenuIcon { get; set; } + public string Header { get; set; } + public string Menu { get; set; } + public string MenuCategory { get; set; } + public double MenuOrder { get; set; } + } + #endregion +} diff --git a/ILSpy/GacInterop.cs b/ILSpy/GacInterop.cs index 4a0863d3c..9ab255f59 100644 --- a/ILSpy/GacInterop.cs +++ b/ILSpy/GacInterop.cs @@ -28,7 +28,7 @@ namespace ICSharpCode.ILSpy /// /// Interop with the .NET GAC. /// - public static class GacInterop + static class GacInterop { /// /// Gets the names of all assemblies in the GAC. diff --git a/ILSpy/GuessFileType.cs b/ILSpy/GuessFileType.cs index b27597337..72a38f210 100644 --- a/ILSpy/GuessFileType.cs +++ b/ILSpy/GuessFileType.cs @@ -12,7 +12,7 @@ namespace ICSharpCode.ILSpy /// /// Static methods for determining the type of a file. /// - public static class GuessFileType + static class GuessFileType { public static FileType DetectFileType(Stream stream) { @@ -120,7 +120,7 @@ namespace ICSharpCode.ILSpy } } - public enum FileType + enum FileType { Binary, Text, diff --git a/ILSpy/ILAstLanguage.cs b/ILSpy/ILAstLanguage.cs index 8a7ae6f8e..1db27d100 100644 --- a/ILSpy/ILAstLanguage.cs +++ b/ILSpy/ILAstLanguage.cs @@ -20,11 +20,10 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using Decompiler; -using Decompiler.ControlFlow; -using Decompiler.Transforms; + using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; +using ICSharpCode.Decompiler.ILAst; using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index aae49e8c0..f94a0a64e 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -56,6 +56,9 @@ 3.0 + + 4.0 + 3.5 @@ -86,11 +89,12 @@ - + + @@ -157,6 +161,7 @@ + diff --git a/ILSpy/Images/Images.cs b/ILSpy/Images/Images.cs index b144b6ef9..dfe0f681a 100644 --- a/ILSpy/Images/Images.cs +++ b/ILSpy/Images/Images.cs @@ -68,5 +68,17 @@ namespace ICSharpCode.ILSpy public static readonly BitmapImage Delete = LoadBitmap("Delete"); public static readonly BitmapImage Search = LoadBitmap("Search"); + + public static BitmapImage LoadImage(object part, string icon) + { + Uri uri; + if (part.GetType().Assembly == typeof(Images).Assembly) + uri = new Uri("pack://application:,,,/" + icon); + else + throw new NotImplementedException(); + BitmapImage image = new BitmapImage(uri); + image.Freeze(); + return image; + } } } diff --git a/ILSpy/Language.cs b/ILSpy/Language.cs index 3bce054af..238e87e63 100644 --- a/ILSpy/Language.cs +++ b/ILSpy/Language.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel.Composition.Hosting; using System.Linq; using ICSharpCode.Decompiler; using Mono.Cecil; @@ -121,19 +122,29 @@ namespace ICSharpCode.ILSpy public static class Languages { + static ReadOnlyCollection allLanguages; + /// /// A list of all languages. /// - public static readonly ReadOnlyCollection AllLanguages = new List( - new Language[] { - new CSharpLanguage(), - new ILLanguage(true) + public static ReadOnlyCollection AllLanguages { + get { + return allLanguages; } + } + + + internal static void Initialize(CompositionContainer composition) + { + List languages = new List(); + languages.AddRange(composition.GetExportedValues()); + languages.Add(new ILLanguage(true)); #if DEBUG - .Concat(ILAstLanguage.GetDebugLanguages()) - .Concat(CSharpLanguage.GetDebugLanguages()) + languages.AddRange(ILAstLanguage.GetDebugLanguages()); + languages.AddRange(CSharpLanguage.GetDebugLanguages()); #endif - ).AsReadOnly(); + allLanguages = languages.AsReadOnly(); + } /// /// Gets a language using its name. diff --git a/ILSpy/LoadedAssembly.cs b/ILSpy/LoadedAssembly.cs index 6e9752b1b..81f46e142 100644 --- a/ILSpy/LoadedAssembly.cs +++ b/ILSpy/LoadedAssembly.cs @@ -14,7 +14,7 @@ namespace ICSharpCode.ILSpy /// /// Represents an assembly loaded into ILSpy. /// - sealed class LoadedAssembly + public sealed class LoadedAssembly { readonly Task assemblyTask; readonly AssemblyList assemblyList; diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index 5ca57c941..ce0f06af6 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -1,11 +1,10 @@  - - - - - - - - @@ -58,28 +33,8 @@ - - - - - - - - - - - - - - - - - - - - - - + + @@ -88,52 +43,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + @@ -174,13 +73,6 @@ SelectedItem="{Binding FilterSettings.Language}" SelectionChanged="LanguageComboBox_SelectionChanged" /> - - - @@ -222,7 +114,7 @@ VerticalAlignment="Stretch" BorderBrush="Transparent" /> - + @@ -243,7 +135,7 @@ - + 0 && sessionSettings.SplitterPosition < 1) { leftColumn.Width = new GridLength(sessionSettings.SplitterPosition, GridUnitType.Star); @@ -91,9 +99,89 @@ namespace ICSharpCode.ILSpy } sessionSettings.FilterSettings.PropertyChanged += filterSettings_PropertyChanged; + InitMainMenu(); + InitToolbar(); + this.Loaded += new RoutedEventHandler(MainWindow_Loaded); } - + + #region Toolbar extensibility + [ImportMany("ToolbarCommand", typeof(ICommand))] + Lazy[] toolbarCommands = null; + + void InitToolbar() + { + int navigationPos = 0; + int openPos = 1; + foreach (var commandGroup in toolbarCommands.OrderBy(c => c.Metadata.ToolbarOrder).GroupBy(c => c.Metadata.ToolbarCategory)) { + if (commandGroup.Key == "Navigation") { + foreach (var command in commandGroup) { + toolBar.Items.Insert(navigationPos++, MakeToolbarItem(command)); + openPos++; + } + } else if (commandGroup.Key == "Open") { + foreach (var command in commandGroup) { + toolBar.Items.Insert(openPos++, MakeToolbarItem(command)); + } + } else { + toolBar.Items.Add(new Separator()); + foreach (var command in commandGroup) { + toolBar.Items.Add(MakeToolbarItem(command)); + } + } + } + + } + + Button MakeToolbarItem(Lazy command) + { + return new Button { + Command = CommandWrapper.Unwrap(command.Value), + ToolTip = command.Metadata.ToolTip, + Content = new Image { + Width = 16, + Height = 16, + Source = Images.LoadImage(command.Value, command.Metadata.ToolbarIcon) + } + }; + } + #endregion + + #region Main Menu extensibility + [ImportMany("MainMenuCommand", typeof(ICommand))] + Lazy[] mainMenuCommands = null; + + void InitMainMenu() + { + foreach (var topLevelMenu in mainMenuCommands.OrderBy(c => c.Metadata.MenuOrder).GroupBy(c => c.Metadata.Menu)) { + var topLevelMenuItem = mainMenu.Items.OfType().FirstOrDefault(m => (m.Header as string) == topLevelMenu.Key); + foreach (var category in topLevelMenu.GroupBy(c => c.Metadata.MenuCategory)) { + if (topLevelMenuItem == null) { + topLevelMenuItem = new MenuItem(); + topLevelMenuItem.Header = topLevelMenu.Key; + mainMenu.Items.Add(topLevelMenuItem); + } else if (topLevelMenuItem.Items.Count > 0) { + topLevelMenuItem.Items.Add(new Separator()); + } + foreach (var entry in category) { + MenuItem menuItem = new MenuItem(); + menuItem.Command = CommandWrapper.Unwrap(entry.Value); + if (!string.IsNullOrEmpty(entry.Metadata.Header)) + menuItem.Header = entry.Metadata.Header; + if (!string.IsNullOrEmpty(entry.Metadata.MenuIcon)) { + menuItem.Icon = new Image { + Width = 16, + Height = 16, + Source = Images.LoadImage(entry.Value, entry.Metadata.MenuIcon) + }; + } + topLevelMenuItem.Items.Add(menuItem); + } + } + } + } + #endregion + void MainWindow_Loaded(object sender, RoutedEventArgs e) { ILSpySettings spySettings = this.spySettings; @@ -307,20 +395,19 @@ namespace ICSharpCode.ILSpy } } - void OpenFiles(string[] fileNames, bool focusNode = true) + public void OpenFiles(string[] fileNames) { - if (focusNode) { - treeView.UnselectAll(); - - SharpTreeNode lastNode = null; - foreach (string file in fileNames) { - var asm = assemblyList.OpenAssembly(file); - if (asm != null) { - var node = assemblyListTreeNode.FindAssemblyNode(asm); - if (node != null) { - treeView.SelectedItems.Add(node); - lastNode = node; - } + if (fileNames == null) + throw new ArgumentNullException("fileNames"); + treeView.UnselectAll(); + SharpTreeNode lastNode = null; + foreach (string file in fileNames) { + var asm = assemblyList.OpenAssembly(file); + if (asm != null) { + var node = assemblyListTreeNode.FindAssemblyNode(asm); + if (node != null) { + treeView.SelectedItems.Add(node); + lastNode = node; } } if (lastNode != null) @@ -328,18 +415,9 @@ namespace ICSharpCode.ILSpy } } - void OpenFromGac_Click(object sender, RoutedEventArgs e) - { - OpenFromGacDialog dlg = new OpenFromGacDialog(); - dlg.Owner = this; - if (dlg.ShowDialog() == true) { - OpenFiles(dlg.SelectedFileNames); - } - } - void RefreshCommandExecuted(object sender, ExecutedRoutedEventArgs e) { - if (!DebuggerService.CurrentDebugger.IsDebugging) { + if (!System.Diagnostics.Debugger.IsAttached) { e.Handled = true; var path = GetPathForNode(treeView.SelectedItem as SharpTreeNode); ShowAssemblyList(assemblyListManager.LoadList(ILSpySettings.Load(), assemblyList.ListName)); @@ -349,197 +427,7 @@ namespace ICSharpCode.ILSpy #endregion - #region Debugger commands - - [System.Runtime.InteropServices.DllImport("user32.dll")] - static extern bool SetWindowPos( - IntPtr hWnd, - IntPtr hWndInsertAfter, - int X, - int Y, - int cx, - int cy, - uint uFlags); - - const UInt32 SWP_NOSIZE = 0x0001; - const UInt32 SWP_NOMOVE = 0x0002; - - static readonly IntPtr HWND_BOTTOM = new IntPtr(1); - static readonly IntPtr HWND_TOP = new IntPtr(0); - - static void SendWpfWindowPos(Window window, IntPtr place) - { - var hWnd = new WindowInteropHelper(window).Handle; - SetWindowPos(hWnd, place, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); - } - - IDebugger CurrentDebugger { - get { - return DebuggerService.CurrentDebugger; - } - } - - void StartDebugging(Process process) - { - CurrentDebugger.Attach(process); - EnableDebuggerUI(false); - CurrentDebugger.DebugStopped += OnDebugStopped; - CurrentDebugger.IsProcessRunningChanged += CurrentDebugger_IsProcessRunningChanged; - } - - void CurrentDebugger_IsProcessRunningChanged(object sender, EventArgs e) - { - if (CurrentDebugger.IsProcessRunning) { - //SendWpfWindowPos(this, HWND_BOTTOM); - return; - } - - // breakpoint was hit => bring to front the main window - SendWpfWindowPos(this, HWND_TOP); - this.Activate(); - - // jump to type & expand folding - if (CurrentLineBookmark.Instance != null) { - if (CurrentLineBookmark.Instance.Type != DebuggedData.CurrentType) - JumpToReference(CurrentLineBookmark.Instance.Type); - - decompilerTextView.UnfoldAndScroll(CurrentLineBookmark.Instance.LineNumber); - } - } - - void DebugExecutableExecuted(object sender, ExecutedRoutedEventArgs e) - { - OpenFileDialog dialog = new OpenFileDialog() { - Filter = ".NET Executable (*.exe) | *.exe", - RestoreDirectory = true, - DefaultExt = "exe" - }; - - if (dialog.ShowDialog() == true) { - string fileName = dialog.FileName; - - // add it to references - OpenFiles(new [] { fileName }, false); - - if (!CurrentDebugger.IsDebugging) { - // execute the process - this.StartDebugging(Process.Start(fileName)); - } - } - } - - void AttachToProcessExecuted(object sender, ExecutedRoutedEventArgs e) - { - if (!CurrentDebugger.IsDebugging) { - var window = new AttachToProcessWindow { Owner = this }; - if (window.ShowDialog() == true) { - this.StartDebugging(window.SelectedProcess); - } - } - } - - void OnDebugStopped(object sender, EventArgs e) - { - EnableDebuggerUI(true); - CurrentDebugger.DebugStopped -= OnDebugStopped; - CurrentDebugger.IsProcessRunningChanged -= CurrentDebugger_IsProcessRunningChanged; - } - - void DetachFromProcessExecuted(object sender, ExecutedRoutedEventArgs e) - { - if (CurrentDebugger.IsDebugging){ - CurrentDebugger.Detach(); - - EnableDebuggerUI(true); - CurrentDebugger.DebugStopped -= OnDebugStopped; - } - } - - void ContinueDebuggingExecuted(object sender, ExecutedRoutedEventArgs e) - { - if (CurrentDebugger.IsDebugging && !CurrentDebugger.IsProcessRunning) - CurrentDebugger.Continue(); - } - - void StepIntoExecuted(object sender, ExecutedRoutedEventArgs e) - { - if (CurrentDebugger.IsDebugging && !CurrentDebugger.IsProcessRunning) - CurrentDebugger.StepInto(); - } - - void StepOverExecuted(object sender, ExecutedRoutedEventArgs e) - { - if (CurrentDebugger.IsDebugging && !CurrentDebugger.IsProcessRunning) - CurrentDebugger.StepOver(); - } - - void StepOutExecuted(object sender, ExecutedRoutedEventArgs e) - { - if (CurrentDebugger.IsDebugging && !CurrentDebugger.IsProcessRunning) - CurrentDebugger.StepOut(); - } - - void RemoveAllBreakpointExecuted(object sender, ExecutedRoutedEventArgs e) - { - for (int i = BookmarkManager.Bookmarks.Count - 1; i >= 0; --i) { - var bookmark = BookmarkManager.Bookmarks[i]; - if (bookmark is BreakpointBookmark) { - BookmarkManager.RemoveMark(bookmark); - } - } - } - - protected override void OnKeyUp(KeyEventArgs e) - { - switch (e.Key) { - case Key.F5: - ContinueDebuggingExecuted(null, null); - e.Handled = true; - break; - case Key.System: - StepOverExecuted(null, null); - e.Handled = true; - break; - case Key.F11: - StepIntoExecuted(null, null); - e.Handled = true; - break; - default: - // do nothing - break; - } - - base.OnKeyUp(e); - } - - void EnableDebuggerUI(bool enable) - { - AttachMenuItem.IsEnabled = AttachButton.IsEnabled = enable; - DebugExecutableButton.IsEnabled = DebugExecutableItem.IsEnabled = enable; - - ContinueDebuggingMenuItem.IsEnabled = - StepIntoMenuItem.IsEnabled = - StepOverMenuItem.IsEnabled = - StepOutMenuItem.IsEnabled = - DetachMenuItem.IsEnabled = !enable; - } - - #endregion - - #region Exit/About - void ExitClick(object sender, RoutedEventArgs e) - { - Close(); - } - - void AboutClick(object sender, RoutedEventArgs e) - { - treeView.UnselectAll(); - AboutPage.Display(decompilerTextView); - } - #endregion - - #region Decompile / Save + #region Decompile (TreeView_SelectionChanged) void TreeView_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (treeView.SelectedItems.Count == 1) { @@ -547,21 +435,23 @@ namespace ICSharpCode.ILSpy if (node != null && node.View(decompilerTextView)) return; } - decompilerTextView.Decompile(sessionSettings.FilterSettings.Language, - treeView.GetTopLevelSelection().OfType(), - new DecompilationOptions()); + decompilerTextView.Decompile(this.CurrentLanguage, this.SelectedNodes, new DecompilationOptions()); } - void saveCode_Click(object sender, RoutedEventArgs e) - { - if (treeView.SelectedItems.Count == 1) { - ILSpyTreeNode node = treeView.SelectedItem as ILSpyTreeNode; - if (node != null && node.Save(decompilerTextView)) - return; + public DecompilerTextView TextView { + get { return decompilerTextView; } + } + + public Language CurrentLanguage { + get { + return sessionSettings.FilterSettings.Language; + } + } + + public IEnumerable SelectedNodes { + get { + return treeView.GetTopLevelSelection().OfType(); } - decompilerTextView.SaveToDisk(sessionSettings.FilterSettings.Language, - treeView.GetTopLevelSelection().OfType(), - new DecompilationOptions() { FullDecompilation = true }); } #endregion diff --git a/ILSpy/NavigationHistory.cs b/ILSpy/NavigationHistory.cs index 521780af1..f15cfa732 100644 --- a/ILSpy/NavigationHistory.cs +++ b/ILSpy/NavigationHistory.cs @@ -10,7 +10,7 @@ namespace ICSharpCode.ILSpy /// /// Stores the navigation history. /// - public class NavigationHistory + sealed class NavigationHistory { List back = new List(); List forward = new List(); diff --git a/ILSpy/Properties/AssemblyInfo.template.cs b/ILSpy/Properties/AssemblyInfo.template.cs index 301312f7d..b6c0c3fd9 100644 --- a/ILSpy/Properties/AssemblyInfo.template.cs +++ b/ILSpy/Properties/AssemblyInfo.template.cs @@ -14,7 +14,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription(".NET assembly inspector and decompiler")] [assembly: AssemblyCompany("ic#code")] [assembly: AssemblyProduct("ILSpy")] -[assembly: AssemblyCopyright("Copyright 2011 AlphaSierraPara for the SharpDevelop Team")] +[assembly: AssemblyCopyright("Copyright 2011 AlphaSierraPapa for the SharpDevelop Team")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/ILSpy/TextView/AvalonEditTextOutput.cs b/ILSpy/TextView/AvalonEditTextOutput.cs index 500501a06..fe7d46846 100644 --- a/ILSpy/TextView/AvalonEditTextOutput.cs +++ b/ILSpy/TextView/AvalonEditTextOutput.cs @@ -62,7 +62,7 @@ namespace ICSharpCode.ILSpy.TextView /// /// Text output implementation for AvalonEdit. /// - sealed class AvalonEditTextOutput : ISmartTextOutput + public sealed class AvalonEditTextOutput : ISmartTextOutput { readonly StringBuilder b = new StringBuilder(); @@ -78,13 +78,12 @@ namespace ICSharpCode.ILSpy.TextView Stack openFoldings = new Stack(); /// List of all foldings that were written to the output - public readonly List Foldings = new List(); + internal readonly List Foldings = new List(); - - public readonly DefinitionLookup DefinitionLookup = new DefinitionLookup(); + internal readonly DefinitionLookup DefinitionLookup = new DefinitionLookup(); /// Embedded UIElements, see . - public readonly List>> UIElements = new List>>(); + internal readonly List>> UIElements = new List>>(); public AvalonEditTextOutput() { @@ -94,7 +93,7 @@ namespace ICSharpCode.ILSpy.TextView /// /// Gets the list of references (hyperlinks). /// - public TextSegmentCollection References { + internal TextSegmentCollection References { get { return references; } } diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 748af1f36..0d3b8266a 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Globalization; using System.IO; @@ -54,12 +55,12 @@ namespace ICSharpCode.ILSpy.TextView /// Manages the TextEditor showing the decompiled code. /// Contains all the threading logic that makes the decompiler work in the background. /// - sealed partial class DecompilerTextView : UserControl + [Export, PartCreationPolicy(CreationPolicy.Shared)] + public sealed partial class DecompilerTextView : UserControl { readonly ReferenceElementGenerator referenceElementGenerator; readonly UIElementGenerator uiElementGenerator; FoldingManager foldingManager; - internal MainWindow mainWindow; DefinitionLookup definitionLookup; CancellationTokenSource currentCancellationTokenSource; @@ -515,7 +516,7 @@ namespace ICSharpCode.ILSpy.TextView return; } } - mainWindow.JumpToReference(reference); + MainWindow.Instance.JumpToReference(reference); } /// diff --git a/ILSpy/TextView/DecompilerTextView.xaml b/ILSpy/TextView/DecompilerTextView.xaml index 161d8c8d5..3d99ced8e 100644 --- a/ILSpy/TextView/DecompilerTextView.xaml +++ b/ILSpy/TextView/DecompilerTextView.xaml @@ -1,4 +1,4 @@ - diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzerTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzerTreeNode.cs index e91b048d8..8ab995e39 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzerTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzerTreeNode.cs @@ -22,7 +22,7 @@ using ICSharpCode.TreeView; namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { - class AnalyzerTreeNode : SharpTreeNode + public class AnalyzerTreeNode : SharpTreeNode { Language language; diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index 35865ef2d..1123ee219 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -38,7 +38,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// Tree node representing an assembly. /// This class is responsible for loading both namespace and type nodes. /// - sealed class AssemblyTreeNode : ILSpyTreeNode + public sealed class AssemblyTreeNode : ILSpyTreeNode { readonly LoadedAssembly assembly; readonly List classes = new List(); diff --git a/ILSpy/TreeNodes/EventTreeNode.cs b/ILSpy/TreeNodes/EventTreeNode.cs index 9d75b5215..deed40194 100644 --- a/ILSpy/TreeNodes/EventTreeNode.cs +++ b/ILSpy/TreeNodes/EventTreeNode.cs @@ -25,7 +25,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// Represents an event in the TreeView. /// - sealed class EventTreeNode : ILSpyTreeNode + public sealed class EventTreeNode : ILSpyTreeNode { readonly EventDefinition ev; diff --git a/ILSpy/TreeNodes/FieldTreeNode.cs b/ILSpy/TreeNodes/FieldTreeNode.cs index d3becb0ce..91f7f31cb 100644 --- a/ILSpy/TreeNodes/FieldTreeNode.cs +++ b/ILSpy/TreeNodes/FieldTreeNode.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// Represents a field in the TreeView. /// - sealed class FieldTreeNode : ILSpyTreeNode + public sealed class FieldTreeNode : ILSpyTreeNode { readonly FieldDefinition field; diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index 960e32203..0ead7be94 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -29,7 +29,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// Base class of all ILSpy tree nodes. /// - abstract class ILSpyTreeNode : SharpTreeNode + public abstract class ILSpyTreeNode : SharpTreeNode { FilterSettings filterSettings; bool childrenNeedFiltering; @@ -166,7 +166,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - enum FilterResult + public enum FilterResult { /// /// Hides the node. diff --git a/ILSpy/TreeNodes/MethodTreeNode.cs b/ILSpy/TreeNodes/MethodTreeNode.cs index 7c015d5fd..9f7898dbc 100644 --- a/ILSpy/TreeNodes/MethodTreeNode.cs +++ b/ILSpy/TreeNodes/MethodTreeNode.cs @@ -29,7 +29,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// Tree Node representing a field, method, property, or event. /// - sealed class MethodTreeNode : ILSpyTreeNode + public sealed class MethodTreeNode : ILSpyTreeNode { MethodDefinition method; diff --git a/ILSpy/TreeNodes/NamespaceTreeNode.cs b/ILSpy/TreeNodes/NamespaceTreeNode.cs index 69810d852..054b4bf74 100644 --- a/ILSpy/TreeNodes/NamespaceTreeNode.cs +++ b/ILSpy/TreeNodes/NamespaceTreeNode.cs @@ -25,7 +25,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// Namespace node. The loading of the type nodes is handled by the parent AssemblyTreeNode. /// - sealed class NamespaceTreeNode : ILSpyTreeNode + public sealed class NamespaceTreeNode : ILSpyTreeNode { string name; diff --git a/ILSpy/TreeNodes/PropertyTreeNode.cs b/ILSpy/TreeNodes/PropertyTreeNode.cs index b815524ca..340ac9f9b 100644 --- a/ILSpy/TreeNodes/PropertyTreeNode.cs +++ b/ILSpy/TreeNodes/PropertyTreeNode.cs @@ -25,7 +25,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// Represents a property in the TreeView. /// - sealed class PropertyTreeNode : ILSpyTreeNode + public sealed class PropertyTreeNode : ILSpyTreeNode { readonly PropertyDefinition property; readonly bool isIndexer; diff --git a/ILSpy/TreeNodes/ResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceEntryNode.cs index dc982bf00..0585dbcda 100644 --- a/ILSpy/TreeNodes/ResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceEntryNode.cs @@ -6,20 +6,24 @@ using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; +using System.ComponentModel.Composition; using System.Windows.Controls; using System.Windows.Media.Imaging; - using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.TextView; using Microsoft.Win32; +using Mono.Cecil; namespace ICSharpCode.ILSpy.TreeNodes { - class ResourceEntryNode : ILSpyTreeNode + /// + /// Entry in a .resources file + /// + public class ResourceEntryNode : ILSpyTreeNode { - string key; - Stream value; + protected readonly string key; + protected readonly Stream data; public override object Text { get { return key.ToString(); } @@ -29,106 +33,93 @@ namespace ICSharpCode.ILSpy.TreeNodes get { return Images.Resource; } } - public ResourceEntryNode(string key, Stream value) + public ResourceEntryNode(string key, Stream data) { + if (key == null) + throw new ArgumentNullException("key"); + if (data == null) + throw new ArgumentNullException("data"); this.key = key; - this.value = value; + this.data = data; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public static ILSpyTreeNode Create(string key, Stream data) { - language.WriteCommentLine(output, string.Format("{0} = {1}", key, value)); + ILSpyTreeNode result = null; + foreach (var factory in App.CompositionContainer.GetExportedValues()) { + result = factory.CreateNode(key, data); + if (result != null) + break; + } + return result ?? new ResourceEntryNode(key, data); } - internal override bool View(DecompilerTextView textView) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { - AvalonEditTextOutput output = new AvalonEditTextOutput(); - IHighlightingDefinition highlighting = null; - - if (LoadImage(output)) { - textView.Show(output, highlighting); - } else { - textView.RunWithCancellation( - token => Task.Factory.StartNew( - () => { - try { - if (LoadBaml(output)) - highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml"); - } catch (Exception ex) { - output.Write(ex.ToString()); - } - return output; - }), - t => textView.Show(t.Result, highlighting) - ); - } - return true; + language.WriteCommentLine(output, string.Format("{0} = {1}", key, data)); } - bool LoadImage(AvalonEditTextOutput output) + public override bool Save(DecompilerTextView textView) { - try { - value.Position = 0; - BitmapImage image = new BitmapImage(); - image.BeginInit(); - image.StreamSource = value; - image.EndInit(); - output.AddUIElement(() => new Image { Source = image }); - output.WriteLine(); - output.AddButton(Images.Save, "Save", delegate { Save(null); }); - } catch (Exception) { - return false; + SaveFileDialog dlg = new SaveFileDialog(); + dlg.FileName = Path.GetFileName(DecompilerTextView.CleanUpName(key)); + if (dlg.ShowDialog() == true) { + data.Position = 0; + using (var fs = dlg.OpenFile()) { + data.CopyTo(fs); + } } return true; } + } + + [Export(typeof(IResourceNodeFactory))] + sealed class ImageResourceNodeFactory : IResourceNodeFactory + { + static readonly string[] imageFileExtensions = { ".png", ".gif", ".bmp", ".jpg", ".ico" }; - bool LoadBaml(AvalonEditTextOutput output) + public ILSpyTreeNode CreateNode(Mono.Cecil.Resource resource) { - var asm = this.Ancestors().OfType().FirstOrDefault().LoadedAssembly; - - AppDomain bamlDecompilerAppDomain = null; - try { - BamlDecompiler decompiler = CreateBamlDecompilerInAppDomain(ref bamlDecompilerAppDomain, asm.FileName); - - MemoryStream bamlStream = new MemoryStream(); - value.Position = 0; - value.CopyTo(bamlStream); - - output.Write(decompiler.DecompileBaml(bamlStream, asm.FileName)); - return true; - } finally { - if (bamlDecompilerAppDomain != null) - AppDomain.Unload(bamlDecompilerAppDomain); + EmbeddedResource er = resource as EmbeddedResource; + if (er != null) { + return CreateNode(er.Name, er.GetResourceStream()); } + return null; } - public static BamlDecompiler CreateBamlDecompilerInAppDomain(ref AppDomain appDomain, string assemblyFileName) + public ILSpyTreeNode CreateNode(string key, Stream data) { - if (appDomain == null) { - // Construct and initialize settings for a second AppDomain. - AppDomainSetup bamlDecompilerAppDomainSetup = new AppDomainSetup(); - bamlDecompilerAppDomainSetup.ApplicationBase = "file:///" + Path.GetDirectoryName(assemblyFileName); - bamlDecompilerAppDomainSetup.DisallowBindingRedirects = false; - bamlDecompilerAppDomainSetup.DisallowCodeDownload = true; - bamlDecompilerAppDomainSetup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile; - - // Create the second AppDomain. - appDomain = AppDomain.CreateDomain("BamlDecompiler AD", null, bamlDecompilerAppDomainSetup); + foreach (string fileExt in imageFileExtensions) { + if (key.EndsWith(fileExt, StringComparison.OrdinalIgnoreCase)) + return new ImageResourceEntryNode(key, data); } - return (BamlDecompiler)appDomain.CreateInstanceFromAndUnwrap(typeof(BamlDecompiler).Assembly.Location, typeof(BamlDecompiler).FullName); + return null; + } + } + + sealed class ImageResourceEntryNode : ResourceEntryNode + { + public ImageResourceEntryNode(string key, Stream data) : base(key, data) + { } - public override bool Save(DecompilerTextView textView) + internal override bool View(DecompilerTextView textView) { - SaveFileDialog dlg = new SaveFileDialog(); - dlg.FileName = Path.GetFileName(DecompilerTextView.CleanUpName(key)); - if (dlg.ShowDialog() == true) { - value.Position = 0; - using (var fs = dlg.OpenFile()) { - value.CopyTo(fs); - } + try { + AvalonEditTextOutput output = new AvalonEditTextOutput(); + data.Position = 0; + BitmapImage image = new BitmapImage(); + image.BeginInit(); + image.StreamSource = data; + image.EndInit(); + output.AddUIElement(() => new Image { Source = image }); + output.WriteLine(); + output.AddButton(Images.Save, "Save", delegate { Save(null); }); + textView.Show(output, null); + return true; + } catch (Exception) { + return false; } - return true; } } } diff --git a/ILSpy/TreeNodes/ResourceListTreeNode.cs b/ILSpy/TreeNodes/ResourceListTreeNode.cs index 44ae1e2c5..07ea76324 100644 --- a/ILSpy/TreeNodes/ResourceListTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceListTreeNode.cs @@ -43,7 +43,7 @@ namespace ICSharpCode.ILSpy.TreeNodes protected override void LoadChildren() { foreach (Resource r in module.Resources) - this.Children.Add(new ResourceTreeNode(r)); + this.Children.Add(ResourceTreeNode.Create(r)); } public override FilterResult Filter(FilterSettings settings) @@ -63,110 +63,4 @@ namespace ICSharpCode.ILSpy.TreeNodes } } } - - class ResourceTreeNode : ILSpyTreeNode - { - Resource r; - - public ResourceTreeNode(Resource r) - { - this.LazyLoading = true; - this.r = r; - } - - public Resource Resource { - get { return r; } - } - - public override object Text { - get { return r.Name; } - } - - public override object Icon { - get { return Images.Resource; } - } - - public override FilterResult Filter(FilterSettings settings) - { - if (!settings.ShowInternalApi && (r.Attributes & ManifestResourceAttributes.VisibilityMask) == ManifestResourceAttributes.Private) - return FilterResult.Hidden; - if (settings.SearchTermMatches(r.Name)) - return FilterResult.Match; - else - return FilterResult.Hidden; - } - - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) - { - language.WriteCommentLine(output, string.Format("{0} ({1}, {2})", r.Name, r.ResourceType, r.Attributes)); - - ISmartTextOutput smartOutput = output as ISmartTextOutput; - if (smartOutput != null && r is EmbeddedResource) { - smartOutput.AddButton(Images.Save, "Save", delegate { Save(null); }); - output.WriteLine(); - } - } - - internal override bool View(DecompilerTextView textView) - { - EmbeddedResource er = r as EmbeddedResource; - if (er != null) { - Stream s = er.GetResourceStream(); - if (s != null && s.Length < DecompilerTextView.DefaultOutputLengthLimit) { - s.Position = 0; - FileType type = GuessFileType.DetectFileType(s); - if (type != FileType.Binary) { - s.Position = 0; - AvalonEditTextOutput output = new AvalonEditTextOutput(); - output.Write(FileReader.OpenStream(s, Encoding.UTF8).ReadToEnd()); - string ext; - if (type == FileType.Xml) - ext = ".xml"; - else - ext = Path.GetExtension(DecompilerTextView.CleanUpName(er.Name)); - textView.Show(output, HighlightingManager.Instance.GetDefinitionByExtension(ext)); - return true; - } - } - } - return false; - } - - public override bool Save(TextView.DecompilerTextView textView) - { - EmbeddedResource er = r as EmbeddedResource; - if (er != null) { - SaveFileDialog dlg = new SaveFileDialog(); - dlg.FileName = DecompilerTextView.CleanUpName(er.Name); - if (dlg.ShowDialog() == true) { - Stream s = er.GetResourceStream(); - s.Position = 0; - using (var fs = dlg.OpenFile()) { - s.CopyTo(fs); - } - } - return true; - } - return false; - } - - protected override void LoadChildren() - { - EmbeddedResource er = r as EmbeddedResource; - if (er != null) { - try { - Stream s = er.GetResourceStream(); - s.Position = 0; - if (er.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) { - ResourceReader reader = new ResourceReader(s); - foreach (DictionaryEntry entry in reader.Cast().OrderBy(e => e.Key.ToString())) { - if (entry.Value is Stream) - Children.Add(new ResourceEntryNode(entry.Key.ToString(), (Stream)entry.Value)); - } - } - } catch (ArgumentException) { - } - } - } - } } diff --git a/ILSpy/TreeNodes/ResourceTreeNode.cs b/ILSpy/TreeNodes/ResourceTreeNode.cs new file mode 100644 index 000000000..92e6676f8 --- /dev/null +++ b/ILSpy/TreeNodes/ResourceTreeNode.cs @@ -0,0 +1,174 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Collections; +using System.ComponentModel.Composition; +using System.IO; +using System.Linq; +using System.Resources; +using System.Text; +using System.Windows; +using System.Windows.Threading; +using ICSharpCode.AvalonEdit.Highlighting; +using ICSharpCode.AvalonEdit.Utils; +using ICSharpCode.Decompiler; +using ICSharpCode.ILSpy.TextView; +using Microsoft.Win32; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.TreeNodes +{ + public class ResourceTreeNode : ILSpyTreeNode + { + Resource r; + + public ResourceTreeNode(Resource r) + { + if (r == null) + throw new ArgumentNullException("r"); + this.r = r; + } + + public Resource Resource { + get { return r; } + } + + public override object Text { + get { return r.Name; } + } + + public override object Icon { + get { return Images.Resource; } + } + + public override FilterResult Filter(FilterSettings settings) + { + if (!settings.ShowInternalApi && (r.Attributes & ManifestResourceAttributes.VisibilityMask) == ManifestResourceAttributes.Private) + return FilterResult.Hidden; + if (settings.SearchTermMatches(r.Name)) + return FilterResult.Match; + else + return FilterResult.Hidden; + } + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, string.Format("{0} ({1}, {2})", r.Name, r.ResourceType, r.Attributes)); + + ISmartTextOutput smartOutput = output as ISmartTextOutput; + if (smartOutput != null && r is EmbeddedResource) { + smartOutput.AddButton(Images.Save, "Save", delegate { Save(null); }); + output.WriteLine(); + } + } + + internal override bool View(DecompilerTextView textView) + { + EmbeddedResource er = r as EmbeddedResource; + if (er != null) { + Stream s = er.GetResourceStream(); + if (s != null && s.Length < DecompilerTextView.DefaultOutputLengthLimit) { + s.Position = 0; + FileType type = GuessFileType.DetectFileType(s); + if (type != FileType.Binary) { + s.Position = 0; + AvalonEditTextOutput output = new AvalonEditTextOutput(); + output.Write(FileReader.OpenStream(s, Encoding.UTF8).ReadToEnd()); + string ext; + if (type == FileType.Xml) + ext = ".xml"; + else + ext = Path.GetExtension(DecompilerTextView.CleanUpName(er.Name)); + textView.Show(output, HighlightingManager.Instance.GetDefinitionByExtension(ext)); + return true; + } + } + } + return false; + } + + public override bool Save(TextView.DecompilerTextView textView) + { + EmbeddedResource er = r as EmbeddedResource; + if (er != null) { + SaveFileDialog dlg = new SaveFileDialog(); + dlg.FileName = DecompilerTextView.CleanUpName(er.Name); + if (dlg.ShowDialog() == true) { + Stream s = er.GetResourceStream(); + s.Position = 0; + using (var fs = dlg.OpenFile()) { + s.CopyTo(fs); + } + } + return true; + } + return false; + } + + public static ILSpyTreeNode Create(Resource resource) + { + ILSpyTreeNode result = null; + foreach (var factory in App.CompositionContainer.GetExportedValues()) { + result = factory.CreateNode(resource); + if (result != null) + break; + } + return result ?? new ResourceTreeNode(resource); + } + } + + /// + /// This interface allows plugins to create custom nodes for resources. + /// + public interface IResourceNodeFactory + { + ILSpyTreeNode CreateNode(Resource resource); + ILSpyTreeNode CreateNode(string key, Stream data); + } + + [Export(typeof(IResourceNodeFactory))] + sealed class ResourcesFileTreeNodeFactory : IResourceNodeFactory + { + public ILSpyTreeNode CreateNode(Resource resource) + { + EmbeddedResource er = resource as EmbeddedResource; + if (er != null && er.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) { + return new ResourcesFileTreeNode(er); + } + return null; + } + + public ILSpyTreeNode CreateNode(string key, Stream data) + { + return null; + } + } + + sealed class ResourcesFileTreeNode : ResourceTreeNode + { + public ResourcesFileTreeNode(EmbeddedResource er) : base(er) + { + this.LazyLoading = true; + } + + protected override void LoadChildren() + { + EmbeddedResource er = this.Resource as EmbeddedResource; + if (er != null) { + Stream s = er.GetResourceStream(); + s.Position = 0; + ResourceReader reader; + try { + reader = new ResourceReader(s); + } catch (ArgumentException) { + return; + } + foreach (DictionaryEntry entry in reader.Cast().OrderBy(e => e.Key.ToString())) { + if (entry.Value is Stream) + Children.Add(ResourceEntryNode.Create(entry.Key.ToString(), (Stream)entry.Value)); + } + } + } + } +} diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index 2ccdc337e..d9f78ea8e 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -28,7 +28,7 @@ using Mono.Cecil; namespace ICSharpCode.ILSpy.TreeNodes { - sealed class TypeTreeNode : ILSpyTreeNode + public sealed class TypeTreeNode : ILSpyTreeNode { readonly TypeDefinition type; readonly AssemblyTreeNode parentAssemblyNode; diff --git a/Mono.Cecil/Mono.Cecil/PInvokeAttributes.cs b/Mono.Cecil/Mono.Cecil/PInvokeAttributes.cs index fc9469253..1389c93ad 100644 --- a/Mono.Cecil/Mono.Cecil/PInvokeAttributes.cs +++ b/Mono.Cecil/Mono.Cecil/PInvokeAttributes.cs @@ -53,7 +53,7 @@ namespace Mono.Cecil { BestFitMask = 0x0030, BestFitEnabled = 0x0010, - BestFidDisabled = 0x0020, + BestFitDisabled = 0x0020, ThrowOnUnmappableCharMask = 0x3000, ThrowOnUnmappableCharEnabled = 0x1000, diff --git a/Mono.Cecil/Mono.Cecil/PInvokeInfo.cs b/Mono.Cecil/Mono.Cecil/PInvokeInfo.cs index 53aae2c38..9a273d550 100644 --- a/Mono.Cecil/Mono.Cecil/PInvokeInfo.cs +++ b/Mono.Cecil/Mono.Cecil/PInvokeInfo.cs @@ -106,14 +106,14 @@ namespace Mono.Cecil { set { attributes = attributes.SetMaskedAttributes ((ushort) PInvokeAttributes.CallConvMask, (ushort) PInvokeAttributes.CallConvFastcall, value); } } - public bool IsBestFistEnabled { + public bool IsBestFitEnabled { get { return attributes.GetMaskedAttributes ((ushort) PInvokeAttributes.BestFitMask, (ushort) PInvokeAttributes.BestFitEnabled); } set { attributes = attributes.SetMaskedAttributes ((ushort) PInvokeAttributes.BestFitMask, (ushort) PInvokeAttributes.BestFitEnabled, value); } } - public bool IsBestFistDisabled { - get { return attributes.GetMaskedAttributes ((ushort) PInvokeAttributes.BestFitMask, (ushort) PInvokeAttributes.BestFidDisabled); } - set { attributes = attributes.SetMaskedAttributes ((ushort) PInvokeAttributes.BestFitMask, (ushort) PInvokeAttributes.BestFidDisabled, value); } + public bool IsBestFitDisabled { + get { return attributes.GetMaskedAttributes ((ushort) PInvokeAttributes.BestFitMask, (ushort) PInvokeAttributes.BestFitDisabled); } + set { attributes = attributes.SetMaskedAttributes ((ushort) PInvokeAttributes.BestFitMask, (ushort) PInvokeAttributes.BestFitDisabled, value); } } public bool IsThrowOnUnmappableCharEnabled {