diff options
author | Lluis Sanchez <llsan@microsoft.com> | 2018-05-25 16:40:29 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-05-25 16:40:29 +0300 |
commit | 7b736c39bbd344cc45d915790b79a07bbd9c2d25 (patch) | |
tree | b92e911a3053d4d62f50bcb8cc379376cd78a98d | |
parent | a1d062b96a1f3b35b8c99deea18a9ea8c0ec5e55 (diff) | |
parent | f4a7b82d6b3d13694125031f9709d03a9e7e8b09 (diff) |
Merge branch 'master' into fix4822
243 files changed, 5296 insertions, 36208 deletions
diff --git a/.gitignore b/.gitignore index b157b36ee0..ac7b0946ef 100644 --- a/.gitignore +++ b/.gitignore @@ -23,8 +23,6 @@ obj/ /main/build/MacOSX/monostub /main/build/MacOSX/monostub-nogui /main/build/MacOSx/monostub-test -/main/contrib/bin/ -/main/contrib/*/*/bin/ /local-libs /main/build/tests /main/monodevelop_version @@ -104,11 +102,6 @@ Thumbs.db /main/build/MacOSX/Makefile /main/build/Makefile /main/external/Makefile -/main/contrib/Makefile -/main/contrib/Mono.Cecil/Makefile -/main/contrib/ICSharpCode.NRefactory.CSharp/Makefile -/main/contrib/GitSharp/Makefile -/main/contrib/Mono.Debugger.Soft/Makefile /main/po/Makefile /main/src/Makefile /main/src/addins/AspNet/Makefile @@ -190,11 +183,6 @@ Thumbs.db /main/tests/UnitTests/Makefile /main/tests/UserInterfaceTests/Makefile /main/theme-icons/Makefile -/main/contrib/NGit/Makefile -/main/contrib/NSch/Makefile -/main/contrib/Sharpen/Makefile -/main/contrib/ICSharpCode.Decompiler/Makefile -/main/contrib/ICSharpCode.NRefactory/Makefile /main/src/addins/TextTemplating/Mono.TextTemplating.Tests/Makefile /main/tests/MacPlatform.Tests/Makefile /main/tests/MonoDevelop.Core.Tests.Addin/Makefile @@ -233,6 +221,7 @@ Thumbs.db /main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.Projects.MSBuild.Shared/Makefile /main/tests/MonoDevelop.Core.Tests/Makefile /main/tests/WindowsPlatform.Tests/Makefile +/main/tests/IdeUnitTests/Makefile # extras configure junk /extras/MonoDevelop.Database/INSTALL diff --git a/main/Main.sln b/main/Main.sln index 7e3d0c7396..831c6119a0 100644 --- a/main/Main.sln +++ b/main/Main.sln @@ -321,6 +321,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoDevelop.Core.Tests.Addi EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoDevelop.AspNetCore.DevCertInstaller", "src\addins\MonoDevelop.AspNetCore\MonoDevelop.AspNetCore.DevCertInstaller\MonoDevelop.AspNetCore.DevCertInstaller.csproj", "{A9D93F6C-EB26-44FF-8F2A-B3DB411FA410}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IdeUnitTests", "tests\IdeUnitTests\IdeUnitTests.csproj", "{F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -2048,6 +2050,26 @@ Global {A9D93F6C-EB26-44FF-8F2A-B3DB411FA410}.ReleaseMac|Any CPU.Build.0 = Release|x64
{A9D93F6C-EB26-44FF-8F2A-B3DB411FA410}.ReleaseWin32|Any CPU.ActiveCfg = Release|Any CPU
{A9D93F6C-EB26-44FF-8F2A-B3DB411FA410}.ReleaseGnome|Any CPU.ActiveCfg = Release|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.DebugMac|Any CPU.ActiveCfg = Debug|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.DebugMac|Any CPU.Build.0 = Debug|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.DebugWin32|Any CPU.Build.0 = Debug|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.DebugGnome|Any CPU.ActiveCfg = Debug|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.DebugGnome|Any CPU.Build.0 = Debug|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.ReleaseMac|Any CPU.ActiveCfg = Release|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.ReleaseMac|Any CPU.Build.0 = Release|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.ReleaseWin32|Any CPU.ActiveCfg = Release|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.ReleaseWin32|Any CPU.Build.0 = Release|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.ReleaseGnome|Any CPU.ActiveCfg = Release|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.ReleaseGnome|Any CPU.Build.0 = Release|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.Debug|x86.Build.0 = Debug|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.Release|x86.ActiveCfg = Release|Any CPU
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{7525BB88-6142-4A26-93B9-A30C6983390A} = {9D360D43-0C05-49D6-84DB-4E7AB2F38F82}
@@ -2188,6 +2210,7 @@ Global {BCC3A6AE-EC45-40AD-BA3E-59F6401D588C} = {78C10DAE-D3D7-44FC-93DF-831D8D54ECF9}
{CF789E43-7AAF-4690-BA32-ABD75DEEB087} = {78C10DAE-D3D7-44FC-93DF-831D8D54ECF9}
{A9D93F6C-EB26-44FF-8F2A-B3DB411FA410} = {1DEFDD53-2677-439D-906E-141ED2AEC19B}
+ {F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F} = {78C10DAE-D3D7-44FC-93DF-831D8D54ECF9}
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
Policies = $0
diff --git a/main/configure.ac b/main/configure.ac index f65119e079..59cfd4c759 100644 --- a/main/configure.ac +++ b/main/configure.ac @@ -378,6 +378,7 @@ src/addins/MonoDevelop.PackageManagement/Makefile src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/Makefile tests/Makefile tests/UnitTests/Makefile +tests/IdeUnitTests/Makefile tests/UserInterfaceTests/Makefile tests/TestRunner/Makefile tests/Ide.Tests/Makefile diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Annotations.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Annotations.cs deleted file mode 100644 index 1c957292ec..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Annotations.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using ICSharpCode.Decompiler.ILAst; -using ICSharpCode.NRefactory.CSharp; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.Ast -{ - public class TypeInformation - { - public readonly TypeReference InferredType; - public readonly TypeReference ExpectedType; - - public TypeInformation(TypeReference inferredType, TypeReference expectedType) - { - this.InferredType = inferredType; - this.ExpectedType = expectedType; - } - } - - public class LdTokenAnnotation {} - - /// <summary> - /// Annotation that is applied to the body expression of an Expression.Lambda() call. - /// </summary> - public class ParameterDeclarationAnnotation - { - public readonly List<ParameterDeclaration> Parameters = new List<ParameterDeclaration>(); - - public ParameterDeclarationAnnotation(ILExpression expr) - { - Debug.Assert(expr.Code == ILCode.ExpressionTreeParameterDeclarations); - for (int i = 0; i < expr.Arguments.Count - 1; i++) { - ILExpression p = expr.Arguments[i]; - // p looks like this: - // stloc(v, call(Expression::Parameter, call(Type::GetTypeFromHandle, ldtoken(...)), ldstr(...))) - ILVariable v = (ILVariable)p.Operand; - TypeReference typeRef = (TypeReference)p.Arguments[0].Arguments[0].Arguments[0].Operand; - string name = (string)p.Arguments[0].Arguments[1].Operand; - Parameters.Add(new ParameterDeclaration(AstBuilder.ConvertType(typeRef), name).WithAnnotation(v)); - } - } - } - - /// <summary> - /// Annotation that is applied to a LambdaExpression that was produced by an expression tree. - /// </summary> - public class ExpressionTreeLambdaAnnotation - { - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/main/contrib/ICSharpCode.Decompiler/Ast/AstBuilder.cs deleted file mode 100644 index 5c047477cf..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ /dev/null @@ -1,1711 +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.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading; - -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; - -namespace ICSharpCode.Decompiler.Ast -{ - using Ast = ICSharpCode.NRefactory.CSharp; - using VarianceModifier = ICSharpCode.NRefactory.TypeSystem.VarianceModifier; - - [Flags] - public enum ConvertTypeOptions - { - None = 0, - IncludeNamespace = 1, - IncludeTypeParameterDefinitions = 2, - DoNotUsePrimitiveTypeNames = 4 - } - - public class AstBuilder - { - DecompilerContext context; - SyntaxTree syntaxTree = new SyntaxTree(); - Dictionary<string, NamespaceDeclaration> astNamespaces = new Dictionary<string, NamespaceDeclaration>(); - bool transformationsHaveRun; - - public AstBuilder(DecompilerContext context) - { - if (context == null) - throw new ArgumentNullException("context"); - this.context = context; - this.DecompileMethodBodies = true; - } - - public static bool MemberIsHidden(MemberReference member, DecompilerSettings settings) - { - MethodDefinition method = member as MethodDefinition; - if (method != null) { - if (method.IsGetter || method.IsSetter || method.IsAddOn || method.IsRemoveOn) - return true; - if (settings.AnonymousMethods && method.HasGeneratedName() && method.IsCompilerGenerated()) - return true; - if (settings.HideNonPublicMembers && !(method.IsPublic || method.IsFamily)) - return true; - } - - TypeDefinition type = member as TypeDefinition; - if (type != null) { - if (type.DeclaringType != null) { - if (settings.AnonymousMethods && IsClosureType(type)) - return true; - if (settings.YieldReturn && YieldReturnDecompiler.IsCompilerGeneratorEnumerator(type)) - return true; - if (settings.AsyncAwait && AsyncDecompiler.IsCompilerGeneratedStateMachine(type)) - return true; - } else if (type.IsCompilerGenerated()) { - if (type.Name.StartsWith("<PrivateImplementationDetails>", StringComparison.Ordinal)) - return true; - if (type.IsAnonymousType()) - return true; - } - if (settings.HideNonPublicMembers && !(type.IsPublic)) - return true; - } - - FieldDefinition field = member as FieldDefinition; - if (field != null) { - if (field.IsCompilerGenerated()) { - if (settings.AnonymousMethods && IsAnonymousMethodCacheField(field)) - return true; - if (settings.AutomaticProperties && IsAutomaticPropertyBackingField(field)) - return true; - if (settings.SwitchStatementOnString && IsSwitchOnStringCache(field)) - return true; - } - // event-fields are not [CompilerGenerated] - if (settings.AutomaticEvents && field.DeclaringType.Events.Any(ev => ev.Name == field.Name)) - return true; - if (settings.HideNonPublicMembers && !(field.IsPublic || field.IsFamily)) - return true; - } - - PropertyDefinition property = member as PropertyDefinition; - if (property != null) { - if (settings.HideNonPublicMembers && (property.GetMethod == null || !(property.GetMethod.IsPublic || property.GetMethod.IsFamily)) && - (property.SetMethod == null || !(property.SetMethod.IsPublic || property.SetMethod.IsFamily))) - return true; - } - - EventDefinition evt = member as EventDefinition; - if (evt != null) { - if (settings.HideNonPublicMembers && (evt.AddMethod == null || !(evt.AddMethod.IsPublic | evt.AddMethod.IsFamily)) && - (evt.RemoveMethod == null || !(evt.RemoveMethod.IsPublic || evt.RemoveMethod.IsFamily))) - return true; - } - - return false; - } - - static bool IsSwitchOnStringCache(FieldDefinition field) - { - return field.Name.StartsWith("<>f__switch", StringComparison.Ordinal); - } - - static bool IsAutomaticPropertyBackingField(FieldDefinition field) - { - return field.HasGeneratedName() && field.Name.EndsWith("BackingField", StringComparison.Ordinal); - } - - static bool IsAnonymousMethodCacheField(FieldDefinition field) - { - return field.Name.StartsWith("CS$<>", StringComparison.Ordinal) || field.Name.StartsWith("<>f__am", StringComparison.Ordinal); - } - - static bool IsClosureType(TypeDefinition type) - { - return type.HasGeneratedName() && type.IsCompilerGenerated() && (type.Name.Contains("DisplayClass") || type.Name.Contains("AnonStorey")); - } - - /// <summary> - /// Runs the C# transformations on the compilation unit. - /// </summary> - public void RunTransformations() - { - RunTransformations(null); - } - - public void RunTransformations(Predicate<IAstTransform> transformAbortCondition) - { - TransformationPipeline.RunTransformationsUntil(syntaxTree, transformAbortCondition, context); - transformationsHaveRun = true; - } - - /// <summary> - /// Gets the abstract source tree. - /// </summary> - public SyntaxTree SyntaxTree { - get { return syntaxTree; } - } - - /// <summary> - /// Generates C# code from the abstract source tree. - /// </summary> - /// <remarks>This method adds ParenthesizedExpressions into the AST, and will run transformations if <see cref="RunTransformations"/> was not called explicitly</remarks> - public void GenerateCode(ITextOutput output) - { - if (!transformationsHaveRun) - RunTransformations(); - - syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); - var outputFormatter = new TextTokenWriter(output, context) { FoldBraces = context.Settings.FoldBraces }; - var formattingPolicy = context.Settings.CSharpFormattingOptions; - syntaxTree.AcceptVisitor(new CSharpOutputVisitor(outputFormatter, formattingPolicy)); - } - - public void AddAssembly(AssemblyDefinition assemblyDefinition, bool onlyAssemblyLevel = false) - { - AddAssembly(assemblyDefinition.MainModule, onlyAssemblyLevel); - } - - public void AddAssembly(ModuleDefinition moduleDefinition, bool onlyAssemblyLevel = false) - { - if (moduleDefinition.Assembly != null && moduleDefinition.Assembly.Name.Version != null) { - syntaxTree.AddChild( - new AttributeSection { - AttributeTarget = "assembly", - Attributes = { - new NRefactory.CSharp.Attribute { - Type = new SimpleType("AssemblyVersion") - .WithAnnotation(new TypeReference( - "System.Reflection", "AssemblyVersionAttribute", - moduleDefinition, moduleDefinition.TypeSystem.Corlib)), - Arguments = { - new PrimitiveExpression(moduleDefinition.Assembly.Name.Version.ToString()) - } - } - } - }, EntityDeclaration.AttributeRole); - } - - if (moduleDefinition.Assembly != null) { - ConvertCustomAttributes(syntaxTree, moduleDefinition.Assembly, "assembly"); - ConvertSecurityAttributes(syntaxTree, moduleDefinition.Assembly, "assembly"); - } - ConvertCustomAttributes(syntaxTree, moduleDefinition, "module"); - AddTypeForwarderAttributes(syntaxTree, moduleDefinition, "assembly"); - - if (!onlyAssemblyLevel) { - foreach (TypeDefinition typeDef in moduleDefinition.Types) { - // Skip the <Module> class - if (typeDef.Name == "<Module>") continue; - // Skip any hidden types - if (AstBuilder.MemberIsHidden(typeDef, context.Settings)) - continue; - - AddType(typeDef); - } - } - } - - void AddTypeForwarderAttributes(SyntaxTree astCompileUnit, ModuleDefinition module, string target) - { - if (!module.HasExportedTypes) - return; - foreach (ExportedType type in module.ExportedTypes) { - if (type.IsForwarder) { - var forwardedType = CreateTypeOfExpression(new TypeReference(type.Namespace, type.Name, module, type.Scope)); - astCompileUnit.AddChild( - new AttributeSection { - AttributeTarget = target, - Attributes = { - new NRefactory.CSharp.Attribute { - Type = new SimpleType("TypeForwardedTo") - .WithAnnotation(new TypeReference( - "System.Runtime.CompilerServices", "TypeForwardedToAttribute", - module, module.TypeSystem.Corlib)), - Arguments = { forwardedType } - } - } - }, EntityDeclaration.AttributeRole); - } - } - } - - NamespaceDeclaration GetCodeNamespace(string name) - { - if (string.IsNullOrEmpty(name)) { - return null; - } - if (astNamespaces.ContainsKey(name)) { - return astNamespaces[name]; - } else { - // Create the namespace - NamespaceDeclaration astNamespace = new NamespaceDeclaration { Name = name }; - syntaxTree.Members.Add(astNamespace); - astNamespaces[name] = astNamespace; - return astNamespace; - } - } - - public void AddType(TypeDefinition typeDef) - { - var astType = CreateType(typeDef); - NamespaceDeclaration astNS = GetCodeNamespace(typeDef.Namespace); - if (astNS != null) { - astNS.Members.Add(astType); - } else { - syntaxTree.Members.Add(astType); - } - } - - public void AddMethod(MethodDefinition method) - { - AstNode node = method.IsConstructor ? (AstNode)CreateConstructor(method) : CreateMethod(method); - syntaxTree.Members.Add(node); - } - - public void AddProperty(PropertyDefinition property) - { - syntaxTree.Members.Add(CreateProperty(property)); - } - - public void AddField(FieldDefinition field) - { - syntaxTree.Members.Add(CreateField(field)); - } - - public void AddEvent(EventDefinition ev) - { - syntaxTree.Members.Add(CreateEvent(ev)); - } - - /// <summary> - /// Creates the AST for a type definition. - /// </summary> - /// <param name="typeDef"></param> - /// <returns>TypeDeclaration or DelegateDeclaration.</returns> - public EntityDeclaration CreateType(TypeDefinition typeDef) - { - // create type - TypeDefinition oldCurrentType = context.CurrentType; - context.CurrentType = typeDef; - TypeDeclaration astType = new TypeDeclaration(); - ConvertAttributes(astType, typeDef); - astType.AddAnnotation(typeDef); - astType.Modifiers = ConvertModifiers(typeDef); - astType.Name = CleanName(typeDef.Name); - - if (typeDef.IsEnum) { // NB: Enum is value type - astType.ClassType = ClassType.Enum; - astType.Modifiers &= ~Modifiers.Sealed; - } else if (typeDef.IsValueType) { - astType.ClassType = ClassType.Struct; - astType.Modifiers &= ~Modifiers.Sealed; - } else if (typeDef.IsInterface) { - astType.ClassType = ClassType.Interface; - astType.Modifiers &= ~Modifiers.Abstract; - } else { - astType.ClassType = ClassType.Class; - } - - IEnumerable<GenericParameter> genericParameters = typeDef.GenericParameters; - if (typeDef.DeclaringType != null && typeDef.DeclaringType.HasGenericParameters) - genericParameters = genericParameters.Skip(typeDef.DeclaringType.GenericParameters.Count); - astType.TypeParameters.AddRange(MakeTypeParameters(genericParameters)); - astType.Constraints.AddRange(MakeConstraints(genericParameters)); - - EntityDeclaration result = astType; - if (typeDef.IsEnum) { - long expectedEnumMemberValue = 0; - bool forcePrintingInitializers = IsFlagsEnum(typeDef); - TypeCode baseType = TypeCode.Int32; - foreach (FieldDefinition field in typeDef.Fields) { - if (!field.IsStatic) { - // the value__ field - if (field.FieldType != typeDef.Module.TypeSystem.Int32) { - astType.AddChild(ConvertType(field.FieldType), Roles.BaseType); - baseType = TypeAnalysis.GetTypeCode(field.FieldType); - } - } else { - EnumMemberDeclaration enumMember = new EnumMemberDeclaration(); - ConvertCustomAttributes(enumMember, field); - enumMember.AddAnnotation(field); - enumMember.Name = CleanName(field.Name); - long memberValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false); - if (forcePrintingInitializers || memberValue != expectedEnumMemberValue) { - enumMember.AddChild(new PrimitiveExpression(CSharpPrimitiveCast.Cast(baseType, field.Constant, false)), EnumMemberDeclaration.InitializerRole); - } - expectedEnumMemberValue = memberValue + 1; - astType.AddChild(enumMember, Roles.TypeMemberRole); - } - } - } else if (typeDef.BaseType != null && typeDef.BaseType.FullName == "System.MulticastDelegate") { - DelegateDeclaration dd = new DelegateDeclaration(); - dd.Modifiers = astType.Modifiers & ~Modifiers.Sealed; - dd.Name = astType.Name; - dd.AddAnnotation(typeDef); - astType.Attributes.MoveTo(dd.Attributes); - astType.TypeParameters.MoveTo(dd.TypeParameters); - astType.Constraints.MoveTo(dd.Constraints); - foreach (var m in typeDef.Methods) { - if (m.Name == "Invoke") { - dd.ReturnType = ConvertType(m.ReturnType, m.MethodReturnType); - dd.Parameters.AddRange(MakeParameters(m)); - ConvertAttributes(dd, m.MethodReturnType, m.Module); - } - } - result = dd; - } else { - // Base type - if (typeDef.BaseType != null && !typeDef.IsValueType && typeDef.BaseType.FullName != "System.Object") { - astType.AddChild(ConvertType(typeDef.BaseType), Roles.BaseType); - } - foreach (var i in typeDef.Interfaces) - astType.AddChild(ConvertType(i.InterfaceType), Roles.BaseType); - - AddTypeMembers(astType, typeDef); - - if (astType.Members.OfType<IndexerDeclaration>().Any(idx => idx.PrivateImplementationType.IsNull)) { - // Remove the [DefaultMember] attribute if the class contains indexers - foreach (AttributeSection section in astType.Attributes) { - foreach (Ast.Attribute attr in section.Attributes) { - TypeReference tr = attr.Type.Annotation<TypeReference>(); - if (tr != null && tr.Name == "DefaultMemberAttribute" && tr.Namespace == "System.Reflection") { - attr.Remove(); - } - } - if (section.Attributes.Count == 0) - section.Remove(); - } - } - } - - context.CurrentType = oldCurrentType; - return result; - } - - internal static string CleanName(string name) - { - int pos = name.LastIndexOf('`'); - if (pos >= 0) - name = name.Substring(0, pos); - pos = name.LastIndexOf('.'); - if (pos >= 0) - name = name.Substring(pos + 1); - return name; - } - - #region Create TypeOf Expression - /// <summary> - /// Creates a typeof-expression for the specified type. - /// </summary> - public static TypeOfExpression CreateTypeOfExpression(TypeReference type) - { - return new TypeOfExpression(AddEmptyTypeArgumentsForUnboundGenerics(ConvertType(type))); - } - - static AstType AddEmptyTypeArgumentsForUnboundGenerics(AstType type) - { - TypeReference typeRef = type.Annotation<TypeReference>(); - if (typeRef == null) - return type; - TypeDefinition typeDef = typeRef.Resolve(); // need to resolve to figure out the number of type parameters - if (typeDef == null || !typeDef.HasGenericParameters) - return type; - SimpleType sType = type as SimpleType; - MemberType mType = type as MemberType; - if (sType != null) { - while (typeDef.GenericParameters.Count > sType.TypeArguments.Count) { - sType.TypeArguments.Add(new SimpleType("")); - } - } - - if (mType != null) { - AddEmptyTypeArgumentsForUnboundGenerics(mType.Target); - - int outerTypeParamCount = typeDef.DeclaringType == null ? 0 : typeDef.DeclaringType.GenericParameters.Count; - - while (typeDef.GenericParameters.Count - outerTypeParamCount > mType.TypeArguments.Count) { - mType.TypeArguments.Add(new SimpleType("")); - } - } - - return type; - } - #endregion - - #region Convert Type Reference - /// <summary> - /// Converts a type reference. - /// </summary> - /// <param name="type">The Cecil type reference that should be converted into - /// a type system type reference.</param> - /// <param name="typeAttributes">Attributes associated with the Cecil type reference. - /// This is used to support the 'dynamic' type.</param> - public static AstType ConvertType(TypeReference type, ICustomAttributeProvider typeAttributes = null, ConvertTypeOptions options = ConvertTypeOptions.None) - { - int typeIndex = 0; - return ConvertType(type, typeAttributes, ref typeIndex, options); - } - - static AstType ConvertType(TypeReference type, ICustomAttributeProvider typeAttributes, ref int typeIndex, ConvertTypeOptions options) - { - while (type is OptionalModifierType || type is RequiredModifierType) { - type = ((TypeSpecification)type).ElementType; - } - if (type == null) { - return AstType.Null; - } - - if (type is Mono.Cecil.ByReferenceType) { - typeIndex++; - // by reference type cannot be represented in C#; so we'll represent it as a pointer instead - return ConvertType((type as Mono.Cecil.ByReferenceType).ElementType, typeAttributes, ref typeIndex, options) - .MakePointerType(); - } else if (type is Mono.Cecil.PointerType) { - typeIndex++; - return ConvertType((type as Mono.Cecil.PointerType).ElementType, typeAttributes, ref typeIndex, options) - .MakePointerType(); - } else if (type is Mono.Cecil.ArrayType) { - typeIndex++; - return ConvertType((type as Mono.Cecil.ArrayType).ElementType, typeAttributes, ref typeIndex, options) - .MakeArrayType((type as Mono.Cecil.ArrayType).Rank); - } else if (type is GenericInstanceType) { - GenericInstanceType gType = (GenericInstanceType)type; - if (gType.ElementType.Namespace == "System" && gType.ElementType.Name == "Nullable`1" && gType.GenericArguments.Count == 1) { - typeIndex++; - return new ComposedType { - BaseType = ConvertType(gType.GenericArguments[0], typeAttributes, ref typeIndex, options), - HasNullableSpecifier = true - }; - } - AstType baseType = ConvertType(gType.ElementType, typeAttributes, ref typeIndex, options & ~ConvertTypeOptions.IncludeTypeParameterDefinitions); - List<AstType> typeArguments = new List<AstType>(); - foreach (var typeArgument in gType.GenericArguments) { - typeIndex++; - typeArguments.Add(ConvertType(typeArgument, typeAttributes, ref typeIndex, options)); - } - ApplyTypeArgumentsTo(baseType, typeArguments); - return baseType; - } else if (type is GenericParameter) { - return new SimpleType(type.Name); - } else if (type.IsNested) { - AstType typeRef = ConvertType(type.DeclaringType, typeAttributes, ref typeIndex, options & ~ConvertTypeOptions.IncludeTypeParameterDefinitions); - string namepart = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name); - MemberType memberType = new MemberType { Target = typeRef, MemberName = namepart }; - memberType.AddAnnotation(type); - if ((options & ConvertTypeOptions.IncludeTypeParameterDefinitions) == ConvertTypeOptions.IncludeTypeParameterDefinitions) { - AddTypeParameterDefininitionsTo(type, memberType); - } - return memberType; - } else { - string ns = type.Namespace ?? string.Empty; - string name = type.Name; - if (name == null) - throw new InvalidOperationException("type.Name returned null. Type: " + type.ToString()); - - if (name == "Object" && ns == "System" && HasDynamicAttribute(typeAttributes, typeIndex)) { - return new PrimitiveType("dynamic"); - } else { - if (ns == "System") { - if ((options & ConvertTypeOptions.DoNotUsePrimitiveTypeNames) - != ConvertTypeOptions.DoNotUsePrimitiveTypeNames) { - switch (name) { - case "SByte": - return new PrimitiveType("sbyte"); - case "Int16": - return new PrimitiveType("short"); - case "Int32": - return new PrimitiveType("int"); - case "Int64": - return new PrimitiveType("long"); - case "Byte": - return new PrimitiveType("byte"); - case "UInt16": - return new PrimitiveType("ushort"); - case "UInt32": - return new PrimitiveType("uint"); - case "UInt64": - return new PrimitiveType("ulong"); - case "String": - return new PrimitiveType("string"); - case "Single": - return new PrimitiveType("float"); - case "Double": - return new PrimitiveType("double"); - case "Decimal": - return new PrimitiveType("decimal"); - case "Char": - return new PrimitiveType("char"); - case "Boolean": - return new PrimitiveType("bool"); - case "Void": - return new PrimitiveType("void"); - case "Object": - return new PrimitiveType("object"); - } - } - } - - name = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(name); - - AstType astType; - if ((options & ConvertTypeOptions.IncludeNamespace) == ConvertTypeOptions.IncludeNamespace && ns.Length > 0) { - string[] parts = ns.Split('.'); - AstType nsType = new SimpleType(parts[0]); - for (int i = 1; i < parts.Length; i++) { - nsType = new MemberType { Target = nsType, MemberName = parts[i] }; - } - astType = new MemberType { Target = nsType, MemberName = name }; - } else { - astType = new SimpleType(name); - } - astType.AddAnnotation(type); - - if ((options & ConvertTypeOptions.IncludeTypeParameterDefinitions) == ConvertTypeOptions.IncludeTypeParameterDefinitions) { - AddTypeParameterDefininitionsTo(type, astType); - } - return astType; - } - } - } - - static void AddTypeParameterDefininitionsTo(TypeReference type, AstType astType) - { - if (type.HasGenericParameters) { - List<AstType> typeArguments = new List<AstType>(); - foreach (GenericParameter gp in type.GenericParameters) { - typeArguments.Add(new SimpleType(gp.Name)); - } - ApplyTypeArgumentsTo(astType, typeArguments); - } - } - - static void ApplyTypeArgumentsTo(AstType baseType, List<AstType> typeArguments) - { - SimpleType st = baseType as SimpleType; - if (st != null) { - st.TypeArguments.AddRange(typeArguments); - } - MemberType mt = baseType as MemberType; - if (mt != null) { - TypeReference type = mt.Annotation<TypeReference>(); - if (type != null) { - int typeParameterCount; - ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name, out typeParameterCount); - if (typeParameterCount > typeArguments.Count) - typeParameterCount = typeArguments.Count; - mt.TypeArguments.AddRange(typeArguments.GetRange(typeArguments.Count - typeParameterCount, typeParameterCount)); - typeArguments.RemoveRange(typeArguments.Count - typeParameterCount, typeParameterCount); - if (typeArguments.Count > 0) - ApplyTypeArgumentsTo(mt.Target, typeArguments); - } else { - mt.TypeArguments.AddRange(typeArguments); - } - } - } - - const string DynamicAttributeFullName = "System.Runtime.CompilerServices.DynamicAttribute"; - - static bool HasDynamicAttribute(ICustomAttributeProvider attributeProvider, int typeIndex) - { - if (attributeProvider == null || !attributeProvider.HasCustomAttributes) - return false; - foreach (CustomAttribute a in attributeProvider.CustomAttributes) { - if (a.Constructor.DeclaringType.FullName == DynamicAttributeFullName) { - if (a.ConstructorArguments.Count == 1) { - CustomAttributeArgument[] values = a.ConstructorArguments[0].Value as CustomAttributeArgument[]; - if (values != null && typeIndex < values.Length && values[typeIndex].Value is bool) - return (bool)values[typeIndex].Value; - } - return true; - } - } - return false; - } - #endregion - - #region ConvertModifiers - Modifiers ConvertModifiers(TypeDefinition typeDef) - { - Modifiers modifiers = Modifiers.None; - if (typeDef.IsNestedPrivate) - modifiers |= Modifiers.Private; - else if (typeDef.IsNestedAssembly || typeDef.IsNestedFamilyAndAssembly || typeDef.IsNotPublic) - modifiers |= Modifiers.Internal; - else if (typeDef.IsNestedFamily) - modifiers |= Modifiers.Protected; - else if (typeDef.IsNestedFamilyOrAssembly) - modifiers |= Modifiers.Protected | Modifiers.Internal; - else if (typeDef.IsPublic || typeDef.IsNestedPublic) - modifiers |= Modifiers.Public; - - if (typeDef.IsAbstract && typeDef.IsSealed) - modifiers |= Modifiers.Static; - else if (typeDef.IsAbstract) - modifiers |= Modifiers.Abstract; - else if (typeDef.IsSealed) - modifiers |= Modifiers.Sealed; - - return modifiers; - } - - Modifiers ConvertModifiers(FieldDefinition fieldDef) - { - Modifiers modifiers = Modifiers.None; - if (fieldDef.IsPrivate) - modifiers |= Modifiers.Private; - else if (fieldDef.IsAssembly || fieldDef.IsFamilyAndAssembly) - modifiers |= Modifiers.Internal; - else if (fieldDef.IsFamily) - modifiers |= Modifiers.Protected; - else if (fieldDef.IsFamilyOrAssembly) - modifiers |= Modifiers.Protected | Modifiers.Internal; - else if (fieldDef.IsPublic) - modifiers |= Modifiers.Public; - - if (fieldDef.IsLiteral) { - modifiers |= Modifiers.Const; - } else { - if (fieldDef.IsStatic) - modifiers |= Modifiers.Static; - - if (fieldDef.IsInitOnly) - modifiers |= Modifiers.Readonly; - } - - RequiredModifierType modreq = fieldDef.FieldType as RequiredModifierType; - if (modreq != null && modreq.ModifierType.FullName == typeof(IsVolatile).FullName) - modifiers |= Modifiers.Volatile; - - return modifiers; - } - - Modifiers ConvertModifiers(MethodDefinition methodDef) - { - if (methodDef == null) - return Modifiers.None; - Modifiers modifiers = Modifiers.None; - if (methodDef.IsPrivate) - modifiers |= Modifiers.Private; - else if (methodDef.IsAssembly || methodDef.IsFamilyAndAssembly) - modifiers |= Modifiers.Internal; - else if (methodDef.IsFamily) - modifiers |= Modifiers.Protected; - else if (methodDef.IsFamilyOrAssembly) - modifiers |= Modifiers.Protected | Modifiers.Internal; - else if (methodDef.IsPublic) - modifiers |= Modifiers.Public; - - if (methodDef.IsStatic) - modifiers |= Modifiers.Static; - - if (methodDef.IsAbstract) { - modifiers |= Modifiers.Abstract; - if (!methodDef.IsNewSlot) - modifiers |= Modifiers.Override; - } else if (methodDef.IsFinal) { - if (!methodDef.IsNewSlot) { - modifiers |= Modifiers.Sealed | Modifiers.Override; - } - } else if (methodDef.IsVirtual) { - if (methodDef.IsNewSlot) - modifiers |= Modifiers.Virtual; - else - modifiers |= Modifiers.Override; - } - if (!methodDef.HasBody && !methodDef.IsAbstract) - modifiers |= Modifiers.Extern; - - return modifiers; - } - - #endregion - - void AddTypeMembers(TypeDeclaration astType, TypeDefinition typeDef) - { - // Nested types - foreach (TypeDefinition nestedTypeDef in typeDef.NestedTypes) { - if (MemberIsHidden(nestedTypeDef, context.Settings)) - continue; - var nestedType = CreateType(nestedTypeDef); - SetNewModifier(nestedType); - astType.AddChild(nestedType, Roles.TypeMemberRole); - } - - // Add fields - foreach(FieldDefinition fieldDef in typeDef.Fields) { - if (MemberIsHidden(fieldDef, context.Settings)) continue; - astType.AddChild(CreateField(fieldDef), Roles.TypeMemberRole); - } - - // Add events - foreach(EventDefinition eventDef in typeDef.Events) { - astType.AddChild(CreateEvent(eventDef), Roles.TypeMemberRole); - } - - // Add properties - foreach(PropertyDefinition propDef in typeDef.Properties) { - astType.Members.Add(CreateProperty(propDef)); - } - - // Add methods - foreach(MethodDefinition methodDef in typeDef.Methods) { - if (MemberIsHidden(methodDef, context.Settings)) continue; - - if (methodDef.IsConstructor) - astType.Members.Add(CreateConstructor(methodDef)); - else - astType.Members.Add(CreateMethod(methodDef)); - } - } - - EntityDeclaration CreateMethod(MethodDefinition methodDef) - { - MethodDeclaration astMethod = new MethodDeclaration(); - astMethod.AddAnnotation(methodDef); - astMethod.ReturnType = ConvertType(methodDef.ReturnType, methodDef.MethodReturnType); - astMethod.Name = CleanName(methodDef.Name); - astMethod.TypeParameters.AddRange(MakeTypeParameters(methodDef.GenericParameters)); - astMethod.Parameters.AddRange(MakeParameters(methodDef)); - // constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly - if (!methodDef.IsVirtual || (methodDef.IsNewSlot && !methodDef.IsPrivate)) astMethod.Constraints.AddRange(MakeConstraints(methodDef.GenericParameters)); - if (!methodDef.DeclaringType.IsInterface) { - if (IsExplicitInterfaceImplementation(methodDef)) { - astMethod.PrivateImplementationType = ConvertType(methodDef.Overrides.First().DeclaringType); - } else { - astMethod.Modifiers = ConvertModifiers(methodDef); - if (methodDef.IsVirtual == methodDef.IsNewSlot) - SetNewModifier(astMethod); - } - astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters); - if (context.CurrentMethodIsAsync) { - astMethod.Modifiers |= Modifiers.Async; - context.CurrentMethodIsAsync = false; - } - } - ConvertAttributes(astMethod, methodDef); - if (methodDef.HasCustomAttributes && astMethod.Parameters.Count > 0) { - foreach (CustomAttribute ca in methodDef.CustomAttributes) { - if (ca.AttributeType.Name == "ExtensionAttribute" && ca.AttributeType.Namespace == "System.Runtime.CompilerServices") { - astMethod.Parameters.First().ParameterModifier = ParameterModifier.This; - } - } - } - - // Convert MethodDeclaration to OperatorDeclaration if possible - if (methodDef.IsSpecialName && !methodDef.HasGenericParameters) { - OperatorType? opType = OperatorDeclaration.GetOperatorType(methodDef.Name); - if (opType.HasValue) { - OperatorDeclaration op = new OperatorDeclaration(); - op.CopyAnnotationsFrom(astMethod); - op.ReturnType = astMethod.ReturnType.Detach(); - op.OperatorType = opType.Value; - op.Modifiers = astMethod.Modifiers; - astMethod.Parameters.MoveTo(op.Parameters); - astMethod.Attributes.MoveTo(op.Attributes); - op.Body = astMethod.Body.Detach(); - return op; - } - } - return astMethod; - } - - bool IsExplicitInterfaceImplementation(MethodDefinition methodDef) - { - return methodDef.HasOverrides && methodDef.IsPrivate; - } - - IEnumerable<TypeParameterDeclaration> MakeTypeParameters(IEnumerable<GenericParameter> genericParameters) - { - foreach (var gp in genericParameters) { - TypeParameterDeclaration tp = new TypeParameterDeclaration(); - tp.Name = CleanName(gp.Name); - if (gp.IsContravariant) - tp.Variance = VarianceModifier.Contravariant; - else if (gp.IsCovariant) - tp.Variance = VarianceModifier.Covariant; - ConvertCustomAttributes(tp, gp); - yield return tp; - } - } - - IEnumerable<Constraint> MakeConstraints(IEnumerable<GenericParameter> genericParameters) - { - foreach (var gp in genericParameters) { - Constraint c = new Constraint(); - c.TypeParameter = new SimpleType(CleanName(gp.Name)); - // class/struct must be first - if (gp.HasReferenceTypeConstraint) - c.BaseTypes.Add(new PrimitiveType("class")); - if (gp.HasNotNullableValueTypeConstraint) - c.BaseTypes.Add(new PrimitiveType("struct")); - - foreach (var constraintType in gp.Constraints) { - if (gp.HasNotNullableValueTypeConstraint && constraintType.FullName == "System.ValueType") - continue; - c.BaseTypes.Add(ConvertType(constraintType)); - } - - if (gp.HasDefaultConstructorConstraint && !gp.HasNotNullableValueTypeConstraint) - c.BaseTypes.Add(new PrimitiveType("new")); // new() must be last - if (c.BaseTypes.Any()) - yield return c; - } - } - - ConstructorDeclaration CreateConstructor(MethodDefinition methodDef) - { - ConstructorDeclaration astMethod = new ConstructorDeclaration(); - astMethod.AddAnnotation(methodDef); - astMethod.Modifiers = ConvertModifiers(methodDef); - if (methodDef.IsStatic) { - // don't show visibility for static ctors - astMethod.Modifiers &= ~Modifiers.VisibilityMask; - } - astMethod.Name = CleanName(methodDef.DeclaringType.Name); - astMethod.Parameters.AddRange(MakeParameters(methodDef)); - astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters); - ConvertAttributes(astMethod, methodDef); - if (methodDef.IsStatic && methodDef.DeclaringType.IsBeforeFieldInit && !astMethod.Body.IsNull) { - astMethod.Body.InsertChildAfter(null, new Comment(" Note: this type is marked as 'beforefieldinit'."), Roles.Comment); - } - return astMethod; - } - - Modifiers FixUpVisibility(Modifiers m) - { - Modifiers v = m & Modifiers.VisibilityMask; - // If any of the modifiers is public, use that - if ((v & Modifiers.Public) == Modifiers.Public) - return Modifiers.Public | (m & ~Modifiers.VisibilityMask); - // If both modifiers are private, no need to fix anything - if (v == Modifiers.Private) - return m; - // Otherwise, use the other modifiers (internal and/or protected) - return m & ~Modifiers.Private; - } - - EntityDeclaration CreateProperty(PropertyDefinition propDef) - { - PropertyDeclaration astProp = new PropertyDeclaration(); - astProp.AddAnnotation(propDef); - var accessor = propDef.GetMethod ?? propDef.SetMethod; - Modifiers getterModifiers = Modifiers.None; - Modifiers setterModifiers = Modifiers.None; - if (IsExplicitInterfaceImplementation(accessor)) { - astProp.PrivateImplementationType = ConvertType(accessor.Overrides.First().DeclaringType); - } else if (!propDef.DeclaringType.IsInterface) { - getterModifiers = ConvertModifiers(propDef.GetMethod); - setterModifiers = ConvertModifiers(propDef.SetMethod); - astProp.Modifiers = FixUpVisibility(getterModifiers | setterModifiers); - try { - if (accessor.IsVirtual && !accessor.IsNewSlot && (propDef.GetMethod == null || propDef.SetMethod == null)) { - foreach (var basePropDef in TypesHierarchyHelpers.FindBaseProperties(propDef)) { - if (basePropDef.GetMethod != null && basePropDef.SetMethod != null) { - var propVisibilityModifiers = ConvertModifiers(basePropDef.GetMethod) | ConvertModifiers(basePropDef.SetMethod); - astProp.Modifiers = FixUpVisibility((astProp.Modifiers & ~Modifiers.VisibilityMask) | (propVisibilityModifiers & Modifiers.VisibilityMask)); - break; - } else if ((basePropDef.GetMethod ?? basePropDef.SetMethod).IsNewSlot) { - break; - } - } - } - } catch (ReferenceResolvingException) { - // TODO: add some kind of notification (a comment?) about possible problems with decompiled code due to unresolved references. - } - } - astProp.Name = CleanName(propDef.Name); - astProp.ReturnType = ConvertType(propDef.PropertyType, propDef); - - if (propDef.GetMethod != null) { - astProp.Getter = new Accessor(); - astProp.Getter.Body = CreateMethodBody(propDef.GetMethod); - astProp.Getter.AddAnnotation(propDef.GetMethod); - ConvertAttributes(astProp.Getter, propDef.GetMethod); - - if ((getterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask)) - astProp.Getter.Modifiers = getterModifiers & Modifiers.VisibilityMask; - } - if (propDef.SetMethod != null) { - astProp.Setter = new Accessor(); - astProp.Setter.Body = CreateMethodBody(propDef.SetMethod); - astProp.Setter.AddAnnotation(propDef.SetMethod); - ConvertAttributes(astProp.Setter, propDef.SetMethod); - ParameterDefinition lastParam = propDef.SetMethod.Parameters.LastOrDefault(); - if (lastParam != null) { - ConvertCustomAttributes(astProp.Setter, lastParam, "param"); - if (lastParam.HasMarshalInfo) { - astProp.Setter.Attributes.Add(new AttributeSection(ConvertMarshalInfo(lastParam, propDef.Module)) { AttributeTarget = "param" }); - } - } - - if ((setterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask)) - astProp.Setter.Modifiers = setterModifiers & Modifiers.VisibilityMask; - } - ConvertCustomAttributes(astProp, propDef); - - EntityDeclaration member = astProp; - if(propDef.IsIndexer()) - member = ConvertPropertyToIndexer(astProp, propDef); - if(!accessor.HasOverrides && !accessor.DeclaringType.IsInterface) - if (accessor.IsVirtual == accessor.IsNewSlot) - SetNewModifier(member); - return member; - } - - IndexerDeclaration ConvertPropertyToIndexer(PropertyDeclaration astProp, PropertyDefinition propDef) - { - var astIndexer = new IndexerDeclaration(); - astIndexer.CopyAnnotationsFrom(astProp); - astProp.Attributes.MoveTo(astIndexer.Attributes); - astIndexer.Modifiers = astProp.Modifiers; - astIndexer.PrivateImplementationType = astProp.PrivateImplementationType.Detach(); - astIndexer.ReturnType = astProp.ReturnType.Detach(); - astIndexer.Getter = astProp.Getter.Detach(); - astIndexer.Setter = astProp.Setter.Detach(); - astIndexer.Parameters.AddRange(MakeParameters(propDef.Parameters)); - return astIndexer; - } - - EntityDeclaration CreateEvent(EventDefinition eventDef) - { - if (eventDef.AddMethod != null && eventDef.AddMethod.IsAbstract) { - // An abstract event cannot be custom - EventDeclaration astEvent = new EventDeclaration(); - ConvertCustomAttributes(astEvent, eventDef); - astEvent.AddAnnotation(eventDef); - astEvent.Variables.Add(new VariableInitializer(CleanName(eventDef.Name))); - astEvent.ReturnType = ConvertType(eventDef.EventType, eventDef); - if (!eventDef.DeclaringType.IsInterface) - astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod); - return astEvent; - } else { - CustomEventDeclaration astEvent = new CustomEventDeclaration(); - ConvertCustomAttributes(astEvent, eventDef); - astEvent.AddAnnotation(eventDef); - astEvent.Name = CleanName(eventDef.Name); - astEvent.ReturnType = ConvertType(eventDef.EventType, eventDef); - if (eventDef.AddMethod == null || !IsExplicitInterfaceImplementation(eventDef.AddMethod)) - astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod); - else - astEvent.PrivateImplementationType = ConvertType(eventDef.AddMethod.Overrides.First().DeclaringType); - - if (eventDef.AddMethod != null) { - astEvent.AddAccessor = new Accessor { - Body = CreateMethodBody(eventDef.AddMethod) - }.WithAnnotation(eventDef.AddMethod); - ConvertAttributes(astEvent.AddAccessor, eventDef.AddMethod); - } - if (eventDef.RemoveMethod != null) { - astEvent.RemoveAccessor = new Accessor { - Body = CreateMethodBody(eventDef.RemoveMethod) - }.WithAnnotation(eventDef.RemoveMethod); - ConvertAttributes(astEvent.RemoveAccessor, eventDef.RemoveMethod); - } - MethodDefinition accessor = eventDef.AddMethod ?? eventDef.RemoveMethod; - if (accessor.IsVirtual == accessor.IsNewSlot) { - SetNewModifier(astEvent); - } - return astEvent; - } - } - - public bool DecompileMethodBodies { get; set; } - - BlockStatement CreateMethodBody(MethodDefinition method, IEnumerable<ParameterDeclaration> parameters = null) - { - if (DecompileMethodBodies) - return AstMethodBodyBuilder.CreateMethodBody(method, context, parameters); - else - return null; - } - - FieldDeclaration CreateField(FieldDefinition fieldDef) - { - FieldDeclaration astField = new FieldDeclaration(); - astField.AddAnnotation(fieldDef); - VariableInitializer initializer = new VariableInitializer(CleanName(fieldDef.Name)); - astField.AddChild(initializer, Roles.Variable); - astField.ReturnType = ConvertType(fieldDef.FieldType, fieldDef); - astField.Modifiers = ConvertModifiers(fieldDef); - if (fieldDef.HasConstant) { - initializer.Initializer = CreateExpressionForConstant(fieldDef.Constant, fieldDef.FieldType, fieldDef.DeclaringType.IsEnum); - } - ConvertAttributes(astField, fieldDef); - SetNewModifier(astField); - return astField; - } - - static Expression CreateExpressionForConstant(object constant, TypeReference type, bool isEnumMemberDeclaration = false) - { - if (constant == null) { - if (type.IsValueType && !(type.Namespace == "System" && type.Name == "Nullable`1")) - return new DefaultValueExpression(ConvertType(type)); - else - return new NullReferenceExpression(); - } else { - TypeCode c = Type.GetTypeCode(constant.GetType()); - if (c >= TypeCode.SByte && c <= TypeCode.UInt64 && !isEnumMemberDeclaration) { - return MakePrimitive((long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constant, false), type); - } else { - return new PrimitiveExpression(constant); - } - } - } - - public static IEnumerable<ParameterDeclaration> MakeParameters(MethodDefinition method, bool isLambda = false) - { - var parameters = MakeParameters(method.Parameters, isLambda); - if (method.CallingConvention == MethodCallingConvention.VarArg) { - return parameters.Concat(new[] { new ParameterDeclaration { Type = new PrimitiveType("__arglist") } }); - } else { - return parameters; - } - } - - public static IEnumerable<ParameterDeclaration> MakeParameters(IEnumerable<ParameterDefinition> paramCol, bool isLambda = false) - { - foreach(ParameterDefinition paramDef in paramCol) { - ParameterDeclaration astParam = new ParameterDeclaration(); - astParam.AddAnnotation(paramDef); - if (!(isLambda && paramDef.ParameterType.ContainsAnonymousType())) - astParam.Type = ConvertType(paramDef.ParameterType, paramDef); - astParam.Name = paramDef.Name; - - if (paramDef.ParameterType is ByReferenceType) { - astParam.ParameterModifier = (!paramDef.IsIn && paramDef.IsOut) ? ParameterModifier.Out : ParameterModifier.Ref; - ComposedType ct = astParam.Type as ComposedType; - if (ct != null && ct.PointerRank > 0) - ct.PointerRank--; - } - - if (paramDef.HasCustomAttributes) { - foreach (CustomAttribute ca in paramDef.CustomAttributes) { - if (ca.AttributeType.Name == "ParamArrayAttribute" && ca.AttributeType.Namespace == "System") - astParam.ParameterModifier = ParameterModifier.Params; - } - } - if (paramDef.IsOptional) { - astParam.DefaultExpression = CreateExpressionForConstant(paramDef.Constant, paramDef.ParameterType); - } - - 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(EntityDeclaration attributedNode, TypeDefinition typeDefinition) - { - ConvertCustomAttributes(attributedNode, typeDefinition); - ConvertSecurityAttributes(attributedNode, typeDefinition); - - // Handle the non-custom attributes: - #region SerializableAttribute - if (typeDefinition.IsSerializable) - attributedNode.Attributes.Add(new AttributeSection(CreateNonCustomAttribute(typeof(SerializableAttribute)))); - #endregion - - #region ComImportAttribute - if (typeDefinition.IsImport) - attributedNode.Attributes.Add(new AttributeSection(CreateNonCustomAttribute(typeof(ComImportAttribute)))); - #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(EntityDeclaration attributedNode, MethodDefinition methodDefinition) - { - ConvertCustomAttributes(attributedNode, methodDefinition); - ConvertSecurityAttributes(attributedNode, methodDefinition); - - MethodImplAttributes implAttributes = methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask; - - #region DllImportAttribute - if (methodDefinition.HasPInvokeInfo && methodDefinition.PInvokeInfo != null) { - 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 - - ConvertAttributes(attributedNode, methodDefinition.MethodReturnType, methodDefinition.Module); - } - - void ConvertAttributes(EntityDeclaration attributedNode, MethodReturnType methodReturnType, ModuleDefinition module) - { - ConvertCustomAttributes(attributedNode, methodReturnType, "return"); - if (methodReturnType.HasMarshalInfo) { - var marshalInfo = ConvertMarshalInfo(methodReturnType, module); - attributedNode.Attributes.Add(new AttributeSection(marshalInfo) { AttributeTarget = "return" }); - } - } - - internal static void ConvertAttributes(EntityDeclaration attributedNode, FieldDefinition fieldDefinition, string attributeTarget = null) - { - ConvertCustomAttributes(attributedNode, fieldDefinition); - - #region FieldOffsetAttribute - if (fieldDefinition.HasLayoutInfo) { - Ast.Attribute fieldOffset = CreateNonCustomAttribute(typeof(FieldOffsetAttribute), fieldDefinition.Module); - fieldOffset.Arguments.Add(new PrimitiveExpression(fieldDefinition.Offset)); - attributedNode.Attributes.Add(new AttributeSection(fieldOffset) { AttributeTarget = attributeTarget }); - } - #endregion - - #region NonSerializedAttribute - if (fieldDefinition.IsNotSerialized) { - Ast.Attribute nonSerialized = CreateNonCustomAttribute(typeof(NonSerializedAttribute), fieldDefinition.Module); - attributedNode.Attributes.Add(new AttributeSection(nonSerialized) { AttributeTarget = attributeTarget }); - } - #endregion - - if (fieldDefinition.HasMarshalInfo) { - attributedNode.Attributes.Add(new AttributeSection(ConvertMarshalInfo(fieldDefinition, fieldDefinition.Module)) { AttributeTarget = attributeTarget }); - } - } - - #region MarshalAsAttribute (ConvertMarshalInfo) - static Ast.Attribute ConvertMarshalInfo(IMarshalInfoProvider marshalInfoProvider, ModuleDefinition module) - { - MarshalInfo marshalInfo = marshalInfoProvider.MarshalInfo; - Ast.Attribute attr = CreateNonCustomAttribute(typeof(MarshalAsAttribute), module); - var unmanagedType = new TypeReference("System.Runtime.InteropServices", "UnmanagedType", module, module.TypeSystem.Corlib); - attr.Arguments.Add(MakePrimitive((int)marshalInfo.NativeType, unmanagedType)); - - FixedArrayMarshalInfo fami = marshalInfo as FixedArrayMarshalInfo; - if (fami != null) { - attr.AddNamedArgument("SizeConst", new PrimitiveExpression(fami.Size)); - if (fami.ElementType != NativeType.None) - attr.AddNamedArgument("ArraySubType", MakePrimitive((int)fami.ElementType, unmanagedType)); - } - SafeArrayMarshalInfo sami = marshalInfo as SafeArrayMarshalInfo; - if (sami != null && sami.ElementType != VariantType.None) { - var varEnum = new TypeReference("System.Runtime.InteropServices", "VarEnum", module, module.TypeSystem.Corlib); - attr.AddNamedArgument("SafeArraySubType", MakePrimitive((int)sami.ElementType, varEnum)); - } - ArrayMarshalInfo ami = marshalInfo as ArrayMarshalInfo; - if (ami != null) { - if (ami.ElementType != NativeType.Max) - attr.AddNamedArgument("ArraySubType", MakePrimitive((int)ami.ElementType, unmanagedType)); - if (ami.Size >= 0) - attr.AddNamedArgument("SizeConst", new PrimitiveExpression(ami.Size)); - if (ami.SizeParameterMultiplier != 0 && ami.SizeParameterIndex >= 0) - attr.AddNamedArgument("SizeParamIndex", new PrimitiveExpression(ami.SizeParameterIndex)); - } - CustomMarshalInfo cmi = marshalInfo as CustomMarshalInfo; - if (cmi != null) { - attr.AddNamedArgument("MarshalType", new PrimitiveExpression(cmi.ManagedType.FullName)); - if (!string.IsNullOrEmpty(cmi.Cookie)) - attr.AddNamedArgument("MarshalCookie", new PrimitiveExpression(cmi.Cookie)); - } - FixedSysStringMarshalInfo fssmi = marshalInfo as FixedSysStringMarshalInfo; - if (fssmi != null) { - attr.AddNamedArgument("SizeConst", new PrimitiveExpression(fssmi.Size)); - } - return attr; - } - #endregion - - 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, string attributeTarget = null) - { - EntityDeclaration entityDecl = attributedNode as EntityDeclaration; - if (customAttributeProvider.HasCustomAttributes) { - var attributes = new List<ICSharpCode.NRefactory.CSharp.Attribute>(); - foreach (var customAttribute in customAttributeProvider.CustomAttributes.OrderBy(a => a.AttributeType.FullName)) { - if (customAttribute.AttributeType.Name == "ExtensionAttribute" && customAttribute.AttributeType.Namespace == "System.Runtime.CompilerServices") { - // don't show the ExtensionAttribute (it's converted to the 'this' modifier) - continue; - } - if (customAttribute.AttributeType.Name == "ParamArrayAttribute" && customAttribute.AttributeType.Namespace == "System") { - // don't show the ParamArrayAttribute (it's converted to the 'params' modifier) - continue; - } - // if the method is async, remove [DebuggerStepThrough] and [Async - if (entityDecl != null && entityDecl.HasModifier(Modifiers.Async)) { - if (customAttribute.AttributeType.Name == "DebuggerStepThroughAttribute" && customAttribute.AttributeType.Namespace == "System.Diagnostics") { - continue; - } - if (customAttribute.AttributeType.Name == "AsyncStateMachineAttribute" && customAttribute.AttributeType.Namespace == "System.Runtime.CompilerServices") { - continue; - } - } - - var attribute = new ICSharpCode.NRefactory.CSharp.Attribute(); - attribute.AddAnnotation(customAttribute); - attribute.Type = ConvertType(customAttribute.AttributeType); - attributes.Add(attribute); - - SimpleType st = attribute.Type as SimpleType; - if (st != null && st.Identifier.EndsWith("Attribute", StringComparison.Ordinal)) { - st.Identifier = st.Identifier.Substring(0, st.Identifier.Length - "Attribute".Length); - } - - if(customAttribute.HasConstructorArguments) { - foreach (var parameter in customAttribute.ConstructorArguments) { - Expression parameterValue = ConvertArgumentValue(parameter); - attribute.Arguments.Add(parameterValue); - } - } - if (customAttribute.HasProperties) { - TypeDefinition resolvedAttributeType = customAttribute.AttributeType.Resolve(); - foreach (var propertyNamedArg in customAttribute.Properties) { - var propertyReference = resolvedAttributeType != null ? resolvedAttributeType.Properties.FirstOrDefault(pr => pr.Name == propertyNamedArg.Name) : null; - var propertyName = new IdentifierExpression(propertyNamedArg.Name).WithAnnotation(propertyReference); - var argumentValue = ConvertArgumentValue(propertyNamedArg.Argument); - attribute.Arguments.Add(new AssignmentExpression(propertyName, argumentValue)); - } - } - - if (customAttribute.HasFields) { - TypeDefinition resolvedAttributeType = customAttribute.AttributeType.Resolve(); - foreach (var fieldNamedArg in customAttribute.Fields) { - var fieldReference = resolvedAttributeType != null ? resolvedAttributeType.Fields.FirstOrDefault(f => f.Name == fieldNamedArg.Name) : null; - var fieldName = new IdentifierExpression(fieldNamedArg.Name).WithAnnotation(fieldReference); - var argumentValue = ConvertArgumentValue(fieldNamedArg.Argument); - attribute.Arguments.Add(new AssignmentExpression(fieldName, argumentValue)); - } - } - } - - if (attributeTarget == "module" || attributeTarget == "assembly") { - // use separate section for each attribute - foreach (var attribute in attributes) { - var section = new AttributeSection(); - section.AttributeTarget = attributeTarget; - section.Attributes.Add(attribute); - attributedNode.AddChild(section, EntityDeclaration.AttributeRole); - } - } else if (attributes.Count > 0) { - // use single section for all attributes - var section = new AttributeSection(); - section.AttributeTarget = attributeTarget; - section.Attributes.AddRange(attributes); - attributedNode.AddChild(section, EntityDeclaration.AttributeRole); - } - } - } - - static void ConvertSecurityAttributes(AstNode attributedNode, ISecurityDeclarationProvider secDeclProvider, string attributeTarget = null) - { - if (!secDeclProvider.HasSecurityDeclarations) - return; - var attributes = new List<ICSharpCode.NRefactory.CSharp.Attribute>(); - foreach (var secDecl in secDeclProvider.SecurityDeclarations.OrderBy(d => d.Action)) { - foreach (var secAttribute in secDecl.SecurityAttributes.OrderBy(a => a.AttributeType.FullName)) { - var attribute = new ICSharpCode.NRefactory.CSharp.Attribute(); - attribute.AddAnnotation(secAttribute); - attribute.Type = ConvertType(secAttribute.AttributeType); - attributes.Add(attribute); - - SimpleType st = attribute.Type as SimpleType; - if (st != null && st.Identifier.EndsWith("Attribute", StringComparison.Ordinal)) { - st.Identifier = st.Identifier.Substring(0, st.Identifier.Length - "Attribute".Length); - } - - var module = secAttribute.AttributeType.Module; - var securityActionType = new TypeReference("System.Security.Permissions", "SecurityAction", module, module.TypeSystem.Corlib); - attribute.Arguments.Add(MakePrimitive((int)secDecl.Action, securityActionType)); - - if (secAttribute.HasProperties) { - TypeDefinition resolvedAttributeType = secAttribute.AttributeType.Resolve(); - foreach (var propertyNamedArg in secAttribute.Properties) { - var propertyReference = resolvedAttributeType != null ? resolvedAttributeType.Properties.FirstOrDefault(pr => pr.Name == propertyNamedArg.Name) : null; - var propertyName = new IdentifierExpression(propertyNamedArg.Name).WithAnnotation(propertyReference); - var argumentValue = ConvertArgumentValue(propertyNamedArg.Argument); - attribute.Arguments.Add(new AssignmentExpression(propertyName, argumentValue)); - } - } - - if (secAttribute.HasFields) { - TypeDefinition resolvedAttributeType = secAttribute.AttributeType.Resolve(); - foreach (var fieldNamedArg in secAttribute.Fields) { - var fieldReference = resolvedAttributeType != null ? resolvedAttributeType.Fields.FirstOrDefault(f => f.Name == fieldNamedArg.Name) : null; - var fieldName = new IdentifierExpression(fieldNamedArg.Name).WithAnnotation(fieldReference); - var argumentValue = ConvertArgumentValue(fieldNamedArg.Argument); - attribute.Arguments.Add(new AssignmentExpression(fieldName, argumentValue)); - } - } - } - } - if (attributeTarget == "module" || attributeTarget == "assembly") { - // use separate section for each attribute - foreach (var attribute in attributes) { - var section = new AttributeSection(); - section.AttributeTarget = attributeTarget; - section.Attributes.Add(attribute); - attributedNode.AddChild(section, EntityDeclaration.AttributeRole); - } - } else if (attributes.Count > 0) { - // use single section for all attributes - var section = new AttributeSection(); - section.AttributeTarget = attributeTarget; - section.Attributes.AddRange(attributes); - attributedNode.AddChild(section, EntityDeclaration.AttributeRole); - } - } - - private static Expression ConvertArgumentValue(CustomAttributeArgument argument) - { - if (argument.Value is CustomAttributeArgument[]) { - ArrayInitializerExpression arrayInit = new ArrayInitializerExpression(); - foreach (CustomAttributeArgument element in (CustomAttributeArgument[])argument.Value) { - arrayInit.Elements.Add(ConvertArgumentValue(element)); - } - ArrayType arrayType = argument.Type as ArrayType; - return new ArrayCreateExpression { - Type = ConvertType(arrayType != null ? arrayType.ElementType : argument.Type), - AdditionalArraySpecifiers = { new ArraySpecifier() }, - Initializer = arrayInit - }; - } else if (argument.Value is CustomAttributeArgument) { - // occurs with boxed arguments - return ConvertArgumentValue((CustomAttributeArgument)argument.Value); - } - var type = argument.Type.Resolve(); - if (type != null && type.IsEnum) { - return MakePrimitive(Convert.ToInt64(argument.Value), type); - } else if (argument.Value is TypeReference) { - return CreateTypeOfExpression((TypeReference)argument.Value); - } else { - return new PrimitiveExpression(argument.Value); - } - } - #endregion - - internal static Expression MakePrimitive(long val, TypeReference type) - { - if (TypeAnalysis.IsBoolean(type) && val == 0) - return new Ast.PrimitiveExpression(false); - else if (TypeAnalysis.IsBoolean(type) && val == 1) - return new Ast.PrimitiveExpression(true); - else if (val == 0 && type is PointerType) - return new Ast.NullReferenceExpression(); - if (type != null) - { // cannot rely on type.IsValueType, it's not set for typerefs (but is set for typespecs) - TypeDefinition enumDefinition = type.Resolve(); - if (enumDefinition != null && enumDefinition.IsEnum) { - TypeCode enumBaseTypeCode = TypeCode.Int32; - foreach (FieldDefinition field in enumDefinition.Fields) { - if (field.IsStatic && object.Equals(CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false), val)) - return ConvertType(type).Member(field.Name).WithAnnotation(field); - else if (!field.IsStatic) - enumBaseTypeCode = TypeAnalysis.GetTypeCode(field.FieldType); // use primitive type of the enum - } - if (IsFlagsEnum(enumDefinition)) { - long enumValue = val; - Expression expr = null; - long negatedEnumValue = ~val; - // limit negatedEnumValue to the appropriate range - switch (enumBaseTypeCode) { - case TypeCode.Byte: - case TypeCode.SByte: - negatedEnumValue &= byte.MaxValue; - break; - case TypeCode.Int16: - case TypeCode.UInt16: - negatedEnumValue &= ushort.MaxValue; - break; - case TypeCode.Int32: - case TypeCode.UInt32: - negatedEnumValue &= uint.MaxValue; - break; - } - Expression negatedExpr = null; - foreach (FieldDefinition field in enumDefinition.Fields.Where(fld => fld.IsStatic)) { - long fieldValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false); - if (fieldValue == 0) - continue; // skip None enum value - - if ((fieldValue & enumValue) == fieldValue) { - var fieldExpression = ConvertType(type).Member(field.Name).WithAnnotation(field); - if (expr == null) - expr = fieldExpression; - else - expr = new BinaryOperatorExpression(expr, BinaryOperatorType.BitwiseOr, fieldExpression); - - enumValue &= ~fieldValue; - } - if ((fieldValue & negatedEnumValue) == fieldValue) { - var fieldExpression = ConvertType(type).Member(field.Name).WithAnnotation(field); - if (negatedExpr == null) - negatedExpr = fieldExpression; - else - negatedExpr = new BinaryOperatorExpression(negatedExpr, BinaryOperatorType.BitwiseOr, fieldExpression); - - negatedEnumValue &= ~fieldValue; - } - } - if (enumValue == 0 && expr != null) { - if (!(negatedEnumValue == 0 && negatedExpr != null && negatedExpr.Descendants.Count() < expr.Descendants.Count())) { - return expr; - } - } - if (negatedEnumValue == 0 && negatedExpr != null) { - return new UnaryOperatorExpression(UnaryOperatorType.BitNot, negatedExpr); - } - } - return new Ast.PrimitiveExpression(CSharpPrimitiveCast.Cast(enumBaseTypeCode, val, false)).CastTo(ConvertType(type)); - } - } - TypeCode code = TypeAnalysis.GetTypeCode(type); - if (code == TypeCode.Object || code == TypeCode.Empty) - code = TypeCode.Int32; - return new Ast.PrimitiveExpression(CSharpPrimitiveCast.Cast(code, val, false)); - } - - static bool IsFlagsEnum(TypeDefinition type) - { - if (!type.HasCustomAttributes) - return false; - - return type.CustomAttributes.Any(attr => attr.AttributeType.FullName == "System.FlagsAttribute"); - } - - /// <summary> - /// Sets new modifier if the member hides some other member from a base type. - /// </summary> - /// <param name="member">The node of the member which new modifier state should be determined.</param> - static void SetNewModifier(EntityDeclaration member) - { - try { - bool addNewModifier = false; - if (member is IndexerDeclaration) { - var propertyDef = member.Annotation<PropertyDefinition>(); - var baseProperties = - TypesHierarchyHelpers.FindBaseProperties(propertyDef); - addNewModifier = baseProperties.Any(); - } else - addNewModifier = HidesBaseMember(member); - - if (addNewModifier) - member.Modifiers |= Modifiers.New; - } - catch (ReferenceResolvingException) { - // TODO: add some kind of notification (a comment?) about possible problems with decompiled code due to unresolved references. - } - } - - private static bool HidesBaseMember(EntityDeclaration member) - { - var memberDefinition = member.Annotation<IMemberDefinition>(); - bool addNewModifier = false; - var methodDefinition = memberDefinition as MethodDefinition; - if (methodDefinition != null) { - addNewModifier = HidesByName(memberDefinition, includeBaseMethods: false); - if (!addNewModifier) - addNewModifier = TypesHierarchyHelpers.FindBaseMethods(methodDefinition).Any(); - } else - addNewModifier = HidesByName(memberDefinition, includeBaseMethods: true); - return addNewModifier; - } - - /// <summary> - /// Determines whether any base class member has the same name as the given member. - /// </summary> - /// <param name="member">The derived type's member.</param> - /// <param name="includeBaseMethods">true if names of methods declared in base types should also be checked.</param> - /// <returns>true if any base member has the same name as given member, otherwise false.</returns> - static bool HidesByName(IMemberDefinition member, bool includeBaseMethods) - { - Debug.Assert(!(member is PropertyDefinition) || !((PropertyDefinition)member).IsIndexer()); - - if (member.DeclaringType.BaseType != null) { - var baseTypeRef = member.DeclaringType.BaseType; - while (baseTypeRef != null) { - var baseType = baseTypeRef.ResolveOrThrow(); - if (baseType.HasProperties && AnyIsHiddenBy(baseType.Properties, member, m => !m.IsIndexer())) - return true; - if (baseType.HasEvents && AnyIsHiddenBy(baseType.Events, member)) - return true; - if (baseType.HasFields && AnyIsHiddenBy(baseType.Fields, member)) - return true; - if (includeBaseMethods && baseType.HasMethods - && AnyIsHiddenBy(baseType.Methods, member, m => !m.IsSpecialName)) - return true; - if (baseType.HasNestedTypes && AnyIsHiddenBy(baseType.NestedTypes, member)) - return true; - baseTypeRef = baseType.BaseType; - } - } - return false; - } - - static bool AnyIsHiddenBy<T>(IEnumerable<T> members, IMemberDefinition derived, Predicate<T> condition = null) - where T : IMemberDefinition - { - return members.Any(m => m.Name == derived.Name - && (condition == null || condition(m)) - && TypesHierarchyHelpers.IsVisibleFromDerived(m, derived.DeclaringType)); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/main/contrib/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs deleted file mode 100644 index 2762ffbc5d..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ /dev/null @@ -1,1220 +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.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; - -using ICSharpCode.Decompiler.Ast.Transforms; -using ICSharpCode.Decompiler.ILAst; -using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.PatternMatching; -using ICSharpCode.NRefactory.Utils; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.Ast -{ - using Ast = ICSharpCode.NRefactory.CSharp; - using Cecil = Mono.Cecil; - - public class AstMethodBodyBuilder - { - MethodDefinition methodDef; - TypeSystem typeSystem; - DecompilerContext context; - HashSet<ILVariable> localVariablesToDefine = new HashSet<ILVariable>(); // local variables that are missing a definition - - /// <summary> - /// Creates the body for the method definition. - /// </summary> - /// <param name="methodDef">Method definition to decompile.</param> - /// <param name="context">Decompilation context.</param> - /// <param name="parameters">Parameter declarations of the method being decompiled. - /// These are used to update the parameter names when the decompiler generates names for the parameters.</param> - /// <returns>Block for the method body</returns> - public static BlockStatement CreateMethodBody(MethodDefinition methodDef, - DecompilerContext context, - IEnumerable<ParameterDeclaration> parameters = null) - { - MethodDefinition oldCurrentMethod = context.CurrentMethod; - Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef); - context.CurrentMethod = methodDef; - context.CurrentMethodIsAsync = false; - try { - AstMethodBodyBuilder builder = new AstMethodBodyBuilder(); - builder.methodDef = methodDef; - builder.context = context; - builder.typeSystem = methodDef.Module.TypeSystem; - if (Debugger.IsAttached) { - return builder.CreateMethodBody(parameters, methodDef); - } else { - try { - return builder.CreateMethodBody(parameters, methodDef); - } catch (OperationCanceledException) { - throw; - } catch (Exception ex) { - throw new ICSharpCode.Decompiler.DecompilerException(methodDef, ex); - } - } - } finally { - context.CurrentMethod = oldCurrentMethod; - } - } - - public BlockStatement CreateMethodBody(IEnumerable<ParameterDeclaration> parameters, MethodDefinition mref) - { - if (methodDef.Body == null) { - return null; - } - - context.CancellationToken.ThrowIfCancellationRequested(); - ILBlock ilMethod = new ILBlock(); - ILAstBuilder astBuilder = new ILAstBuilder(); - ilMethod.Body = astBuilder.Build(methodDef, true, context); - - context.CancellationToken.ThrowIfCancellationRequested(); - ILAstOptimizer bodyGraph = new ILAstOptimizer(); - bodyGraph.Optimize(context, ilMethod); - context.CancellationToken.ThrowIfCancellationRequested(); - - var localVariables = ilMethod.GetSelfAndChildrenRecursive<ILExpression>().Select(e => e.Operand as ILVariable) - .Where(v => v != null && !v.IsParameter).Distinct(); - Debug.Assert(context.CurrentMethod == methodDef); - NameVariables.AssignNamesToVariables(context, astBuilder.Parameters, localVariables, ilMethod, methodDef); - - if (parameters != null) { - foreach (var pair in (from p in parameters - join v in astBuilder.Parameters on p.Annotation<ParameterDefinition>() equals v.OriginalParameter - select new { p, v.Name })) - { - pair.p.Name = pair.Name; - } - } - - context.CancellationToken.ThrowIfCancellationRequested(); - Ast.BlockStatement astBlock = TransformBlock(ilMethod); - CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments - - Statement insertionPoint = astBlock.Statements.FirstOrDefault(); - foreach (ILVariable v in localVariablesToDefine) { - AstType type; - if (v.Type.ContainsAnonymousType()) - type = new SimpleType("var"); - else - type = AstBuilder.ConvertType(v.Type); - var newVarDecl = new VariableDeclarationStatement(type, v.Name); - newVarDecl.Variables.Single().AddAnnotation(v); - astBlock.Statements.InsertBefore(insertionPoint, newVarDecl); - } - - astBlock.AddAnnotation(new MethodDebugSymbols(methodDef) { LocalVariables = localVariables.ToList() }); - - return astBlock; - } - - Ast.BlockStatement TransformBlock(ILBlock block) - { - Ast.BlockStatement astBlock = new BlockStatement(); - if (block != null) { - foreach(ILNode node in block.GetChildren()) { - astBlock.Statements.AddRange(TransformNode(node)); - } - } - return astBlock; - } - - IEnumerable<Statement> TransformNode(ILNode node) - { - if (node is ILLabel) { - yield return new Ast.LabelStatement { Label = ((ILLabel)node).Name }; - } else if (node is ILExpression) { - List<ILRange> ilRanges = ILRange.OrderAndJoin(node.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.ILRanges)); - AstNode codeExpr = TransformExpression((ILExpression)node); - if (codeExpr != null) { - codeExpr = codeExpr.WithAnnotation(ilRanges); - if (codeExpr is Ast.Expression) { - yield return new Ast.ExpressionStatement { Expression = (Ast.Expression)codeExpr }; - } else if (codeExpr is Ast.Statement) { - yield return (Ast.Statement)codeExpr; - } else { - throw new Exception(); - } - } - } else if (node is ILWhileLoop) { - ILWhileLoop ilLoop = (ILWhileLoop)node; - WhileStatement whileStmt = new WhileStatement() { - Condition = ilLoop.Condition != null ? (Expression)TransformExpression(ilLoop.Condition) : new PrimitiveExpression(true), - EmbeddedStatement = TransformBlock(ilLoop.BodyBlock) - }; - yield return whileStmt; - } else if (node is ILCondition) { - ILCondition conditionalNode = (ILCondition)node; - bool hasFalseBlock = conditionalNode.FalseBlock.EntryGoto != null || conditionalNode.FalseBlock.Body.Count > 0; - yield return new Ast.IfElseStatement { - Condition = (Expression)TransformExpression(conditionalNode.Condition), - TrueStatement = TransformBlock(conditionalNode.TrueBlock), - FalseStatement = hasFalseBlock ? TransformBlock(conditionalNode.FalseBlock) : null - }; - } else if (node is ILSwitch) { - ILSwitch ilSwitch = (ILSwitch)node; - if (TypeAnalysis.IsBoolean(ilSwitch.Condition.InferredType) && ( - from cb in ilSwitch.CaseBlocks - where cb.Values != null - from val in cb.Values - select val - ).Any(val => val != 0 && val != 1)) - { - // If switch cases contain values other then 0 and 1, force the condition to be non-boolean - ilSwitch.Condition.ExpectedType = typeSystem.Int32; - } - SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition) }; - foreach (var caseBlock in ilSwitch.CaseBlocks) { - SwitchSection section = new SwitchSection(); - if (caseBlock.Values != null) { - section.CaseLabels.AddRange(caseBlock.Values.Select(i => new CaseLabel() { Expression = AstBuilder.MakePrimitive(i, ilSwitch.Condition.ExpectedType ?? ilSwitch.Condition.InferredType) })); - } else { - section.CaseLabels.Add(new CaseLabel()); - } - section.Statements.Add(TransformBlock(caseBlock)); - switchStmt.SwitchSections.Add(section); - } - yield return switchStmt; - } else if (node is ILTryCatchBlock) { - ILTryCatchBlock tryCatchNode = ((ILTryCatchBlock)node); - var tryCatchStmt = new Ast.TryCatchStatement(); - tryCatchStmt.TryBlock = TransformBlock(tryCatchNode.TryBlock); - foreach (var catchClause in tryCatchNode.CatchBlocks) { - if (catchClause.ExceptionVariable == null - && (catchClause.ExceptionType == null || catchClause.ExceptionType.MetadataType == MetadataType.Object)) - { - tryCatchStmt.CatchClauses.Add(new Ast.CatchClause { Body = TransformBlock(catchClause) }); - } else { - tryCatchStmt.CatchClauses.Add( - new Ast.CatchClause { - Type = AstBuilder.ConvertType(catchClause.ExceptionType), - VariableName = catchClause.ExceptionVariable == null ? null : catchClause.ExceptionVariable.Name, - Body = TransformBlock(catchClause) - }.WithAnnotation(catchClause.ExceptionVariable)); - } - } - if (tryCatchNode.FinallyBlock != null) - tryCatchStmt.FinallyBlock = TransformBlock(tryCatchNode.FinallyBlock); - if (tryCatchNode.FaultBlock != null) { - CatchClause cc = new CatchClause(); - cc.Body = TransformBlock(tryCatchNode.FaultBlock); - cc.Body.Add(new ThrowStatement()); // rethrow - tryCatchStmt.CatchClauses.Add(cc); - } - yield return tryCatchStmt; - } else if (node is ILFixedStatement) { - ILFixedStatement fixedNode = (ILFixedStatement)node; - FixedStatement fixedStatement = new FixedStatement(); - foreach (ILExpression initializer in fixedNode.Initializers) { - Debug.Assert(initializer.Code == ILCode.Stloc); - ILVariable v = (ILVariable)initializer.Operand; - fixedStatement.Variables.Add( - new VariableInitializer { - Name = v.Name, - Initializer = (Expression)TransformExpression(initializer.Arguments[0]) - }.WithAnnotation(v)); - } - fixedStatement.Type = AstBuilder.ConvertType(((ILVariable)fixedNode.Initializers[0].Operand).Type); - fixedStatement.EmbeddedStatement = TransformBlock(fixedNode.BodyBlock); - yield return fixedStatement; - } else if (node is ILBlock) { - yield return TransformBlock((ILBlock)node); - } else { - throw new Exception("Unknown node type"); - } - } - - AstNode TransformExpression(ILExpression expr) - { - AstNode node = TransformByteCode(expr); - Expression astExpr = node as Expression; - - // get IL ranges - used in debugger - List<ILRange> ilRanges = ILRange.OrderAndJoin(expr.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.ILRanges)); - AstNode result; - - if (astExpr != null) - result = Convert(astExpr, expr.InferredType, expr.ExpectedType); - else - result = node; - - if (result != null) - result = result.WithAnnotation(new TypeInformation(expr.InferredType, expr.ExpectedType)); - - if (result != null) - return result.WithAnnotation(ilRanges); - - return result; - } - - AstNode TransformByteCode(ILExpression byteCode) - { - object operand = byteCode.Operand; - AstType operandAsTypeRef = AstBuilder.ConvertType(operand as Cecil.TypeReference); - - List<Ast.Expression> args = new List<Expression>(); - foreach(ILExpression arg in byteCode.Arguments) { - args.Add((Ast.Expression)TransformExpression(arg)); - } - Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; - Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; - Ast.Expression arg3 = args.Count >= 3 ? args[2] : null; - - switch (byteCode.Code) { - #region Arithmetic - case ILCode.Add: - case ILCode.Add_Ovf: - case ILCode.Add_Ovf_Un: - { - BinaryOperatorExpression boe; - if (byteCode.InferredType is PointerType) { - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); - if (byteCode.Arguments[0].ExpectedType is PointerType || - byteCode.Arguments[1].ExpectedType is PointerType) { - boe.AddAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation); - } - } else { - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); - } - boe.AddAnnotation(byteCode.Code == ILCode.Add ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation); - return boe; - } - case ILCode.Sub: - case ILCode.Sub_Ovf: - case ILCode.Sub_Ovf_Un: - { - BinaryOperatorExpression boe; - if (byteCode.InferredType is PointerType) { - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); - if (byteCode.Arguments[0].ExpectedType is PointerType) { - boe.WithAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation); - } - } else { - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); - } - boe.AddAnnotation(byteCode.Code == ILCode.Sub ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation); - return boe; - } - case ILCode.Div: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); - case ILCode.Div_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); - case ILCode.Mul: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.UncheckedAnnotation); - case ILCode.Mul_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.CheckedAnnotation); - case ILCode.Mul_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.CheckedAnnotation); - case ILCode.Rem: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); - case ILCode.Rem_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); - case ILCode.And: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseAnd, arg2); - case ILCode.Or: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseOr, arg2); - case ILCode.Xor: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ExclusiveOr, arg2); - case ILCode.Shl: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftLeft, arg2); - case ILCode.Shr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); - case ILCode.Shr_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); - case ILCode.Neg: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Minus, arg1).WithAnnotation(AddCheckedBlocks.UncheckedAnnotation); - case ILCode.Not: return new Ast.UnaryOperatorExpression(UnaryOperatorType.BitNot, arg1); - case ILCode.PostIncrement: - case ILCode.PostIncrement_Ovf: - case ILCode.PostIncrement_Ovf_Un: - { - if (arg1 is DirectionExpression) - arg1 = ((DirectionExpression)arg1).Expression.Detach(); - var uoe = new Ast.UnaryOperatorExpression( - (int)byteCode.Operand > 0 ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement, arg1); - uoe.AddAnnotation((byteCode.Code == ILCode.PostIncrement) ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation); - return uoe; - } - #endregion - #region Arrays - case ILCode.Newarr: { - var ace = new Ast.ArrayCreateExpression(); - ace.Type = operandAsTypeRef; - ComposedType ct = operandAsTypeRef as ComposedType; - if (ct != null) { - // change "new (int[,])[10] to new int[10][,]" - ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); - } - if (byteCode.Code == ILCode.InitArray) { - ace.Initializer = new ArrayInitializerExpression(); - ace.Initializer.Elements.AddRange(args); - } else { - ace.Arguments.Add(arg1); - } - return ace; - } - case ILCode.InitArray: { - var ace = new Ast.ArrayCreateExpression(); - ace.Type = operandAsTypeRef; - ComposedType ct = operandAsTypeRef as ComposedType; - var arrayType = (ArrayType) operand; - if (ct != null) - { - // change "new (int[,])[10] to new int[10][,]" - ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); - ace.Initializer = new ArrayInitializerExpression(); - } - var newArgs = new List<Expression>(); - foreach (var arrayDimension in arrayType.Dimensions.Skip(1).Reverse()) - { - int length = (int)arrayDimension.UpperBound - (int)arrayDimension.LowerBound; - for (int j = 0; j < args.Count; j += length) - { - var child = new ArrayInitializerExpression(); - child.Elements.AddRange(args.GetRange(j, length)); - newArgs.Add(child); - } - var temp = args; - args = newArgs; - newArgs = temp; - newArgs.Clear(); - } - ace.Initializer.Elements.AddRange(args); - return ace; - } - case ILCode.Ldlen: return arg1.Member("Length"); - case ILCode.Ldelem_I: - case ILCode.Ldelem_I1: - case ILCode.Ldelem_I2: - case ILCode.Ldelem_I4: - case ILCode.Ldelem_I8: - case ILCode.Ldelem_U1: - case ILCode.Ldelem_U2: - case ILCode.Ldelem_U4: - case ILCode.Ldelem_R4: - case ILCode.Ldelem_R8: - case ILCode.Ldelem_Ref: - case ILCode.Ldelem_Any: - return arg1.Indexer(arg2); - case ILCode.Ldelema: - return MakeRef(arg1.Indexer(arg2)); - case ILCode.Stelem_I: - case ILCode.Stelem_I1: - case ILCode.Stelem_I2: - case ILCode.Stelem_I4: - case ILCode.Stelem_I8: - case ILCode.Stelem_R4: - case ILCode.Stelem_R8: - case ILCode.Stelem_Ref: - case ILCode.Stelem_Any: - return new Ast.AssignmentExpression(arg1.Indexer(arg2), arg3); - case ILCode.CompoundAssignment: - { - CastExpression cast = arg1 as CastExpression; - var boe = cast != null ? (BinaryOperatorExpression)cast.Expression : arg1 as BinaryOperatorExpression; - // AssignmentExpression doesn't support overloaded operators so they have to be processed to BinaryOperatorExpression - if (boe == null) { - var tmp = new ParenthesizedExpression(arg1); - ReplaceMethodCallsWithOperators.ProcessInvocationExpression((InvocationExpression)arg1); - boe = (BinaryOperatorExpression)tmp.Expression; - } - var assignment = new Ast.AssignmentExpression { - Left = boe.Left.Detach(), - Operator = ReplaceMethodCallsWithOperators.GetAssignmentOperatorForBinaryOperator(boe.Operator), - Right = boe.Right.Detach() - }.CopyAnnotationsFrom(boe); - // We do not mark the resulting assignment as RestoreOriginalAssignOperatorAnnotation, because - // the operator cannot be translated back to the expanded form (as the left-hand expression - // would be evaluated twice, and might have side-effects) - if (cast != null) { - cast.Expression = assignment; - return cast; - } else { - return assignment; - } - } - #endregion - #region Comparison - case ILCode.Ceq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); - case ILCode.Cne: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); - case ILCode.Cgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); - case ILCode.Cgt_Un: { - // can also mean Inequality, when used with object references - TypeReference arg1Type = byteCode.Arguments[0].InferredType; - if (arg1Type != null && !arg1Type.IsValueType) goto case ILCode.Cne; - - // when comparing signed integral values using Cgt_Un with 0 - // the Ast should actually contain InEquality since "(uint)a > 0u" is identical to "a != 0" - if (arg1Type.IsSignedIntegralType()) - { - var p = arg2 as Ast.PrimitiveExpression; - if (p != null && p.Value.IsZero()) goto case ILCode.Cne; - } - - goto case ILCode.Cgt; - } - case ILCode.Cle_Un: { - // can also mean Equality, when used with object references - TypeReference arg1Type = byteCode.Arguments[0].InferredType; - if (arg1Type != null && !arg1Type.IsValueType) goto case ILCode.Ceq; - - // when comparing signed integral values using Cle_Un with 0 - // the Ast should actually contain Equality since "(uint)a <= 0u" is identical to "a == 0" - if (arg1Type.IsSignedIntegralType()) - { - var p = arg2 as Ast.PrimitiveExpression; - if (p != null && p.Value.IsZero()) goto case ILCode.Ceq; - } - - goto case ILCode.Cle; - } - case ILCode.Cle: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); - case ILCode.Cge_Un: - case ILCode.Cge: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); - case ILCode.Clt_Un: - case ILCode.Clt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); - #endregion - #region Logical - case ILCode.LogicNot: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); - case ILCode.LogicAnd: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalAnd, arg2); - case ILCode.LogicOr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalOr, arg2); - case ILCode.TernaryOp: return new Ast.ConditionalExpression() { Condition = arg1, TrueExpression = arg2, FalseExpression = arg3 }; - case ILCode.NullCoalescing: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.NullCoalescing, arg2); - #endregion - #region Branch - case ILCode.Br: return new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name); - case ILCode.Brtrue: - return new Ast.IfElseStatement() { - Condition = arg1, - TrueStatement = new BlockStatement() { - new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name) - } - }; - case ILCode.LoopOrSwitchBreak: return new Ast.BreakStatement(); - case ILCode.LoopContinue: return new Ast.ContinueStatement(); - #endregion - #region Conversions - case ILCode.Conv_I1: - case ILCode.Conv_I2: - case ILCode.Conv_I4: - case ILCode.Conv_I8: - case ILCode.Conv_U1: - case ILCode.Conv_U2: - case ILCode.Conv_U4: - case ILCode.Conv_U8: - case ILCode.Conv_I: - case ILCode.Conv_U: - { - // conversion was handled by Convert() function using the info from type analysis - CastExpression cast = arg1 as CastExpression; - if (cast != null) { - cast.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation); - } - return arg1; - } - case ILCode.Conv_R4: - case ILCode.Conv_R8: - case ILCode.Conv_R_Un: // TODO - return arg1; - case ILCode.Conv_Ovf_I1: - case ILCode.Conv_Ovf_I2: - case ILCode.Conv_Ovf_I4: - case ILCode.Conv_Ovf_I8: - case ILCode.Conv_Ovf_U1: - case ILCode.Conv_Ovf_U2: - case ILCode.Conv_Ovf_U4: - case ILCode.Conv_Ovf_U8: - case ILCode.Conv_Ovf_I1_Un: - case ILCode.Conv_Ovf_I2_Un: - case ILCode.Conv_Ovf_I4_Un: - case ILCode.Conv_Ovf_I8_Un: - case ILCode.Conv_Ovf_U1_Un: - case ILCode.Conv_Ovf_U2_Un: - case ILCode.Conv_Ovf_U4_Un: - case ILCode.Conv_Ovf_U8_Un: - case ILCode.Conv_Ovf_I: - case ILCode.Conv_Ovf_U: - case ILCode.Conv_Ovf_I_Un: - case ILCode.Conv_Ovf_U_Un: - { - // conversion was handled by Convert() function using the info from type analysis - CastExpression cast = arg1 as CastExpression; - if (cast != null) { - cast.AddAnnotation(AddCheckedBlocks.CheckedAnnotation); - } - return arg1; - } - case ILCode.Unbox_Any: - // unboxing does not require a cast if the argument was an isinst instruction - if (arg1 is AsExpression && byteCode.Arguments[0].Code == ILCode.Isinst && TypeAnalysis.IsSameType(operand as TypeReference, byteCode.Arguments[0].Operand as TypeReference)) - return arg1; - else - goto case ILCode.Castclass; - case ILCode.Castclass: - if ((byteCode.Arguments[0].InferredType != null && byteCode.Arguments[0].InferredType.IsGenericParameter) || ((Cecil.TypeReference)operand).IsGenericParameter) - return arg1.CastTo(new PrimitiveType("object")).CastTo(operandAsTypeRef); - else - return arg1.CastTo(operandAsTypeRef); - case ILCode.Isinst: - return arg1.CastAs(operandAsTypeRef); - case ILCode.Box: - return arg1; - case ILCode.Unbox: - return MakeRef(arg1.CastTo(operandAsTypeRef)); - #endregion - #region Indirect - case ILCode.Ldind_Ref: - case ILCode.Ldobj: - if (arg1 is DirectionExpression) - return ((DirectionExpression)arg1).Expression.Detach(); - else - return new UnaryOperatorExpression(UnaryOperatorType.Dereference, arg1); - case ILCode.Stind_Ref: - case ILCode.Stobj: - if (arg1 is DirectionExpression) - return new AssignmentExpression(((DirectionExpression)arg1).Expression.Detach(), arg2); - else - return new AssignmentExpression(new UnaryOperatorExpression(UnaryOperatorType.Dereference, arg1), arg2); - #endregion - case ILCode.Arglist: - return new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.ArgListAccess }; - case ILCode.Break: return InlineAssembly(byteCode, args); - case ILCode.Call: - case ILCode.CallGetter: - case ILCode.CallSetter: - return TransformCall(false, byteCode, args); - case ILCode.Callvirt: - case ILCode.CallvirtGetter: - case ILCode.CallvirtSetter: - return TransformCall(true, byteCode, args); - case ILCode.Ldftn: { - Cecil.MethodReference cecilMethod = ((MethodReference)operand); - var expr = new Ast.IdentifierExpression(cecilMethod.Name); - expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); - expr.AddAnnotation(cecilMethod); - return new IdentifierExpression("ldftn").Invoke(expr) - .WithAnnotation(new Transforms.DelegateConstruction.Annotation(false)); - } - case ILCode.Ldvirtftn: { - Cecil.MethodReference cecilMethod = ((MethodReference)operand); - var expr = new Ast.IdentifierExpression(cecilMethod.Name); - expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); - expr.AddAnnotation(cecilMethod); - return new IdentifierExpression("ldvirtftn").Invoke(expr) - .WithAnnotation(new Transforms.DelegateConstruction.Annotation(true)); - } - case ILCode.Calli: return InlineAssembly(byteCode, args); - case ILCode.Ckfinite: return InlineAssembly(byteCode, args); - case ILCode.Constrained: return InlineAssembly(byteCode, args); - case ILCode.Cpblk: return InlineAssembly(byteCode, args); - case ILCode.Cpobj: return InlineAssembly(byteCode, args); - case ILCode.Dup: return arg1; - case ILCode.Endfilter: return InlineAssembly(byteCode, args); - case ILCode.Endfinally: return null; - case ILCode.Initblk: return InlineAssembly(byteCode, args); - case ILCode.Initobj: return InlineAssembly(byteCode, args); - case ILCode.DefaultValue: - return MakeDefaultValue((TypeReference)operand); - case ILCode.Jmp: return InlineAssembly(byteCode, args); - case ILCode.Ldc_I4: - return AstBuilder.MakePrimitive((int)operand, byteCode.InferredType); - case ILCode.Ldc_I8: - return AstBuilder.MakePrimitive((long)operand, byteCode.InferredType); - case ILCode.Ldc_R4: - case ILCode.Ldc_R8: - case ILCode.Ldc_Decimal: - return new Ast.PrimitiveExpression(operand); - case ILCode.Ldfld: - if (arg1 is DirectionExpression) - arg1 = ((DirectionExpression)arg1).Expression.Detach(); - return arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand); - case ILCode.Ldsfld: - return AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) - .Member(((FieldReference)operand).Name).WithAnnotation(operand); - case ILCode.Stfld: - if (arg1 is DirectionExpression) - arg1 = ((DirectionExpression)arg1).Expression.Detach(); - return new AssignmentExpression(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand), arg2); - case ILCode.Stsfld: - return new AssignmentExpression( - AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) - .Member(((FieldReference)operand).Name).WithAnnotation(operand), - arg1); - case ILCode.Ldflda: - if (arg1 is DirectionExpression) - arg1 = ((DirectionExpression)arg1).Expression.Detach(); - return MakeRef(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand)); - case ILCode.Ldsflda: - return MakeRef( - AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) - .Member(((FieldReference)operand).Name).WithAnnotation(operand)); - case ILCode.Ldloc: { - ILVariable v = (ILVariable)operand; - if (!v.IsParameter) - localVariablesToDefine.Add((ILVariable)operand); - Expression expr; - if (v.IsParameter && v.OriginalParameter.Index < 0) - expr = new ThisReferenceExpression(); - else - expr = new Ast.IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand); - return v.IsParameter && v.Type is ByReferenceType ? MakeRef(expr) : expr; - } - case ILCode.Ldloca: { - ILVariable v = (ILVariable)operand; - if (v.IsParameter && v.OriginalParameter.Index < 0) - return MakeRef(new ThisReferenceExpression()); - if (!v.IsParameter) - localVariablesToDefine.Add((ILVariable)operand); - return MakeRef(new Ast.IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand)); - } - case ILCode.Ldnull: return new Ast.NullReferenceExpression(); - case ILCode.Ldstr: return new Ast.PrimitiveExpression(operand); - case ILCode.Ldtoken: - if (operand is Cecil.TypeReference) { - return AstBuilder.CreateTypeOfExpression((TypeReference)operand).Member("TypeHandle"); - } else { - Expression referencedEntity; - string loadName; - string handleName; - if (operand is Cecil.FieldReference) { - loadName = "fieldof"; - handleName = "FieldHandle"; - FieldReference fr = (FieldReference)operand; - referencedEntity = AstBuilder.ConvertType(fr.DeclaringType).Member(fr.Name).WithAnnotation(fr); - } else if (operand is Cecil.MethodReference) { - loadName = "methodof"; - handleName = "MethodHandle"; - MethodReference mr = (MethodReference)operand; - var methodParameters = mr.Parameters.Select(p => new TypeReferenceExpression(AstBuilder.ConvertType(p.ParameterType))); - referencedEntity = AstBuilder.ConvertType(mr.DeclaringType).Invoke(mr.Name, methodParameters).WithAnnotation(mr); - } else { - loadName = "ldtoken"; - handleName = "Handle"; - referencedEntity = new IdentifierExpression(FormatByteCodeOperand(byteCode.Operand)); - } - return new IdentifierExpression(loadName).Invoke(referencedEntity).WithAnnotation(new LdTokenAnnotation()).Member(handleName); - } - case ILCode.Leave: return new GotoStatement() { Label = ((ILLabel)operand).Name }; - case ILCode.Localloc: - { - PointerType ptrType = byteCode.InferredType as PointerType; - TypeReference type; - if (ptrType != null) { - type = ptrType.ElementType; - } else { - type = typeSystem.Byte; - } - return new StackAllocExpression { - Type = AstBuilder.ConvertType(type), - CountExpression = arg1 - }; - } - case ILCode.Mkrefany: - { - DirectionExpression dir = arg1 as DirectionExpression; - if (dir != null) { - return new UndocumentedExpression { - UndocumentedExpressionType = UndocumentedExpressionType.MakeRef, - Arguments = { dir.Expression.Detach() } - }; - } else { - return InlineAssembly(byteCode, args); - } - } - case ILCode.Refanytype: - return new UndocumentedExpression { - UndocumentedExpressionType = UndocumentedExpressionType.RefType, - Arguments = { arg1 } - }.Member("TypeHandle"); - case ILCode.Refanyval: - return MakeRef( - new UndocumentedExpression { - UndocumentedExpressionType = UndocumentedExpressionType.RefValue, - Arguments = { arg1, new TypeReferenceExpression(operandAsTypeRef) } - }); - case ILCode.Newobj: { - Cecil.TypeReference declaringType = ((MethodReference)operand).DeclaringType; - if (declaringType is ArrayType) { - ComposedType ct = AstBuilder.ConvertType((ArrayType)declaringType) as ComposedType; - if (ct != null && ct.ArraySpecifiers.Count >= 1) { - var ace = new Ast.ArrayCreateExpression(); - ct.ArraySpecifiers.First().Remove(); - ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); - ace.Type = ct; - ace.Arguments.AddRange(args); - return ace; - } - } - if (declaringType.IsAnonymousType()) { - MethodDefinition ctor = ((MethodReference)operand).Resolve(); - if (methodDef != null) { - AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression(); - if (CanInferAnonymousTypePropertyNamesFromArguments(args, ctor.Parameters)) { - atce.Initializers.AddRange(args); - } else { - for (int i = 0; i < args.Count; i++) { - atce.Initializers.Add( - new NamedExpression { - Name = ctor.Parameters[i].Name, - Expression = args[i] - }); - } - } - return atce; - } - } - var oce = new Ast.ObjectCreateExpression(); - oce.Type = AstBuilder.ConvertType(declaringType); - oce.Arguments.AddRange(args); - return oce.WithAnnotation(operand); - } - case ILCode.No: return InlineAssembly(byteCode, args); - case ILCode.Nop: return null; - case ILCode.Pop: return arg1; - case ILCode.Readonly: return InlineAssembly(byteCode, args); - case ILCode.Ret: - if (methodDef.ReturnType.FullName != "System.Void") { - return new Ast.ReturnStatement { Expression = arg1 }; - } else { - return new Ast.ReturnStatement(); - } - case ILCode.Rethrow: return new Ast.ThrowStatement(); - case ILCode.Sizeof: return new Ast.SizeOfExpression { Type = operandAsTypeRef }; - case ILCode.Stloc: { - ILVariable locVar = (ILVariable)operand; - if (!locVar.IsParameter) - localVariablesToDefine.Add(locVar); - return new Ast.AssignmentExpression(new Ast.IdentifierExpression(locVar.Name).WithAnnotation(locVar), arg1); - } - case ILCode.Switch: return InlineAssembly(byteCode, args); - case ILCode.Tail: return InlineAssembly(byteCode, args); - case ILCode.Throw: return new Ast.ThrowStatement { Expression = arg1 }; - case ILCode.Unaligned: return InlineAssembly(byteCode, args); - case ILCode.Volatile: return InlineAssembly(byteCode, args); - case ILCode.YieldBreak: - return new Ast.YieldBreakStatement(); - case ILCode.YieldReturn: - return new Ast.YieldReturnStatement { Expression = arg1 }; - case ILCode.InitObject: - case ILCode.InitCollection: - { - ArrayInitializerExpression initializer = new ArrayInitializerExpression(); - for (int i = 1; i < args.Count; i++) { - Match m = objectInitializerPattern.Match(args[i]); - if (m.Success) { - MemberReferenceExpression mre = m.Get<MemberReferenceExpression>("left").Single(); - initializer.Elements.Add( - new NamedExpression { - Name = mre.MemberName, - Expression = m.Get<Expression>("right").Single().Detach() - }.CopyAnnotationsFrom(mre)); - } else { - m = collectionInitializerPattern.Match(args[i]); - if (m.Success) { - if (m.Get("arg").Count() == 1) { - initializer.Elements.Add(m.Get<Expression>("arg").Single().Detach()); - } else { - ArrayInitializerExpression argList = new ArrayInitializerExpression(); - foreach (var expr in m.Get<Expression>("arg")) { - argList.Elements.Add(expr.Detach()); - } - initializer.Elements.Add(argList); - } - } else { - initializer.Elements.Add(args[i]); - } - } - } - ObjectCreateExpression oce = arg1 as ObjectCreateExpression; - DefaultValueExpression dve = arg1 as DefaultValueExpression; - if (oce != null) { - oce.Initializer = initializer; - return oce; - } else if (dve != null) { - oce = new ObjectCreateExpression(dve.Type.Detach()); - oce.CopyAnnotationsFrom(dve); - oce.Initializer = initializer; - return oce; - } else { - return new AssignmentExpression(arg1, initializer); - } - } - case ILCode.InitializedObject: - return new InitializedObjectExpression(); - case ILCode.Wrap: - return arg1.WithAnnotation(PushNegation.LiftedOperatorAnnotation); - case ILCode.AddressOf: - return MakeRef(arg1); - case ILCode.ExpressionTreeParameterDeclarations: - args[args.Count - 1].AddAnnotation(new ParameterDeclarationAnnotation(byteCode)); - return args[args.Count - 1]; - case ILCode.Await: - return new UnaryOperatorExpression(UnaryOperatorType.Await, UnpackDirectionExpression(arg1)); - case ILCode.NullableOf: - case ILCode.ValueOf: - return arg1; - default: - throw new Exception("Unknown OpCode: " + byteCode.Code); - } - } - - internal static bool CanInferAnonymousTypePropertyNamesFromArguments(IList<Expression> args, IList<ParameterDefinition> parameters) - { - for (int i = 0; i < args.Count; i++) { - string inferredName; - if (args[i] is IdentifierExpression) - inferredName = ((IdentifierExpression)args[i]).Identifier; - else if (args[i] is MemberReferenceExpression) - inferredName = ((MemberReferenceExpression)args[i]).MemberName; - else - inferredName = null; - - if (inferredName != parameters[i].Name) { - return false; - } - } - return true; - } - - static readonly AstNode objectInitializerPattern = new AssignmentExpression( - new MemberReferenceExpression { - Target = new InitializedObjectExpression(), - MemberName = Pattern.AnyString - }.WithName("left"), - new AnyNode("right") - ); - - static readonly AstNode collectionInitializerPattern = new InvocationExpression { - Target = new MemberReferenceExpression { - Target = new InitializedObjectExpression(), - MemberName = "Add" - }, - Arguments = { new Repeat(new AnyNode("arg")) } - }; - - sealed class InitializedObjectExpression : IdentifierExpression - { - public InitializedObjectExpression() : base("__initialized_object__") {} - - protected override bool DoMatch(AstNode other, Match match) - { - return other is InitializedObjectExpression; - } - } - - Expression MakeDefaultValue(TypeReference type) - { - TypeDefinition typeDef = type.Resolve(); - if (typeDef != null) { - if (TypeAnalysis.IsIntegerOrEnum(typeDef)) - return AstBuilder.MakePrimitive(0, typeDef); - else if (!typeDef.IsValueType) - return new NullReferenceExpression(); - switch (typeDef.FullName) { - case "System.Nullable`1": - return new NullReferenceExpression(); - case "System.Single": - return new PrimitiveExpression(0f); - case "System.Double": - return new PrimitiveExpression(0.0); - case "System.Decimal": - return new PrimitiveExpression(0m); - } - } - return new DefaultValueExpression { Type = AstBuilder.ConvertType(type) }; - } - - AstNode TransformCall(bool isVirtual, ILExpression byteCode, List<Ast.Expression> args) - { - Cecil.MethodReference cecilMethod = (MethodReference)byteCode.Operand; - Cecil.MethodDefinition cecilMethodDef = cecilMethod.Resolve(); - Ast.Expression target; - List<Ast.Expression> methodArgs = new List<Ast.Expression>(args); - if (cecilMethod.HasThis) { - target = methodArgs[0]; - methodArgs.RemoveAt(0); - - // Unpack any DirectionExpression that is used as target for the call - // (calling methods on value types implicitly passes the first argument by reference) - target = UnpackDirectionExpression(target); - - if (cecilMethodDef != null) { - // convert null.ToLower() to ((string)null).ToLower() - if (target is NullReferenceExpression) - target = target.CastTo(AstBuilder.ConvertType(cecilMethod.DeclaringType)); - - if (cecilMethodDef.DeclaringType.IsInterface) { - TypeReference tr = byteCode.Arguments[0].InferredType; - if (tr != null) { - TypeDefinition td = tr.Resolve(); - if (td != null && !td.IsInterface) { - // Calling an interface method on a non-interface object: - // we need to introduce an explicit cast - target = target.CastTo(AstBuilder.ConvertType(cecilMethod.DeclaringType)); - } - } - } - } - } else { - target = new TypeReferenceExpression { Type = AstBuilder.ConvertType(cecilMethod.DeclaringType) }; - } - if (target is ThisReferenceExpression && !isVirtual) { - // a non-virtual call on "this" might be a "base"-call. - if (cecilMethod.DeclaringType.GetElementType() != methodDef.DeclaringType) { - // If we're not calling a method in the current class; we must be calling one in the base class. - target = new BaseReferenceExpression(); - } - } - - if (cecilMethod.Name == ".ctor" && cecilMethod.DeclaringType.IsValueType) { - // On value types, the constructor can be called. - // This is equivalent to 'target = new ValueType(args);'. - ObjectCreateExpression oce = new ObjectCreateExpression(); - oce.Type = AstBuilder.ConvertType(cecilMethod.DeclaringType); - oce.AddAnnotation(cecilMethod); - AdjustArgumentsForMethodCall(cecilMethod, methodArgs); - oce.Arguments.AddRange(methodArgs); - return new AssignmentExpression(target, oce); - } - - if (cecilMethod.Name == "Get" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) { - return target.Indexer(methodArgs); - } else if (cecilMethod.Name == "Set" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 2) { - return new AssignmentExpression(target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)), methodArgs.Last()); - } - - // Test whether the method is an accessor: - if (cecilMethodDef != null) { - if (cecilMethodDef.IsGetter && methodArgs.Count == 0) { - foreach (var prop in cecilMethodDef.DeclaringType.Properties) { - if (prop.GetMethod == cecilMethodDef) - return target.Member(prop.Name).WithAnnotation(prop).WithAnnotation(cecilMethod); - } - } else if (cecilMethodDef.IsGetter) { // with parameters - PropertyDefinition indexer = GetIndexer(cecilMethodDef); - if (indexer != null) - return target.Indexer(methodArgs).WithAnnotation(indexer).WithAnnotation(cecilMethod); - } else if (cecilMethodDef.IsSetter && methodArgs.Count == 1) { - foreach (var prop in cecilMethodDef.DeclaringType.Properties) { - if (prop.SetMethod == cecilMethodDef) - return new Ast.AssignmentExpression(target.Member(prop.Name).WithAnnotation(prop).WithAnnotation(cecilMethod), methodArgs[0]); - } - } else if (cecilMethodDef.IsSetter && methodArgs.Count > 1) { - PropertyDefinition indexer = GetIndexer(cecilMethodDef); - if (indexer != null) - return new AssignmentExpression( - target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)).WithAnnotation(indexer).WithAnnotation(cecilMethod), - methodArgs[methodArgs.Count - 1] - ); - } else if (cecilMethodDef.IsAddOn && methodArgs.Count == 1) { - foreach (var ev in cecilMethodDef.DeclaringType.Events) { - if (ev.AddMethod == cecilMethodDef) { - return new Ast.AssignmentExpression { - Left = target.Member(ev.Name).WithAnnotation(ev).WithAnnotation(cecilMethod), - Operator = AssignmentOperatorType.Add, - Right = methodArgs[0] - }; - } - } - } else if (cecilMethodDef.IsRemoveOn && methodArgs.Count == 1) { - foreach (var ev in cecilMethodDef.DeclaringType.Events) { - if (ev.RemoveMethod == cecilMethodDef) { - return new Ast.AssignmentExpression { - Left = target.Member(ev.Name).WithAnnotation(ev).WithAnnotation(cecilMethod), - Operator = AssignmentOperatorType.Subtract, - Right = methodArgs[0] - }; - } - } - } else if (cecilMethodDef.Name == "Invoke" && cecilMethodDef.DeclaringType.BaseType != null && cecilMethodDef.DeclaringType.BaseType.FullName == "System.MulticastDelegate") { - AdjustArgumentsForMethodCall(cecilMethod, methodArgs); - return target.Invoke(methodArgs).WithAnnotation(cecilMethod); - } - } - // Default invocation - AdjustArgumentsForMethodCall(cecilMethodDef ?? cecilMethod, methodArgs); - return target.Invoke(cecilMethod.Name, ConvertTypeArguments(cecilMethod), methodArgs).WithAnnotation(cecilMethod); - } - - static Expression UnpackDirectionExpression(Expression target) - { - if (target is DirectionExpression) { - return ((DirectionExpression)target).Expression.Detach(); - } else { - return target; - } - } - - static void AdjustArgumentsForMethodCall(MethodReference cecilMethod, List<Expression> methodArgs) - { - // Convert 'ref' into 'out' where necessary - for (int i = 0; i < methodArgs.Count && i < cecilMethod.Parameters.Count; i++) { - DirectionExpression dir = methodArgs[i] as DirectionExpression; - ParameterDefinition p = cecilMethod.Parameters[i]; - if (dir != null && p.IsOut && !p.IsIn) - dir.FieldDirection = FieldDirection.Out; - } - } - - internal static PropertyDefinition GetIndexer(MethodDefinition cecilMethodDef) - { - TypeDefinition typeDef = cecilMethodDef.DeclaringType; - string indexerName = null; - foreach (CustomAttribute ca in typeDef.CustomAttributes) { - if (ca.Constructor.FullName == "System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)") { - indexerName = ca.ConstructorArguments.Single().Value as string; - break; - } - } - if (indexerName == null) - return null; - foreach (PropertyDefinition prop in typeDef.Properties) { - if (prop.Name == indexerName) { - if (prop.GetMethod == cecilMethodDef || prop.SetMethod == cecilMethodDef) - return prop; - } - } - return null; - } - - #if DEBUG - static readonly ConcurrentDictionary<ILCode, int> unhandledOpcodes = new ConcurrentDictionary<ILCode, int>(); - #endif - - [Conditional("DEBUG")] - public static void ClearUnhandledOpcodes() - { - #if DEBUG - unhandledOpcodes.Clear(); - #endif - } - - [Conditional("DEBUG")] - public static void PrintNumberOfUnhandledOpcodes() - { - #if DEBUG - foreach (var pair in unhandledOpcodes) { - Debug.WriteLine("AddMethodBodyBuilder unhandled opcode: {1}x {0}", pair.Key, pair.Value); - } - #endif - } - - static Expression InlineAssembly(ILExpression byteCode, List<Ast.Expression> args) - { - #if DEBUG - unhandledOpcodes.AddOrUpdate(byteCode.Code, c => 1, (c, n) => n+1); - #endif - // Output the operand of the unknown IL code as well - if (byteCode.Operand != null) { - args.Insert(0, new IdentifierExpression(FormatByteCodeOperand(byteCode.Operand))); - } - return new IdentifierExpression(byteCode.Code.GetName()).Invoke(args); - } - - static string FormatByteCodeOperand(object operand) - { - if (operand == null) { - return string.Empty; - //} else if (operand is ILExpression) { - // return string.Format("IL_{0:X2}", ((ILExpression)operand).Offset); - } else if (operand is MethodReference) { - return ((MethodReference)operand).Name + "()"; - } else if (operand is Cecil.TypeReference) { - return ((Cecil.TypeReference)operand).FullName; - } else if (operand is VariableDefinition) { - return "V_" + ((VariableDefinition)operand).Index; - } else if (operand is ParameterDefinition) { - return ((ParameterDefinition)operand).Name; - } else if (operand is FieldReference) { - return ((FieldReference)operand).Name; - } else if (operand is string) { - return "\"" + operand + "\""; - } else if (operand is int) { - return operand.ToString(); - } else { - return operand.ToString(); - } - } - - static IEnumerable<AstType> ConvertTypeArguments(MethodReference cecilMethod) - { - GenericInstanceMethod g = cecilMethod as GenericInstanceMethod; - if (g == null) - return null; - if (g.GenericArguments.Any(ta => ta.ContainsAnonymousType())) - return null; - return g.GenericArguments.Select(t => AstBuilder.ConvertType(t)); - } - - static Ast.DirectionExpression MakeRef(Ast.Expression expr) - { - return new DirectionExpression { Expression = expr, FieldDirection = FieldDirection.Ref }; - } - - Ast.Expression Convert(Ast.Expression expr, Cecil.TypeReference actualType, Cecil.TypeReference reqType) - { - if (actualType == null || reqType == null || TypeAnalysis.IsSameType(actualType, reqType)) { - return expr; - } else if (actualType is ByReferenceType && reqType is PointerType && expr is DirectionExpression) { - return Convert( - new UnaryOperatorExpression(UnaryOperatorType.AddressOf, ((DirectionExpression)expr).Expression.Detach()), - new PointerType(((ByReferenceType)actualType).ElementType), - reqType); - } else if (actualType is PointerType && reqType is ByReferenceType) { - expr = Convert(expr, actualType, new PointerType(((ByReferenceType)reqType).ElementType)); - return new DirectionExpression { - FieldDirection = FieldDirection.Ref, - Expression = new UnaryOperatorExpression(UnaryOperatorType.Dereference, expr) - }; - } else if (actualType is PointerType && reqType is PointerType) { - if (actualType.FullName != reqType.FullName) - return expr.CastTo(AstBuilder.ConvertType(reqType)); - else - return expr; - } else { - bool actualIsIntegerOrEnum = TypeAnalysis.IsIntegerOrEnum(actualType); - bool requiredIsIntegerOrEnum = TypeAnalysis.IsIntegerOrEnum(reqType); - - if (TypeAnalysis.IsBoolean(reqType)) { - if (TypeAnalysis.IsBoolean(actualType)) - return expr; - if (actualIsIntegerOrEnum) { - return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, AstBuilder.MakePrimitive(0, actualType)); - } else { - return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, new NullReferenceExpression()); - } - } - if (TypeAnalysis.IsBoolean(actualType) && requiredIsIntegerOrEnum) { - return new ConditionalExpression { - Condition = expr, - TrueExpression = AstBuilder.MakePrimitive(1, reqType), - FalseExpression = AstBuilder.MakePrimitive(0, reqType) - }; - } - - if (expr is PrimitiveExpression && !requiredIsIntegerOrEnum && TypeAnalysis.IsEnum(actualType)) - { - return expr.CastTo(AstBuilder.ConvertType(actualType)); - } - - bool actualIsPrimitiveType = actualIsIntegerOrEnum - || actualType.MetadataType == MetadataType.Single || actualType.MetadataType == MetadataType.Double; - bool requiredIsPrimitiveType = requiredIsIntegerOrEnum - || reqType.MetadataType == MetadataType.Single || reqType.MetadataType == MetadataType.Double; - if (actualIsPrimitiveType && requiredIsPrimitiveType) { - return expr.CastTo(AstBuilder.ConvertType(reqType)); - } - return expr; - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/CommentStatement.cs b/main/contrib/ICSharpCode.Decompiler/Ast/CommentStatement.cs deleted file mode 100644 index 6837b2418f..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/CommentStatement.cs +++ /dev/null @@ -1,69 +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.Linq; -using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.PatternMatching; - -namespace ICSharpCode.Decompiler.Ast -{ - /// <summary> - /// Allows storing comments inside IEnumerable{Statement}. Used in the AstMethodBuilder. - /// CommentStatement nodes are replaced with regular comments later on. - /// </summary> - class CommentStatement : Statement - { - string comment; - - public CommentStatement(string comment) - { - if (comment == null) - throw new ArgumentNullException("comment"); - this.comment = comment; - } - - public override void AcceptVisitor(IAstVisitor visitor) - { - } - - public override T AcceptVisitor<T>(IAstVisitor<T> visitor) - { - return default(T); - } - - public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data) - { - return default(S); - } - - public static void ReplaceAll(AstNode tree) - { - foreach (var cs in tree.Descendants.OfType<CommentStatement>()) { - cs.Parent.InsertChildBefore(cs, new Comment(cs.comment), Roles.Comment); - cs.Remove(); - } - } - - protected override bool DoMatch(AstNode other, Match match) - { - CommentStatement o = other as CommentStatement; - return o != null && MatchString(comment, o.comment); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/DecompilerContext.cs b/main/contrib/ICSharpCode.Decompiler/Ast/DecompilerContext.cs deleted file mode 100644 index 624b1b33fd..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/DecompilerContext.cs +++ /dev/null @@ -1,71 +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.Collections.Generic; -using System.Threading; -using ICSharpCode.Decompiler.Ast; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.TypeSystem.Implementation; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler -{ - public class DecompilerContext - { - public ModuleDefinition CurrentModule; - public CancellationToken CancellationToken; - public TypeDefinition CurrentType; - public MethodDefinition CurrentMethod; - public DecompilerSettings Settings = new DecompilerSettings(); - public bool CurrentMethodIsAsync; - -// public ITypeResolveContext TypeResolveContext; -// public IProjectContent ProjectContent; - - public DecompilerContext(ModuleDefinition currentModule) - { - if (currentModule == null) - throw new ArgumentNullException("currentModule"); - this.CurrentModule = currentModule; - -// this.ProjectContent = new CecilTypeResolveContext(currentModule); -// List<ITypeResolveContext> resolveContexts = new List<ITypeResolveContext>(); -// resolveContexts.Add(this.ProjectContent); -// foreach (AssemblyNameReference r in currentModule.AssemblyReferences) { -// AssemblyDefinition d = currentModule.AssemblyResolver.Resolve(r); -// if (d != null) { -// resolveContexts.Add(new CecilTypeResolveContext(d.MainModule)); -// } -// } -// this.TypeResolveContext = new CompositeTypeResolveContext(resolveContexts); - } - - /// <summary> - /// Used to pass variable names from a method to its anonymous methods. - /// </summary> - internal List<string> ReservedVariableNames = new List<string>(); - - public DecompilerContext Clone() - { - DecompilerContext ctx = (DecompilerContext)MemberwiseClone(); - ctx.ReservedVariableNames = new List<string>(ctx.ReservedVariableNames); - return ctx; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs b/main/contrib/ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs deleted file mode 100644 index 2edd30b591..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs +++ /dev/null @@ -1,86 +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 ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.PatternMatching; - -namespace ICSharpCode.Decompiler.Ast -{ - static class NRefactoryExtensions - { - public static T WithAnnotation<T>(this T node, object annotation) where T : AstNode - { - if (annotation != null) - node.AddAnnotation(annotation); - return node; - } - - public static T CopyAnnotationsFrom<T>(this T node, AstNode other) where T : AstNode - { - foreach (object annotation in other.Annotations) { - node.AddAnnotation(annotation); - } - return node; - } - - public static T Detach<T>(this T node) where T : AstNode - { - node.Remove(); - return node; - } - - public static Expression WithName(this Expression node, string patternGroupName) - { - return new NamedNode(patternGroupName, node); - } - - public static Statement WithName(this Statement node, string patternGroupName) - { - return new NamedNode(patternGroupName, node); - } - - public static void AddNamedArgument(this NRefactory.CSharp.Attribute attribute, string name, Expression argument) - { - attribute.Arguments.Add(new AssignmentExpression(new IdentifierExpression(name), argument)); - } - - public static AstType ToType(this Pattern pattern) - { - return pattern; - } - - public static Expression ToExpression(this Pattern pattern) - { - return pattern; - } - - public static Statement ToStatement(this Pattern pattern) - { - return pattern; - } - - public static Statement GetNextStatement(this Statement statement) - { - AstNode next = statement.NextSibling; - while (next != null && !(next is Statement)) - next = next.NextSibling; - return (Statement)next; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/NameVariables.cs b/main/contrib/ICSharpCode.Decompiler/Ast/NameVariables.cs deleted file mode 100644 index c62b58d987..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/NameVariables.cs +++ /dev/null @@ -1,347 +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.Collections.Generic; -using System.Linq; - -using ICSharpCode.Decompiler.ILAst; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.Ast -{ - public class NameVariables - { - static readonly Dictionary<string, string> typeNameToVariableNameDict = new Dictionary<string, string> { - { "System.Boolean", "flag" }, - { "System.Byte", "b" }, - { "System.SByte", "b" }, - { "System.Int16", "num" }, - { "System.Int32", "num" }, - { "System.Int64", "num" }, - { "System.UInt16", "num" }, - { "System.UInt32", "num" }, - { "System.UInt64", "num" }, - { "System.Single", "num" }, - { "System.Double", "num" }, - { "System.Decimal", "num" }, - { "System.String", "text" }, - { "System.Object", "obj" }, - { "System.Char", "c" } - }; - - - public static void AssignNamesToVariables(DecompilerContext context, IEnumerable<ILVariable> parameters, IEnumerable<ILVariable> variables, ILBlock methodBody, MethodDefinition mdef) - { - NameVariables nv = new NameVariables(); - nv.context = context; - nv.fieldNamesInCurrentType = context.CurrentType.Fields.Select(f => f.Name).ToList(); - // First mark existing variable names as reserved. - foreach (string name in context.ReservedVariableNames) - nv.AddExistingName(name); - foreach (var p in parameters) - nv.AddExistingName(p.Name); - foreach (var v in variables) { - if (v.IsGenerated) { - // don't introduce names for variables generated by ILSpy - keep "expr"/"arg" - nv.AddExistingName(v.Name); - } else if (v.OriginalVariable != null && context.Settings.UseDebugSymbols) { - string varName; - if (!mdef.DebugInformation.TryGetName (v.OriginalVariable, out varName) || varName.StartsWith ("V_", StringComparison.Ordinal) || !IsValidName (varName)) - { - // don't use the name from the debug symbols if it looks like a generated name - v.Name = null; - } else { - // use the name from the debug symbols - // (but ensure we don't use the same name for two variables) - v.Name = nv.GetAlternativeName(varName); - } - } else { - v.Name = null; - } - } - // Now generate names: - foreach (ILVariable p in parameters) { - if (string.IsNullOrEmpty(p.Name)) - p.Name = nv.GenerateNameForVariable(p, methodBody); - } - foreach (ILVariable varDef in variables) { - if (string.IsNullOrEmpty(varDef.Name)) - varDef.Name = nv.GenerateNameForVariable(varDef, methodBody); - } - } - - static bool IsValidName(string varName) - { - if (string.IsNullOrEmpty(varName)) - return false; - if (!(char.IsLetter(varName[0]) || varName[0] == '_')) - return false; - for (int i = 1; i < varName.Length; i++) { - if (!(char.IsLetterOrDigit(varName[i]) || varName[i] == '_')) - return false; - } - return true; - } - - DecompilerContext context; - List<string> fieldNamesInCurrentType; - Dictionary<string, int> typeNames = new Dictionary<string, int>(); - - public void AddExistingName(string name) - { - if (string.IsNullOrEmpty(name)) - return; - int number; - string nameWithoutDigits = SplitName(name, out number); - int existingNumber; - if (typeNames.TryGetValue(nameWithoutDigits, out existingNumber)) { - typeNames[nameWithoutDigits] = Math.Max(number, existingNumber); - } else { - typeNames.Add(nameWithoutDigits, number); - } - } - - string SplitName(string name, out int number) - { - // First, identify whether the name already ends with a number: - int pos = name.Length; - while (pos > 0 && name[pos-1] >= '0' && name[pos-1] <= '9') - pos--; - if (pos < name.Length) { - if (int.TryParse(name.Substring(pos), out number)) { - return name.Substring(0, pos); - } - } - number = 1; - return name; - } - - const char maxLoopVariableName = 'n'; - - public string GetAlternativeName(string oldVariableName) - { - if (oldVariableName.Length == 1 && oldVariableName[0] >= 'i' && oldVariableName[0] <= maxLoopVariableName) { - for (char c = 'i'; c <= maxLoopVariableName; c++) { - if (!typeNames.ContainsKey(c.ToString())) { - typeNames.Add(c.ToString(), 1); - return c.ToString(); - } - } - } - - int number; - string nameWithoutDigits = SplitName(oldVariableName, out number); - - if (!typeNames.ContainsKey(nameWithoutDigits)) { - typeNames.Add(nameWithoutDigits, number - 1); - } - int count = ++typeNames[nameWithoutDigits]; - if (count != 1) { - return nameWithoutDigits + count.ToString(); - } else { - return nameWithoutDigits; - } - } - - string GenerateNameForVariable(ILVariable variable, ILBlock methodBody) - { - string proposedName = null; - if (variable.Type == context.CurrentType.Module.TypeSystem.Int32) { - // test whether the variable might be a loop counter - bool isLoopCounter = false; - foreach (ILWhileLoop loop in methodBody.GetSelfAndChildrenRecursive<ILWhileLoop>()) { - ILExpression expr = loop.Condition; - while (expr != null && expr.Code == ILCode.LogicNot) - expr = expr.Arguments[0]; - if (expr != null) { - switch (expr.Code) { - case ILCode.Clt: - case ILCode.Clt_Un: - case ILCode.Cgt: - case ILCode.Cgt_Un: - case ILCode.Cle: - case ILCode.Cle_Un: - case ILCode.Cge: - case ILCode.Cge_Un: - ILVariable loadVar; - if (expr.Arguments[0].Match(ILCode.Ldloc, out loadVar) && loadVar == variable) { - isLoopCounter = true; - } - break; - } - } - } - if (isLoopCounter) { - // For loop variables, use i,j,k,l,m,n - for (char c = 'i'; c <= maxLoopVariableName; c++) { - if (!typeNames.ContainsKey(c.ToString())) { - proposedName = c.ToString(); - break; - } - } - } - } - if (string.IsNullOrEmpty(proposedName)) { - var proposedNameForStores = - (from expr in methodBody.GetSelfAndChildrenRecursive<ILExpression>() - where expr.Code == ILCode.Stloc && expr.Operand == variable - select GetNameFromExpression(expr.Arguments.Single()) - ).Except(fieldNamesInCurrentType).ToList(); - if (proposedNameForStores.Count == 1) { - proposedName = proposedNameForStores[0]; - } - } - if (string.IsNullOrEmpty(proposedName)) { - var proposedNameForLoads = - (from expr in methodBody.GetSelfAndChildrenRecursive<ILExpression>() - from i in Enumerable.Range(0, expr.Arguments.Count) - let arg = expr.Arguments[i] - where arg.Code == ILCode.Ldloc && arg.Operand == variable - select GetNameForArgument(expr, i) - ).Except(fieldNamesInCurrentType).ToList(); - if (proposedNameForLoads.Count == 1) { - proposedName = proposedNameForLoads[0]; - } - } - if (string.IsNullOrEmpty(proposedName)) { - proposedName = GetNameByType(variable.Type); - } - - // remove any numbers from the proposed name - int number; - proposedName = SplitName(proposedName, out number); - - if (!typeNames.ContainsKey(proposedName)) { - typeNames.Add(proposedName, 0); - } - int count = ++typeNames[proposedName]; - if (count > 1) { - return proposedName + count.ToString(); - } else { - return proposedName; - } - } - - static string GetNameFromExpression(ILExpression expr) - { - switch (expr.Code) { - case ILCode.Ldfld: - case ILCode.Ldsfld: - return CleanUpVariableName(((FieldReference)expr.Operand).Name); - case ILCode.Call: - case ILCode.Callvirt: - case ILCode.CallGetter: - case ILCode.CallvirtGetter: - MethodReference mr = (MethodReference)expr.Operand; - if (mr.Name.StartsWith("get_", StringComparison.OrdinalIgnoreCase) && mr.Parameters.Count == 0) { - // use name from properties, but not from indexers - return CleanUpVariableName(mr.Name.Substring(4)); - } else if (mr.Name.StartsWith("Get", StringComparison.OrdinalIgnoreCase) && mr.Name.Length >= 4 && char.IsUpper(mr.Name[3])) { - // use name from Get-methods - return CleanUpVariableName(mr.Name.Substring(3)); - } - break; - } - return null; - } - - static string GetNameForArgument(ILExpression parent, int i) - { - switch (parent.Code) { - case ILCode.Stfld: - case ILCode.Stsfld: - if (i == parent.Arguments.Count - 1) // last argument is stored value - return CleanUpVariableName(((FieldReference)parent.Operand).Name); - else - break; - case ILCode.Call: - case ILCode.Callvirt: - case ILCode.Newobj: - case ILCode.CallGetter: - case ILCode.CallvirtGetter: - case ILCode.CallSetter: - case ILCode.CallvirtSetter: - MethodReference methodRef = (MethodReference)parent.Operand; - if (methodRef.Parameters.Count == 1 && i == parent.Arguments.Count - 1) { - // argument might be value of a setter - if (methodRef.Name.StartsWith("set_", StringComparison.OrdinalIgnoreCase)) { - return CleanUpVariableName(methodRef.Name.Substring(4)); - } else if (methodRef.Name.StartsWith("Set", StringComparison.OrdinalIgnoreCase) && methodRef.Name.Length >= 4 && char.IsUpper(methodRef.Name[3])) { - return CleanUpVariableName(methodRef.Name.Substring(3)); - } - } - MethodDefinition methodDef = methodRef.Resolve(); - if (methodDef != null) { - var p = methodDef.Parameters.ElementAtOrDefault((parent.Code != ILCode.Newobj && methodDef.HasThis) ? i - 1 : i); - if (p != null && !string.IsNullOrEmpty(p.Name)) - return CleanUpVariableName(p.Name); - } - break; - case ILCode.Ret: - return "result"; - } - return null; - } - - string GetNameByType(TypeReference type) - { - type = TypeAnalysis.UnpackModifiers(type); - - GenericInstanceType git = type as GenericInstanceType; - if (git != null && git.ElementType.FullName == "System.Nullable`1" && git.GenericArguments.Count == 1) { - type = ((GenericInstanceType)type).GenericArguments[0]; - } - - string name; - if (type.IsArray) { - name = "array"; - } else if (type.IsPointer || type.IsByReference) { - name = "ptr"; - } else if (type.Name.EndsWith("Exception", StringComparison.Ordinal)) { - name = "ex"; - } else if (!typeNameToVariableNameDict.TryGetValue(type.FullName, out name)) { - name = type.Name; - // remove the 'I' for interfaces - if (name.Length >= 3 && name[0] == 'I' && char.IsUpper(name[1]) && char.IsLower(name[2])) - name = name.Substring(1); - name = CleanUpVariableName(name); - } - return name; - } - - static string CleanUpVariableName(string name) - { - // remove the backtick (generics) - int pos = name.IndexOf('`'); - if (pos >= 0) - name = name.Substring(0, pos); - - // remove field prefix: - if (name.Length > 2 && name.StartsWith("m_", StringComparison.Ordinal)) - name = name.Substring(2); - else if (name.Length > 1 && name[0] == '_') - name = name.Substring(1); - - if (name.Length == 0) - return "obj"; - else - return char.ToLower(name[0]) + name.Substring(1); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs b/main/contrib/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs deleted file mode 100644 index 2cfff7d969..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs +++ /dev/null @@ -1,373 +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.Collections.Generic; -using System.Linq; -using ICSharpCode.Decompiler; -using ICSharpCode.Decompiler.ILAst; -using ICSharpCode.NRefactory; -using ICSharpCode.NRefactory.CSharp; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.Ast -{ - public class TextTokenWriter : TokenWriter - { - readonly ITextOutput output; - readonly DecompilerContext context; - readonly Stack<AstNode> nodeStack = new Stack<AstNode>(); - int braceLevelWithinType = -1; - bool inDocumentationComment = false; - bool firstUsingDeclaration; - bool lastUsingDeclaration; - - TextLocation? lastEndOfLine; - - public bool FoldBraces = false; - - public TextTokenWriter(ITextOutput output, DecompilerContext context) - { - if (output == null) - throw new ArgumentNullException("output"); - if (context == null) - throw new ArgumentNullException("context"); - this.output = output; - this.context = context; - } - - public override void WriteIdentifier(Identifier identifier) - { - if (identifier.IsVerbatim || CSharpOutputVisitor.IsKeyword(identifier.Name, identifier)) { - output.Write('@'); - } - - var definition = GetCurrentDefinition(); - if (definition != null) { - output.WriteDefinition(identifier.Name, definition, false); - return; - } - - object memberRef = GetCurrentMemberReference(); - - if (memberRef != null) { - output.WriteReference(identifier.Name, memberRef); - return; - } - - definition = GetCurrentLocalDefinition(); - if (definition != null) { - output.WriteDefinition(identifier.Name, definition); - return; - } - - memberRef = GetCurrentLocalReference(); - if (memberRef != null) { - output.WriteReference(identifier.Name, memberRef, true); - return; - } - - if (firstUsingDeclaration) { - output.MarkFoldStart(defaultCollapsed: true); - firstUsingDeclaration = false; - } - - output.Write(identifier.Name); - } - - MemberReference GetCurrentMemberReference() - { - AstNode node = nodeStack.Peek(); - MemberReference memberRef = node.Annotation<MemberReference>(); - if (memberRef == null && node.Role == Roles.TargetExpression && (node.Parent is InvocationExpression || node.Parent is ObjectCreateExpression)) { - memberRef = node.Parent.Annotation<MemberReference>(); - } - if (node is IdentifierExpression && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression && memberRef != null) { - var declaringType = memberRef.DeclaringType.Resolve(); - if (declaringType != null && declaringType.IsDelegate()) - return null; - } - return FilterMemberReference(memberRef); - } - - MemberReference FilterMemberReference(MemberReference memberRef) - { - if (memberRef == null) - return null; - - if (context.Settings.AutomaticEvents && memberRef is FieldDefinition) { - var field = (FieldDefinition)memberRef; - return field.DeclaringType.Events.FirstOrDefault(ev => ev.Name == field.Name) ?? memberRef; - } - - return memberRef; - } - - object GetCurrentLocalReference() - { - AstNode node = nodeStack.Peek(); - ILVariable variable = node.Annotation<ILVariable>(); - if (variable != null) { - if (variable.OriginalParameter != null) - return variable.OriginalParameter; - //if (variable.OriginalVariable != null) - // return variable.OriginalVariable; - return variable; - } - - var gotoStatement = node as GotoStatement; - if (gotoStatement != null) - { - var method = nodeStack.Select(nd => nd.Annotation<MethodReference>()).FirstOrDefault(mr => mr != null); - if (method != null) - return method.ToString() + gotoStatement.Label; - } - - return null; - } - - object GetCurrentLocalDefinition() - { - AstNode node = nodeStack.Peek(); - if (node is Identifier && node.Parent != null) - node = node.Parent; - - var parameterDef = node.Annotation<ParameterDefinition>(); - if (parameterDef != null) - return parameterDef; - - if (node is VariableInitializer || node is CatchClause || node is ForeachStatement) { - var variable = node.Annotation<ILVariable>(); - if (variable != null) { - if (variable.OriginalParameter != null) - return variable.OriginalParameter; - //if (variable.OriginalVariable != null) - // return variable.OriginalVariable; - return variable; - } - } - - var label = node as LabelStatement; - if (label != null) { - var method = nodeStack.Select(nd => nd.Annotation<MethodReference>()).FirstOrDefault(mr => mr != null); - if (method != null) - return method.ToString() + label.Label; - } - - return null; - } - - object GetCurrentDefinition() - { - if (nodeStack == null || nodeStack.Count == 0) - return null; - - var node = nodeStack.Peek(); - if (node is Identifier) - node = node.Parent; - if (IsDefinition(node)) - return node.Annotation<MemberReference>(); - - return null; - } - - public override void WriteKeyword(Role role, string keyword) - { - output.Write(keyword); - } - - public override void WriteToken(Role role, string token) - { - // Attach member reference to token only if there's no identifier in the current node. - MemberReference memberRef = GetCurrentMemberReference(); - var node = nodeStack.Peek(); - if (memberRef != null && node.GetChildByRole(Roles.Identifier).IsNull) - output.WriteReference(token, memberRef); - else - output.Write(token); - } - - public override void Space() - { - output.Write(' '); - } - - public void OpenBrace(BraceStyle style) - { - if (braceLevelWithinType >= 0 || nodeStack.Peek() is TypeDeclaration) - braceLevelWithinType++; - if (nodeStack.OfType<BlockStatement>().Count() <= 1 || FoldBraces) { - output.MarkFoldStart(defaultCollapsed: braceLevelWithinType == 1); - } - output.WriteLine(); - output.WriteLine("{"); - output.Indent(); - } - - public void CloseBrace(BraceStyle style) - { - output.Unindent(); - output.Write('}'); - if (nodeStack.OfType<BlockStatement>().Count() <= 1 || FoldBraces) - output.MarkFoldEnd(); - if (braceLevelWithinType >= 0) - braceLevelWithinType--; - } - - public override void Indent() - { - output.Indent(); - } - - public override void Unindent() - { - output.Unindent(); - } - - public override void NewLine() - { - if (lastUsingDeclaration) { - output.MarkFoldEnd(); - lastUsingDeclaration = false; - } - lastEndOfLine = output.Location; - output.WriteLine(); - } - - public override void WriteComment(CommentType commentType, string content) - { - switch (commentType) { - case CommentType.SingleLine: - output.Write("//"); - output.WriteLine(content); - break; - case CommentType.MultiLine: - output.Write("/*"); - output.Write(content); - output.Write("*/"); - break; - case CommentType.Documentation: - bool isLastLine = !(nodeStack.Peek().NextSibling is Comment); - if (!inDocumentationComment && !isLastLine) { - inDocumentationComment = true; - output.MarkFoldStart("///" + content, true); - } - output.Write("///"); - output.Write(content); - if (inDocumentationComment && isLastLine) { - inDocumentationComment = false; - output.MarkFoldEnd(); - } - output.WriteLine(); - break; - default: - output.Write(content); - break; - } - } - - public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) - { - // pre-processor directive must start on its own line - output.Write('#'); - output.Write(type.ToString().ToLowerInvariant()); - if (!string.IsNullOrEmpty(argument)) { - output.Write(' '); - output.Write(argument); - } - output.WriteLine(); - } - - public override void WritePrimitiveValue(object value, string literalValue = null) - { - new TextWriterTokenWriter(new TextOutputWriter(output)).WritePrimitiveValue(value, literalValue); - } - - public override void WritePrimitiveType(string type) - { - output.Write(type); - if (type == "new") { - output.Write("()"); - } - } - - Stack<TextLocation> startLocations = new Stack<TextLocation>(); - Stack<MethodDebugSymbols> symbolsStack = new Stack<MethodDebugSymbols>(); - - public override void StartNode(AstNode node) - { - if (nodeStack.Count == 0) { - if (IsUsingDeclaration(node)) { - firstUsingDeclaration = !IsUsingDeclaration(node.PrevSibling); - lastUsingDeclaration = !IsUsingDeclaration(node.NextSibling); - } else { - firstUsingDeclaration = false; - lastUsingDeclaration = false; - } - } - nodeStack.Push(node); - startLocations.Push(output.Location); - - if (node is EntityDeclaration && node.Annotation<MemberReference>() != null && node.GetChildByRole(Roles.Identifier).IsNull) - output.WriteDefinition("", node.Annotation<MemberReference>(), false); - - if (node.Annotation<MethodDebugSymbols>() != null) { - symbolsStack.Push(node.Annotation<MethodDebugSymbols>()); - symbolsStack.Peek().StartLocation = startLocations.Peek(); - } - } - - private bool IsUsingDeclaration(AstNode node) - { - return node is UsingDeclaration || node is UsingAliasDeclaration; - } - - public override void EndNode(AstNode node) - { - if (nodeStack.Pop() != node) - throw new InvalidOperationException(); - - var startLocation = startLocations.Pop(); - - // code mappings - var ranges = node.Annotation<List<ILRange>>(); - if (symbolsStack.Count > 0 && ranges != null && ranges.Count > 0) { - // Ignore the newline which was printed at the end of the statement - TextLocation endLocation = (node is Statement) ? (lastEndOfLine ?? output.Location) : output.Location; - symbolsStack.Peek().SequencePoints.Add( - new SequencePoint() { - ILRanges = ILRange.OrderAndJoin(ranges).ToArray(), - StartLocation = startLocation, - EndLocation = endLocation - }); - } - - if (node.Annotation<MethodDebugSymbols>() != null) { - symbolsStack.Peek().EndLocation = output.Location; - output.AddDebugSymbols(symbolsStack.Pop()); - } - } - - private static bool IsDefinition(AstNode node) - { - return node is EntityDeclaration - || (node is VariableInitializer && node.Parent is FieldDeclaration) - || node is FixedVariableInitializer; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs deleted file mode 100644 index b975d88528..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs +++ /dev/null @@ -1,368 +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.Linq; -using ICSharpCode.Decompiler.ILAst; -using ICSharpCode.NRefactory.CSharp; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - /// <summary> - /// Add checked/unchecked blocks. - /// </summary> - public class AddCheckedBlocks : IAstTransform - { - #region Annotation - sealed class CheckedUncheckedAnnotation { - /// <summary> - /// true=checked, false=unchecked - /// </summary> - public bool IsChecked; - } - - public static readonly object CheckedAnnotation = new CheckedUncheckedAnnotation { IsChecked = true }; - public static readonly object UncheckedAnnotation = new CheckedUncheckedAnnotation { IsChecked = false }; - #endregion - - /* - We treat placing checked/unchecked blocks as an optimization problem, with the following goals: - 1. Use minimum number of checked blocks+expressions - 2. Prefer checked expressions over checked blocks - 3. Make the scope of checked expressions as small as possible - 4. Open checked blocks as late as possible, and close checked blocks as late as possible - (where goal 1 has the highest priority) - - Goal 4a (open checked blocks as late as possible) is necessary so that we don't move variable declarations - into checked blocks, as the variable might still be used after the checked block. - (this could cause DeclareVariables to omit the variable declaration, producing incorrect code) - Goal 4b (close checked blocks as late as possible) makes the code look nicer in this case: - checked { - int c = a + b; - int r = a + c; - return r; - } - If the checked block was closed as early as possible, the variable r would have to be declared outside - (this would work, but look badly) - */ - - #region struct Cost - struct Cost - { - // highest possible cost so that the Blocks+Expressions addition doesn't overflow - public static readonly Cost Infinite = new Cost(0x3fffffff, 0x3fffffff); - - public readonly int Blocks; - public readonly int Expressions; - - public Cost(int blocks, int expressions) - { - this.Blocks = blocks; - this.Expressions = expressions; - } - - public static bool operator <(Cost a, Cost b) - { - return a.Blocks + a.Expressions < b.Blocks + b.Expressions - || a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks < b.Blocks; - } - - public static bool operator >(Cost a, Cost b) - { - return a.Blocks + a.Expressions > b.Blocks + b.Expressions - || a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks > b.Blocks; - } - - public static bool operator <=(Cost a, Cost b) - { - return a.Blocks + a.Expressions < b.Blocks + b.Expressions - || a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks <= b.Blocks; - } - - public static bool operator >=(Cost a, Cost b) - { - return a.Blocks + a.Expressions > b.Blocks + b.Expressions - || a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks >= b.Blocks; - } - - public static Cost operator +(Cost a, Cost b) - { - return new Cost(a.Blocks + b.Blocks, a.Expressions + b.Expressions); - } - - public override string ToString() - { - return string.Format("[{0} + {1}]", Blocks, Expressions); - } - } - #endregion - - #region class InsertedNode - /// <summary> - /// Holds the blocks and expressions that should be inserted - /// </summary> - abstract class InsertedNode - { - public static InsertedNode operator +(InsertedNode a, InsertedNode b) - { - if (a == null) - return b; - if (b == null) - return a; - return new InsertedNodeList(a, b); - } - - public abstract void Insert(); - } - - class InsertedNodeList : InsertedNode - { - readonly InsertedNode child1, child2; - - public InsertedNodeList(AddCheckedBlocks.InsertedNode child1, AddCheckedBlocks.InsertedNode child2) - { - this.child1 = child1; - this.child2 = child2; - } - - public override void Insert() - { - child1.Insert(); - child2.Insert(); - } - } - - class InsertedExpression : InsertedNode - { - readonly Expression expression; - readonly bool isChecked; - - public InsertedExpression(Expression expression, bool isChecked) - { - this.expression = expression; - this.isChecked = isChecked; - } - - public override void Insert() - { - if (isChecked) - expression.ReplaceWith(e => new CheckedExpression { Expression = e }); - else - expression.ReplaceWith(e => new UncheckedExpression { Expression = e }); - } - } - - class ConvertCompoundAssignment : InsertedNode - { - readonly Expression expression; - readonly bool isChecked; - - public ConvertCompoundAssignment(Expression expression, bool isChecked) - { - this.expression = expression; - this.isChecked = isChecked; - } - - public override void Insert() - { - AssignmentExpression assign = expression.Annotation<ReplaceMethodCallsWithOperators.RestoreOriginalAssignOperatorAnnotation>().Restore(expression); - expression.ReplaceWith(assign); - if (isChecked) - assign.Right = new CheckedExpression { Expression = assign.Right.Detach() }; - else - assign.Right = new UncheckedExpression { Expression = assign.Right.Detach() }; - } - } - - class InsertedBlock : InsertedNode - { - readonly Statement firstStatement; // inclusive - readonly Statement lastStatement; // exclusive - readonly bool isChecked; - - public InsertedBlock(Statement firstStatement, Statement lastStatement, bool isChecked) - { - this.firstStatement = firstStatement; - this.lastStatement = lastStatement; - this.isChecked = isChecked; - } - - public override void Insert() - { - BlockStatement newBlock = new BlockStatement(); - // Move all statements except for the first - Statement next; - for (Statement stmt = firstStatement.GetNextStatement(); stmt != lastStatement; stmt = next) { - next = stmt.GetNextStatement(); - newBlock.Add(stmt.Detach()); - } - // Replace the first statement with the new (un)checked block - if (isChecked) - firstStatement.ReplaceWith(new CheckedStatement { Body = newBlock }); - else - firstStatement.ReplaceWith(new UncheckedStatement { Body = newBlock }); - // now also move the first node into the new block - newBlock.Statements.InsertAfter(null, firstStatement); - } - } - #endregion - - #region class Result - /// <summary> - /// Holds the result of an insertion operation. - /// </summary> - class Result - { - public Cost CostInCheckedContext; - public InsertedNode NodesToInsertInCheckedContext; - public Cost CostInUncheckedContext; - public InsertedNode NodesToInsertInUncheckedContext; - } - #endregion - - public void Run(AstNode node) - { - BlockStatement block = node as BlockStatement; - if (block == null) { - for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { - Run(child); - } - } else { - Result r = GetResultFromBlock(block); - if (r.NodesToInsertInUncheckedContext != null) - r.NodesToInsertInUncheckedContext.Insert(); - } - } - - Result GetResultFromBlock(BlockStatement block) - { - // For a block, we are tracking 4 possibilities: - // a) context is checked, no unchecked block open - Cost costCheckedContext = new Cost(0, 0); - InsertedNode nodesCheckedContext = null; - // b) context is checked, an unchecked block is open - Cost costCheckedContextUncheckedBlockOpen = Cost.Infinite; - InsertedNode nodesCheckedContextUncheckedBlockOpen = null; - Statement uncheckedBlockStart = null; - // c) context is unchecked, no checked block open - Cost costUncheckedContext = new Cost(0, 0); - InsertedNode nodesUncheckedContext = null; - // d) context is unchecked, a checked block is open - Cost costUncheckedContextCheckedBlockOpen = Cost.Infinite; - InsertedNode nodesUncheckedContextCheckedBlockOpen = null; - Statement checkedBlockStart = null; - - Statement statement = block.Statements.FirstOrDefault(); - while (true) { - // Blocks can be closed 'for free'. We use '<=' so that blocks are closed as late as possible (goal 4b) - if (costCheckedContextUncheckedBlockOpen <= costCheckedContext) { - costCheckedContext = costCheckedContextUncheckedBlockOpen; - nodesCheckedContext = nodesCheckedContextUncheckedBlockOpen + new InsertedBlock(uncheckedBlockStart, statement, false); - } - if (costUncheckedContextCheckedBlockOpen <= costUncheckedContext) { - costUncheckedContext = costUncheckedContextCheckedBlockOpen; - nodesUncheckedContext = nodesUncheckedContextCheckedBlockOpen + new InsertedBlock(checkedBlockStart, statement, true); - } - if (statement == null) - break; - // Now try opening blocks. We use '<=' so that blocks are opened as late as possible. (goal 4a) - if (costCheckedContext + new Cost(1, 0) <= costCheckedContextUncheckedBlockOpen) { - costCheckedContextUncheckedBlockOpen = costCheckedContext + new Cost(1, 0); - nodesCheckedContextUncheckedBlockOpen = nodesCheckedContext; - uncheckedBlockStart = statement; - } - if (costUncheckedContext + new Cost(1, 0) <= costUncheckedContextCheckedBlockOpen) { - costUncheckedContextCheckedBlockOpen = costUncheckedContext + new Cost(1, 0); - nodesUncheckedContextCheckedBlockOpen = nodesUncheckedContext; - checkedBlockStart = statement; - } - // Now handle the statement - Result stmtResult = GetResult(statement); - - costCheckedContext += stmtResult.CostInCheckedContext; - nodesCheckedContext += stmtResult.NodesToInsertInCheckedContext; - costCheckedContextUncheckedBlockOpen += stmtResult.CostInUncheckedContext; - nodesCheckedContextUncheckedBlockOpen += stmtResult.NodesToInsertInUncheckedContext; - costUncheckedContext += stmtResult.CostInUncheckedContext; - nodesUncheckedContext += stmtResult.NodesToInsertInUncheckedContext; - costUncheckedContextCheckedBlockOpen += stmtResult.CostInCheckedContext; - nodesUncheckedContextCheckedBlockOpen += stmtResult.NodesToInsertInCheckedContext; - - statement = statement.GetNextStatement(); - } - - return new Result { - CostInCheckedContext = costCheckedContext, NodesToInsertInCheckedContext = nodesCheckedContext, - CostInUncheckedContext = costUncheckedContext, NodesToInsertInUncheckedContext = nodesUncheckedContext - }; - } - - Result GetResult(AstNode node) - { - if (node is BlockStatement) - return GetResultFromBlock((BlockStatement)node); - Result result = new Result(); - for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { - Result childResult = GetResult(child); - result.CostInCheckedContext += childResult.CostInCheckedContext; - result.NodesToInsertInCheckedContext += childResult.NodesToInsertInCheckedContext; - result.CostInUncheckedContext += childResult.CostInUncheckedContext; - result.NodesToInsertInUncheckedContext += childResult.NodesToInsertInUncheckedContext; - } - Expression expr = node as Expression; - if (expr != null) { - CheckedUncheckedAnnotation annotation = expr.Annotation<CheckedUncheckedAnnotation>(); - if (annotation != null) { - // If the annotation requires this node to be in a specific context, add a huge cost to the other context - // That huge cost gives us the option to ignore a required checked/unchecked expression when there wouldn't be any - // solution otherwise. (e.g. "for (checked(M().x += 1); true; unchecked(M().x += 2)) {}") - if (annotation.IsChecked) - result.CostInUncheckedContext += new Cost(10000, 0); - else - result.CostInCheckedContext += new Cost(10000, 0); - } - // Embed this node in an checked/unchecked expression: - if (expr.Parent is ExpressionStatement) { - // We cannot use checked/unchecked for top-level-expressions. - // However, we could try converting a compound assignment (checked(a+=b);) or unary operator (checked(a++);) - // back to its old form. - if (expr.Annotation<ReplaceMethodCallsWithOperators.RestoreOriginalAssignOperatorAnnotation>() != null) { - // We use '<' so that expressions are introduced on the deepest level possible (goal 3) - if (result.CostInCheckedContext + new Cost(1, 1) < result.CostInUncheckedContext) { - result.CostInUncheckedContext = result.CostInCheckedContext + new Cost(1, 1); - result.NodesToInsertInUncheckedContext = result.NodesToInsertInCheckedContext + new ConvertCompoundAssignment(expr, true); - } else if (result.CostInUncheckedContext + new Cost(1, 1) < result.CostInCheckedContext) { - result.CostInCheckedContext = result.CostInUncheckedContext + new Cost(1, 1); - result.NodesToInsertInCheckedContext = result.NodesToInsertInUncheckedContext + new ConvertCompoundAssignment(expr, false); - } - } - } else if (expr.Role.IsValid(Expression.Null)) { - // We use '<' so that expressions are introduced on the deepest level possible (goal 3) - if (result.CostInCheckedContext + new Cost(0, 1) < result.CostInUncheckedContext) { - result.CostInUncheckedContext = result.CostInCheckedContext + new Cost(0, 1); - result.NodesToInsertInUncheckedContext = result.NodesToInsertInCheckedContext + new InsertedExpression(expr, true); - } else if (result.CostInUncheckedContext + new Cost(0, 1) < result.CostInCheckedContext) { - result.CostInCheckedContext = result.CostInUncheckedContext + new Cost(0, 1); - result.NodesToInsertInCheckedContext = result.NodesToInsertInUncheckedContext + new InsertedExpression(expr, false); - } - } - } - return result; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs deleted file mode 100644 index a0d1ca8c1e..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs +++ /dev/null @@ -1,179 +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.Linq; -using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.PatternMatching; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - /// <summary> - /// Combines query expressions and removes transparent identifiers. - /// </summary> - public class CombineQueryExpressions : IAstTransform - { - readonly DecompilerContext context; - - public CombineQueryExpressions(DecompilerContext context) - { - this.context = context; - } - - public void Run(AstNode compilationUnit) - { - if (!context.Settings.QueryExpressions) - return; - CombineQueries(compilationUnit); - } - - static readonly InvocationExpression castPattern = new InvocationExpression { - Target = new MemberReferenceExpression { - Target = new AnyNode("inExpr"), - MemberName = "Cast", - TypeArguments = { new AnyNode("targetType") } - }}; - - void CombineQueries(AstNode node) - { - for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { - CombineQueries(child); - } - QueryExpression query = node as QueryExpression; - if (query != null) { - QueryFromClause fromClause = (QueryFromClause)query.Clauses.First(); - QueryExpression innerQuery = fromClause.Expression as QueryExpression; - if (innerQuery != null) { - if (TryRemoveTransparentIdentifier(query, fromClause, innerQuery)) { - RemoveTransparentIdentifierReferences(query); - } else { - QueryContinuationClause continuation = new QueryContinuationClause(); - continuation.PrecedingQuery = innerQuery.Detach(); - continuation.Identifier = fromClause.Identifier; - fromClause.ReplaceWith(continuation); - } - } else { - Match m = castPattern.Match(fromClause.Expression); - if (m.Success) { - fromClause.Type = m.Get<AstType>("targetType").Single().Detach(); - fromClause.Expression = m.Get<Expression>("inExpr").Single().Detach(); - } - } - } - } - - static readonly QuerySelectClause selectTransparentIdentifierPattern = new QuerySelectClause { - Expression = new Choice { - new AnonymousTypeCreateExpression { - Initializers = { - new NamedExpression { - Name = Pattern.AnyString, - Expression = new IdentifierExpression(Pattern.AnyString) - }.WithName("nae1"), - new NamedExpression { - Name = Pattern.AnyString, - Expression = new AnyNode("nae2Expr") - }.WithName("nae2") - } - }, - new AnonymousTypeCreateExpression { - Initializers = { - new NamedNode("identifier", new IdentifierExpression(Pattern.AnyString)), - new AnyNode("nae2Expr") - } - } - }}; - - bool IsTransparentIdentifier(string identifier) - { - return identifier.StartsWith("<>", StringComparison.Ordinal) && identifier.Contains("TransparentIdentifier"); - } - - bool TryRemoveTransparentIdentifier(QueryExpression query, QueryFromClause fromClause, QueryExpression innerQuery) - { - if (!IsTransparentIdentifier(fromClause.Identifier)) - return false; - Match match = selectTransparentIdentifierPattern.Match(innerQuery.Clauses.Last()); - if (!match.Success) - return false; - QuerySelectClause selectClause = (QuerySelectClause)innerQuery.Clauses.Last(); - NamedExpression nae1 = match.Get<NamedExpression>("nae1").SingleOrDefault(); - NamedExpression nae2 = match.Get<NamedExpression>("nae2").SingleOrDefault(); - if (nae1 != null && nae1.Name != ((IdentifierExpression)nae1.Expression).Identifier) - return false; - Expression nae2Expr = match.Get<Expression>("nae2Expr").Single(); - IdentifierExpression nae2IdentExpr = nae2Expr as IdentifierExpression; - if (nae2IdentExpr != null && (nae2 == null || nae2.Name == nae2IdentExpr.Identifier)) { - // from * in (from x in ... select new { x = x, y = y }) ... - // => - // from x in ... ... - fromClause.Remove(); - selectClause.Remove(); - // Move clauses from innerQuery to query - QueryClause insertionPos = null; - foreach (var clause in innerQuery.Clauses) { - query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach()); - } - } else { - // from * in (from x in ... select new { x = x, y = expr }) ... - // => - // from x in ... let y = expr ... - fromClause.Remove(); - selectClause.Remove(); - // Move clauses from innerQuery to query - QueryClause insertionPos = null; - foreach (var clause in innerQuery.Clauses) { - query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach()); - } - string ident; - if (nae2 != null) - ident = nae2.Name; - else if (nae2Expr is IdentifierExpression) - ident = ((IdentifierExpression)nae2Expr).Identifier; - else if (nae2Expr is MemberReferenceExpression) - ident = ((MemberReferenceExpression)nae2Expr).MemberName; - else - throw new InvalidOperationException("Could not infer name from initializer in AnonymousTypeCreateExpression"); - query.Clauses.InsertAfter(insertionPos, new QueryLetClause { Identifier = ident, Expression = nae2Expr.Detach() }); - } - return true; - } - - /// <summary> - /// Removes all occurrences of transparent identifiers - /// </summary> - void RemoveTransparentIdentifierReferences(AstNode node) - { - foreach (AstNode child in node.Children) { - RemoveTransparentIdentifierReferences(child); - } - MemberReferenceExpression mre = node as MemberReferenceExpression; - if (mre != null) { - IdentifierExpression ident = mre.Target as IdentifierExpression; - if (ident != null && IsTransparentIdentifier(ident.Identifier)) { - IdentifierExpression newIdent = new IdentifierExpression(mre.MemberName); - mre.TypeArguments.MoveTo(newIdent.TypeArguments); - newIdent.CopyAnnotationsFrom(mre); - newIdent.RemoveAnnotations<PropertyDeclaration>(); // remove the reference to the property of the anonymous type - mre.ReplaceWith(newIdent); - return; - } - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs deleted file mode 100644 index 1d1f3d0817..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs +++ /dev/null @@ -1,111 +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.Diagnostics; -using ICSharpCode.NRefactory.CSharp; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - /// <summary> - /// Base class for AST visitors that need the current type/method context info. - /// </summary> - public abstract class ContextTrackingVisitor<TResult> : DepthFirstAstVisitor<object, TResult>, IAstTransform - { - protected readonly DecompilerContext context; - - protected ContextTrackingVisitor(DecompilerContext context) - { - if (context == null) - throw new ArgumentNullException("context"); - this.context = context; - } - - public override TResult VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) - { - TypeDefinition oldType = context.CurrentType; - try { - context.CurrentType = typeDeclaration.Annotation<TypeDefinition>(); - return base.VisitTypeDeclaration(typeDeclaration, data); - } finally { - context.CurrentType = oldType; - } - } - - public override TResult VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) - { - Debug.Assert(context.CurrentMethod == null); - try { - context.CurrentMethod = methodDeclaration.Annotation<MethodDefinition>(); - return base.VisitMethodDeclaration(methodDeclaration, data); - } finally { - context.CurrentMethod = null; - } - } - - public override TResult VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) - { - Debug.Assert(context.CurrentMethod == null); - try { - context.CurrentMethod = constructorDeclaration.Annotation<MethodDefinition>(); - return base.VisitConstructorDeclaration(constructorDeclaration, data); - } finally { - context.CurrentMethod = null; - } - } - - public override TResult VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration, object data) - { - Debug.Assert(context.CurrentMethod == null); - try { - context.CurrentMethod = destructorDeclaration.Annotation<MethodDefinition>(); - return base.VisitDestructorDeclaration(destructorDeclaration, data); - } finally { - context.CurrentMethod = null; - } - } - - public override TResult VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, object data) - { - Debug.Assert(context.CurrentMethod == null); - try { - context.CurrentMethod = operatorDeclaration.Annotation<MethodDefinition>(); - return base.VisitOperatorDeclaration(operatorDeclaration, data); - } finally { - context.CurrentMethod = null; - } - } - - public override TResult VisitAccessor(Accessor accessor, object data) - { - Debug.Assert(context.CurrentMethod == null); - try { - context.CurrentMethod = accessor.Annotation<MethodDefinition>(); - return base.VisitAccessor(accessor, data); - } finally { - context.CurrentMethod = null; - } - } - - void IAstTransform.Run(AstNode node) - { - node.AcceptVisitor(this, null); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs deleted file mode 100644 index 36811c1852..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs +++ /dev/null @@ -1,183 +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.Collections.Generic; -using System.Linq; -using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.PatternMatching; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - /// <summary> - /// If the first element of a constructor is a chained constructor call, convert it into a constructor initializer. - /// </summary> - public class ConvertConstructorCallIntoInitializer : DepthFirstAstVisitor<object, object>, IAstTransform - { - public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) - { - ExpressionStatement stmt = constructorDeclaration.Body.Statements.FirstOrDefault() as ExpressionStatement; - if (stmt == null) - return null; - InvocationExpression invocation = stmt.Expression as InvocationExpression; - if (invocation == null) - return null; - MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression; - if (mre != null && mre.MemberName == ".ctor") { - ConstructorInitializer ci = new ConstructorInitializer(); - if (mre.Target is ThisReferenceExpression) - ci.ConstructorInitializerType = ConstructorInitializerType.This; - else if (mre.Target is BaseReferenceExpression) - ci.ConstructorInitializerType = ConstructorInitializerType.Base; - else - return null; - // Move arguments from invocation to initializer: - invocation.Arguments.MoveTo(ci.Arguments); - // Add the initializer: (unless it is the default 'base()') - if (!(ci.ConstructorInitializerType == ConstructorInitializerType.Base && ci.Arguments.Count == 0)) - constructorDeclaration.Initializer = ci.WithAnnotation(invocation.Annotation<MethodReference>()); - // Remove the statement: - stmt.Remove(); - } - return null; - } - - static readonly ExpressionStatement fieldInitializerPattern = new ExpressionStatement { - Expression = new AssignmentExpression { - Left = new NamedNode("fieldAccess", new MemberReferenceExpression { - Target = new ThisReferenceExpression(), - MemberName = Pattern.AnyString - }), - Operator = AssignmentOperatorType.Assign, - Right = new AnyNode("initializer") - } - }; - - static readonly AstNode thisCallPattern = new ExpressionStatement(new ThisReferenceExpression().Invoke(".ctor", new Repeat(new AnyNode()))); - - public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) - { - // Handle initializers on instance fields - HandleInstanceFieldInitializers(typeDeclaration.Members); - - // Now convert base constructor calls to initializers: - base.VisitTypeDeclaration(typeDeclaration, data); - - // Remove single empty constructor: - RemoveSingleEmptyConstructor(typeDeclaration); - - // Handle initializers on static fields: - HandleStaticFieldInitializers(typeDeclaration.Members); - return null; - } - - void HandleInstanceFieldInitializers(IEnumerable<AstNode> members) - { - var instanceCtors = members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray(); - var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray(); - if (instanceCtorsNotChainingWithThis.Length > 0) { - MethodDefinition ctorMethodDef = instanceCtorsNotChainingWithThis[0].Annotation<MethodDefinition>(); - if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsValueType) - return; - - // Recognize field initializers: - // Convert first statement in all ctors (if all ctors have the same statement) into a field initializer. - bool allSame; - do { - Match m = fieldInitializerPattern.Match(instanceCtorsNotChainingWithThis[0].Body.FirstOrDefault()); - if (!m.Success) - break; - - FieldDefinition fieldDef = m.Get<AstNode>("fieldAccess").Single().Annotation<FieldReference>().ResolveWithinSameModule(); - if (fieldDef == null) - break; - AstNode fieldOrEventDecl = members.FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef); - if (fieldOrEventDecl == null) - break; - Expression initializer = m.Get<Expression>("initializer").Single(); - // 'this'/'base' cannot be used in field initializers - if (initializer.DescendantsAndSelf.Any(n => n is ThisReferenceExpression || n is BaseReferenceExpression)) - break; - - allSame = true; - for (int i = 1; i < instanceCtorsNotChainingWithThis.Length; i++) { - if (!instanceCtors[0].Body.First().IsMatch(instanceCtorsNotChainingWithThis[i].Body.FirstOrDefault())) - allSame = false; - } - if (allSame) { - foreach (var ctor in instanceCtorsNotChainingWithThis) - ctor.Body.First().Remove(); - fieldOrEventDecl.GetChildrenByRole(Roles.Variable).Single().Initializer = initializer.Detach(); - } - } while (allSame); - } - } - - void RemoveSingleEmptyConstructor(TypeDeclaration typeDeclaration) - { - var instanceCtors = typeDeclaration.Members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray(); - if (instanceCtors.Length == 1) { - ConstructorDeclaration emptyCtor = new ConstructorDeclaration(); - emptyCtor.Modifiers = ((typeDeclaration.Modifiers & Modifiers.Abstract) == Modifiers.Abstract ? Modifiers.Protected : Modifiers.Public); - emptyCtor.Body = new BlockStatement(); - if (emptyCtor.IsMatch(instanceCtors[0])) - instanceCtors[0].Remove(); - } - } - - void HandleStaticFieldInitializers(IEnumerable<AstNode> members) - { - // Convert static constructor into field initializers if the class is BeforeFieldInit - var staticCtor = members.OfType<ConstructorDeclaration>().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static); - if (staticCtor != null) { - MethodDefinition ctorMethodDef = staticCtor.Annotation<MethodDefinition>(); - if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsBeforeFieldInit) { - while (true) { - ExpressionStatement es = staticCtor.Body.Statements.FirstOrDefault() as ExpressionStatement; - if (es == null) - break; - AssignmentExpression assignment = es.Expression as AssignmentExpression; - if (assignment == null || assignment.Operator != AssignmentOperatorType.Assign) - break; - FieldDefinition fieldDef = assignment.Left.Annotation<FieldReference>().ResolveWithinSameModule(); - if (fieldDef == null || !fieldDef.IsStatic) - break; - FieldDeclaration fieldDecl = members.OfType<FieldDeclaration>().FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef); - if (fieldDecl == null) - break; - fieldDecl.Variables.Single().Initializer = assignment.Right.Detach(); - es.Remove(); - } - if (staticCtor.Body.Statements.Count == 0) - staticCtor.Remove(); - } - } - } - - void IAstTransform.Run(AstNode node) - { - // If we're viewing some set of members (fields are direct children of CompilationUnit), - // we also need to handle those: - HandleInstanceFieldInitializers(node.Children); - HandleStaticFieldInitializers(node.Children); - - node.AcceptVisitor(this, null); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/CustomPatterns.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/CustomPatterns.cs deleted file mode 100644 index 9c1d801974..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/CustomPatterns.cs +++ /dev/null @@ -1,109 +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.Linq; -using System.Reflection; -using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.PatternMatching; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - sealed class TypePattern : Pattern - { - readonly string ns; - readonly string name; - - public TypePattern(Type type) - { - this.ns = type.Namespace; - this.name = type.Name; - } - - public override bool DoMatch(INode other, Match match) - { - ComposedType ct = other as ComposedType; - AstType o; - if (ct != null && !ct.HasNullableSpecifier && ct.PointerRank == 0 && !ct.ArraySpecifiers.Any()) { - // Special case: ILSpy sometimes produces a ComposedType but then removed all array specifiers - // from it. In that case, we need to look at the base type for the annotations. - o = ct.BaseType; - } else { - o = other as AstType; - if (o == null) - return false; - } - TypeReference tr = o.Annotation<TypeReference>(); - return tr != null && tr.Namespace == ns && tr.Name == name; - } - - public override string ToString() - { - return name; - } - } - - sealed class LdTokenPattern : Pattern - { - AnyNode childNode; - - public LdTokenPattern(string groupName) - { - this.childNode = new AnyNode(groupName); - } - - public override bool DoMatch(INode other, Match match) - { - InvocationExpression ie = other as InvocationExpression; - if (ie != null && ie.Annotation<LdTokenAnnotation>() != null && ie.Arguments.Count == 1) { - return childNode.DoMatch(ie.Arguments.Single(), match); - } - return false; - } - - public override string ToString() - { - return "ldtoken(...)"; - } - } - - /// <summary> - /// typeof-Pattern that applies on the expanded form of typeof (prior to ReplaceMethodCallsWithOperators) - /// </summary> - sealed class TypeOfPattern : Pattern - { - INode childNode; - - public TypeOfPattern(string groupName) - { - childNode = new TypePattern(typeof(Type)).ToType().Invoke( - "GetTypeFromHandle", new TypeOfExpression(new AnyNode(groupName)).Member("TypeHandle")); - } - - public override bool DoMatch(INode other, Match match) - { - return childNode.DoMatch(other, match); - } - - public override string ToString() - { - return "typeof(...)"; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs deleted file mode 100644 index 298682afb6..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs +++ /dev/null @@ -1,58 +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 ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.PatternMatching; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - /// <summary> - /// Transforms decimal constant fields. - /// </summary> - public class DecimalConstantTransform : DepthFirstAstVisitor<object, object>, IAstTransform - { - static readonly PrimitiveType decimalType = new PrimitiveType("decimal"); - - public override object VisitFieldDeclaration(FieldDeclaration fieldDeclaration, object data) - { - const Modifiers staticReadOnly = Modifiers.Static | Modifiers.Readonly; - if ((fieldDeclaration.Modifiers & staticReadOnly) == staticReadOnly && decimalType.IsMatch(fieldDeclaration.ReturnType)) { - foreach (var attributeSection in fieldDeclaration.Attributes) { - foreach (var attribute in attributeSection.Attributes) { - TypeReference tr = attribute.Type.Annotation<TypeReference>(); - if (tr != null && tr.Name == "DecimalConstantAttribute" && tr.Namespace == "System.Runtime.CompilerServices") { - attribute.Remove(); - if (attributeSection.Attributes.Count == 0) - attributeSection.Remove(); - fieldDeclaration.Modifiers = (fieldDeclaration.Modifiers & ~staticReadOnly) | Modifiers.Const; - return null; - } - } - } - } - return null; - } - - public void Run(AstNode compilationUnit) - { - compilationUnit.AcceptVisitor(this, null); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs deleted file mode 100644 index ed1155ba15..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs +++ /dev/null @@ -1,368 +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.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using ICSharpCode.Decompiler.ILAst; -using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.CSharp.Analysis; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - /// <summary> - /// Moves variable declarations to improved positions. - /// </summary> - public class DeclareVariables : IAstTransform - { - sealed class VariableToDeclare - { - public AstType Type; - public string Name; - public ILVariable ILVariable; - - public AssignmentExpression ReplacedAssignment; - public Statement InsertionPoint; - } - - readonly CancellationToken cancellationToken; - List<VariableToDeclare> variablesToDeclare = new List<VariableToDeclare>(); - - public DeclareVariables(DecompilerContext context) - { - this.cancellationToken = context.CancellationToken; - } - - public void Run(AstNode node) - { - Run(node, null); - // Declare all the variables at the end, after all the logic has run. - // This is done so that definite assignment analysis can work on a single representation and doesn't have to be updated - // when we change the AST. - foreach (var v in variablesToDeclare) { - if (v.ReplacedAssignment == null) { - BlockStatement block = (BlockStatement)v.InsertionPoint.Parent; - var decl = new VariableDeclarationStatement((AstType)v.Type.Clone(), v.Name); - if (v.ILVariable != null) - decl.Variables.Single().AddAnnotation(v.ILVariable); - block.Statements.InsertBefore( - v.InsertionPoint, - decl); - } - } - // First do all the insertions, then do all the replacements. This is necessary because a replacement might remove our reference point from the AST. - foreach (var v in variablesToDeclare) { - if (v.ReplacedAssignment != null) { - // We clone the right expression so that it doesn't get removed from the old ExpressionStatement, - // which might be still in use by the definite assignment graph. - VariableInitializer initializer = new VariableInitializer(v.Name, v.ReplacedAssignment.Right.Detach()).CopyAnnotationsFrom(v.ReplacedAssignment).WithAnnotation(v.ILVariable); - VariableDeclarationStatement varDecl = new VariableDeclarationStatement { - Type = (AstType)v.Type.Clone(), - Variables = { initializer } - }; - ExpressionStatement es = v.ReplacedAssignment.Parent as ExpressionStatement; - if (es != null) { - // Note: if this crashes with 'Cannot replace the root node', check whether two variables were assigned the same name - es.ReplaceWith(varDecl.CopyAnnotationsFrom(es)); - } else { - v.ReplacedAssignment.ReplaceWith(varDecl); - } - } - } - variablesToDeclare = null; - } - - void Run(AstNode node, DefiniteAssignmentAnalysis daa) - { - BlockStatement block = node as BlockStatement; - if (block != null) { - var variables = block.Statements.TakeWhile(stmt => stmt is VariableDeclarationStatement) - .Cast<VariableDeclarationStatement>().ToList(); - if (variables.Count > 0) { - // remove old variable declarations: - foreach (VariableDeclarationStatement varDecl in variables) { - Debug.Assert(varDecl.Variables.Single().Initializer.IsNull); - varDecl.Remove(); - } - if (daa == null) { - // If possible, reuse the DefiniteAssignmentAnalysis that was created for the parent block - daa = new DefiniteAssignmentAnalysis(block, cancellationToken); - } - foreach (VariableDeclarationStatement varDecl in variables) { - VariableInitializer initializer = varDecl.Variables.Single(); - string variableName = initializer.Name; - ILVariable v = initializer.Annotation<ILVariable>(); - bool allowPassIntoLoops = initializer.Annotation<DelegateConstruction.CapturedVariableAnnotation>() == null; - DeclareVariableInBlock(daa, block, varDecl.Type, variableName, v, allowPassIntoLoops); - } - } - } - for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { - Run(child, daa); - } - } - - void DeclareVariableInBlock(DefiniteAssignmentAnalysis daa, BlockStatement block, AstType type, string variableName, ILVariable v, bool allowPassIntoLoops) - { - // declarationPoint: The point where the variable would be declared, if we decide to declare it in this block - Statement declarationPoint = null; - // Check whether we can move down the variable into the sub-blocks - bool canMoveVariableIntoSubBlocks = FindDeclarationPoint(daa, variableName, allowPassIntoLoops, block, out declarationPoint); - if (declarationPoint == null) { - // The variable isn't used at all - return; - } - if (canMoveVariableIntoSubBlocks) { - // Declare the variable within the sub-blocks - foreach (Statement stmt in block.Statements) { - ForStatement forStmt = stmt as ForStatement; - if (forStmt != null && forStmt.Initializers.Count == 1) { - // handle the special case of moving a variable into the for initializer - if (TryConvertAssignmentExpressionIntoVariableDeclaration(forStmt.Initializers.Single(), type, variableName)) - continue; - } - UsingStatement usingStmt = stmt as UsingStatement; - if (usingStmt != null && usingStmt.ResourceAcquisition is AssignmentExpression) { - // handle the special case of moving a variable into a using statement - if (TryConvertAssignmentExpressionIntoVariableDeclaration((Expression)usingStmt.ResourceAcquisition, type, variableName)) - continue; - } - IfElseStatement ies = stmt as IfElseStatement; - if (ies != null) { - foreach (var child in IfElseChainChildren(ies)) { - BlockStatement subBlock = child as BlockStatement; - if (subBlock != null) - DeclareVariableInBlock(daa, subBlock, type, variableName, v, allowPassIntoLoops); - } - continue; - } - foreach (AstNode child in stmt.Children) { - BlockStatement subBlock = child as BlockStatement; - if (subBlock != null) { - DeclareVariableInBlock(daa, subBlock, type, variableName, v, allowPassIntoLoops); - } else if (HasNestedBlocks(child)) { - foreach (BlockStatement nestedSubBlock in child.Children.OfType<BlockStatement>()) { - DeclareVariableInBlock(daa, nestedSubBlock, type, variableName, v, allowPassIntoLoops); - } - } - } - } - } else { - // Try converting an assignment expression into a VariableDeclarationStatement - if (!TryConvertAssignmentExpressionIntoVariableDeclaration(declarationPoint, type, variableName)) { - // Declare the variable in front of declarationPoint - variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ILVariable = v, InsertionPoint = declarationPoint }); - } - } - } - - bool TryConvertAssignmentExpressionIntoVariableDeclaration(Statement declarationPoint, AstType type, string variableName) - { - // convert the declarationPoint into a VariableDeclarationStatement - ExpressionStatement es = declarationPoint as ExpressionStatement; - if (es != null) { - return TryConvertAssignmentExpressionIntoVariableDeclaration(es.Expression, type, variableName); - } - return false; - } - - bool TryConvertAssignmentExpressionIntoVariableDeclaration(Expression expression, AstType type, string variableName) - { - AssignmentExpression ae = expression as AssignmentExpression; - if (ae != null && ae.Operator == AssignmentOperatorType.Assign) { - IdentifierExpression ident = ae.Left as IdentifierExpression; - if (ident != null && ident.Identifier == variableName) { - variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ILVariable = ident.Annotation<ILVariable>(), ReplacedAssignment = ae }); - return true; - } - } - return false; - } - - /// <summary> - /// Finds the declaration point for the variable within the specified block. - /// </summary> - /// <param name="daa"> - /// Definite assignment analysis, must be prepared for 'block' or one of its parents. - /// </param> - /// <param name="varDecl">The variable to declare</param> - /// <param name="block">The block in which the variable should be declared</param> - /// <param name="declarationPoint"> - /// Output parameter: the first statement within 'block' where the variable needs to be declared. - /// </param> - /// <returns> - /// Returns whether it is possible to move the variable declaration into sub-blocks. - /// </returns> - public static bool FindDeclarationPoint(DefiniteAssignmentAnalysis daa, VariableDeclarationStatement varDecl, BlockStatement block, out Statement declarationPoint) - { - string variableName = varDecl.Variables.Single().Name; - bool allowPassIntoLoops = varDecl.Variables.Single().Annotation<DelegateConstruction.CapturedVariableAnnotation>() == null; - return FindDeclarationPoint(daa, variableName, allowPassIntoLoops, block, out declarationPoint); - } - - static bool FindDeclarationPoint(DefiniteAssignmentAnalysis daa, string variableName, bool allowPassIntoLoops, BlockStatement block, out Statement declarationPoint) - { - // declarationPoint: The point where the variable would be declared, if we decide to declare it in this block - declarationPoint = null; - foreach (Statement stmt in block.Statements) { - if (UsesVariable(stmt, variableName)) { - if (declarationPoint == null) - declarationPoint = stmt; - if (!CanMoveVariableUseIntoSubBlock(stmt, variableName, allowPassIntoLoops)) { - // If it's not possible to move the variable use into a nested block, - // we need to declare the variable in this block - return false; - } - // If we can move the variable into the sub-block, we need to ensure that the remaining code - // does not use the value that was assigned by the first sub-block - Statement nextStatement = stmt.GetNextStatement(); - if (nextStatement != null) { - // Analyze the range from the next statement to the end of the block - daa.SetAnalyzedRange(nextStatement, block); - daa.Analyze(variableName); - if (daa.UnassignedVariableUses.Count > 0) { - return false; - } - } - } - } - return true; - } - - static bool CanMoveVariableUseIntoSubBlock(Statement stmt, string variableName, bool allowPassIntoLoops) - { - if (!allowPassIntoLoops && (stmt is ForStatement || stmt is ForeachStatement || stmt is DoWhileStatement || stmt is WhileStatement)) - return false; - - ForStatement forStatement = stmt as ForStatement; - if (forStatement != null && forStatement.Initializers.Count == 1) { - // for-statement is special case: we can move variable declarations into the initializer - ExpressionStatement es = forStatement.Initializers.Single() as ExpressionStatement; - if (es != null) { - AssignmentExpression ae = es.Expression as AssignmentExpression; - if (ae != null && ae.Operator == AssignmentOperatorType.Assign) { - IdentifierExpression ident = ae.Left as IdentifierExpression; - if (ident != null && ident.Identifier == variableName) { - return !UsesVariable(ae.Right, variableName); - } - } - } - } - - UsingStatement usingStatement = stmt as UsingStatement; - if (usingStatement != null) { - // using-statement is special case: we can move variable declarations into the initializer - AssignmentExpression ae = usingStatement.ResourceAcquisition as AssignmentExpression; - if (ae != null && ae.Operator == AssignmentOperatorType.Assign) { - IdentifierExpression ident = ae.Left as IdentifierExpression; - if (ident != null && ident.Identifier == variableName) { - return !UsesVariable(ae.Right, variableName); - } - } - } - - IfElseStatement ies = stmt as IfElseStatement; - if (ies != null) { - foreach (var child in IfElseChainChildren(ies)) { - if (!(child is BlockStatement) && UsesVariable(child, variableName)) - return false; - } - return true; - } - - // We can move the variable into a sub-block only if the variable is used in only that sub-block (and not in expressions such as the loop condition) - for (AstNode child = stmt.FirstChild; child != null; child = child.NextSibling) { - if (!(child is BlockStatement) && UsesVariable(child, variableName)) { - if (HasNestedBlocks(child)) { - // catch clauses/switch sections can contain nested blocks - for (AstNode grandchild = child.FirstChild; grandchild != null; grandchild = grandchild.NextSibling) { - if (!(grandchild is BlockStatement) && UsesVariable(grandchild, variableName)) - return false; - } - } else { - return false; - } - } - } - return true; - } - - static IEnumerable<AstNode> IfElseChainChildren(IfElseStatement ies) - { - IfElseStatement prev; - do { - yield return ies.Condition; - yield return ies.TrueStatement; - prev = ies; - ies = ies.FalseStatement as IfElseStatement; - } while (ies != null); - if (!prev.FalseStatement.IsNull) - yield return prev.FalseStatement; - } - - static bool HasNestedBlocks(AstNode node) - { - return node is CatchClause || node is SwitchSection; - } - - static bool UsesVariable(AstNode node, string variableName) - { - IdentifierExpression ie = node as IdentifierExpression; - if (ie != null && ie.Identifier == variableName) - return true; - - FixedStatement fixedStatement = node as FixedStatement; - if (fixedStatement != null) { - foreach (VariableInitializer v in fixedStatement.Variables) { - if (v.Name == variableName) - return false; // no need to introduce the variable here - } - } - - ForeachStatement foreachStatement = node as ForeachStatement; - if (foreachStatement != null) { - if (foreachStatement.VariableName == variableName) - return false; // no need to introduce the variable here - } - - UsingStatement usingStatement = node as UsingStatement; - if (usingStatement != null) { - VariableDeclarationStatement varDecl = usingStatement.ResourceAcquisition as VariableDeclarationStatement; - if (varDecl != null) { - foreach (VariableInitializer v in varDecl.Variables) { - if (v.Name == variableName) - return false; // no need to introduce the variable here - } - } - } - - CatchClause catchClause = node as CatchClause; - if (catchClause != null && catchClause.VariableName == variableName) { - return false; // no need to introduce the variable here - } - - for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { - if (UsesVariable(child, variableName)) - return true; - } - return false; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs deleted file mode 100644 index 0741466f95..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ /dev/null @@ -1,502 +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.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using ICSharpCode.Decompiler; -using ICSharpCode.Decompiler.ILAst; -using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.PatternMatching; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - /// <summary> - /// Converts "new Action(obj, ldftn(func))" into "new Action(obj.func)". - /// For anonymous methods, creates an AnonymousMethodExpression. - /// Also gets rid of any "Display Classes" left over after inlining an anonymous method. - /// </summary> - public class DelegateConstruction : ContextTrackingVisitor<object> - { - internal sealed class Annotation - { - /// <summary> - /// ldftn or ldvirtftn? - /// </summary> - public readonly bool IsVirtual; - - public Annotation(bool isVirtual) - { - this.IsVirtual = isVirtual; - } - } - - internal sealed class CapturedVariableAnnotation - { - } - - List<string> currentlyUsedVariableNames = new List<string>(); - - public DelegateConstruction(DecompilerContext context) : base(context) - { - } - - public override object VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, object data) - { - if (objectCreateExpression.Arguments.Count == 2) { - Expression obj = objectCreateExpression.Arguments.First(); - Expression func = objectCreateExpression.Arguments.Last(); - Annotation annotation = func.Annotation<Annotation>(); - if (annotation != null) { - IdentifierExpression methodIdent = (IdentifierExpression)((InvocationExpression)func).Arguments.Single(); - MethodReference method = methodIdent.Annotation<MethodReference>(); - if (method != null) { - if (HandleAnonymousMethod(objectCreateExpression, obj, method)) - return null; - // Perform the transformation to "new Action(obj.func)". - obj.Remove(); - methodIdent.Remove(); - if (!annotation.IsVirtual && obj is ThisReferenceExpression) { - // maybe it's getting the pointer of a base method? - if (method.DeclaringType.GetElementType() != context.CurrentType) { - obj = new BaseReferenceExpression(); - } - } - if (!annotation.IsVirtual && obj is NullReferenceExpression && !method.HasThis) { - // We're loading a static method. - // However it is possible to load extension methods with an instance, so we compare the number of arguments: - bool isExtensionMethod = false; - TypeReference delegateType = objectCreateExpression.Type.Annotation<TypeReference>(); - if (delegateType != null) { - TypeDefinition delegateTypeDef = delegateType.Resolve(); - if (delegateTypeDef != null) { - MethodDefinition invokeMethod = delegateTypeDef.Methods.FirstOrDefault(m => m.Name == "Invoke"); - if (invokeMethod != null) { - isExtensionMethod = (invokeMethod.Parameters.Count + 1 == method.Parameters.Count); - } - } - } - if (!isExtensionMethod) { - obj = new TypeReferenceExpression { Type = AstBuilder.ConvertType(method.DeclaringType) }; - } - } - // now transform the identifier into a member reference - MemberReferenceExpression mre = new MemberReferenceExpression(); - mre.Target = obj; - mre.MemberName = methodIdent.Identifier; - methodIdent.TypeArguments.MoveTo(mre.TypeArguments); - mre.AddAnnotation(method); - objectCreateExpression.Arguments.Clear(); - objectCreateExpression.Arguments.Add(mre); - return null; - } - } - } - return base.VisitObjectCreateExpression(objectCreateExpression, data); - } - - internal static bool IsAnonymousMethod(DecompilerContext context, MethodDefinition method) - { - if (method == null || !(method.HasGeneratedName() || method.Name.Contains("$"))) - return false; - if (!(method.IsCompilerGenerated() || IsPotentialClosure(context, method.DeclaringType))) - return false; - return true; - } - - bool HandleAnonymousMethod(ObjectCreateExpression objectCreateExpression, Expression target, MethodReference methodRef) - { - if (!context.Settings.AnonymousMethods) - return false; // anonymous method decompilation is disabled - if (target != null && !(target is IdentifierExpression || target is ThisReferenceExpression || target is NullReferenceExpression)) - return false; // don't copy arbitrary expressions, deal with identifiers only - - // Anonymous methods are defined in the same assembly - MethodDefinition method = methodRef.ResolveWithinSameModule(); - if (!IsAnonymousMethod(context, method)) - return false; - - // Create AnonymousMethodExpression and prepare parameters - AnonymousMethodExpression ame = new AnonymousMethodExpression(); - ame.CopyAnnotationsFrom(objectCreateExpression); // copy ILRanges etc. - ame.RemoveAnnotations<MethodReference>(); // remove reference to delegate ctor - ame.AddAnnotation(method); // add reference to anonymous method - ame.Parameters.AddRange(AstBuilder.MakeParameters(method, isLambda: true)); - ame.HasParameterList = true; - - // rename variables so that they don't conflict with the parameters: - foreach (ParameterDeclaration pd in ame.Parameters) { - EnsureVariableNameIsAvailable(objectCreateExpression, pd.Name); - } - - // Decompile the anonymous method: - - DecompilerContext subContext = context.Clone(); - subContext.CurrentMethod = method; - subContext.CurrentMethodIsAsync = false; - subContext.ReservedVariableNames.AddRange(currentlyUsedVariableNames); - BlockStatement body = AstMethodBodyBuilder.CreateMethodBody(method, subContext, ame.Parameters); - TransformationPipeline.RunTransformationsUntil(body, v => v is DelegateConstruction, subContext); - body.AcceptVisitor(this, null); - - - bool isLambda = false; - if (ame.Parameters.All(p => p.ParameterModifier == ParameterModifier.None)) { - isLambda = (body.Statements.Count == 1 && body.Statements.Single() is ReturnStatement); - } - // Remove the parameter list from an AnonymousMethodExpression if the original method had no names, - // and the parameters are not used in the method body - if (!isLambda && method.Parameters.All(p => string.IsNullOrEmpty(p.Name))) { - var parameterReferencingIdentifiers = - from ident in body.Descendants.OfType<IdentifierExpression>() - let v = ident.Annotation<ILVariable>() - where v != null && v.IsParameter && method.Parameters.Contains(v.OriginalParameter) - select ident; - if (!parameterReferencingIdentifiers.Any()) { - ame.Parameters.Clear(); - ame.HasParameterList = false; - } - } - - // Replace all occurrences of 'this' in the method body with the delegate's target: - foreach (AstNode node in body.Descendants) { - if (node is ThisReferenceExpression) - node.ReplaceWith(target.Clone()); - } - Expression replacement; - if (isLambda) { - LambdaExpression lambda = new LambdaExpression(); - lambda.CopyAnnotationsFrom(ame); - ame.Parameters.MoveTo(lambda.Parameters); - Expression returnExpr = ((ReturnStatement)body.Statements.Single()).Expression; - returnExpr.Remove(); - lambda.Body = returnExpr; - replacement = lambda; - } else { - ame.Body = body; - replacement = ame; - } - var expectedType = objectCreateExpression.Annotation<TypeInformation>().ExpectedType.Resolve(); - if (expectedType != null && !expectedType.IsDelegate()) { - var simplifiedDelegateCreation = (ObjectCreateExpression)objectCreateExpression.Clone(); - simplifiedDelegateCreation.Arguments.Clear(); - simplifiedDelegateCreation.Arguments.Add(replacement); - replacement = simplifiedDelegateCreation; - } - objectCreateExpression.ReplaceWith(replacement); - return true; - } - - internal static bool IsPotentialClosure(DecompilerContext context, TypeDefinition potentialDisplayClass) - { - if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) - return false; - // check that methodContainingType is within containingType - while (potentialDisplayClass != context.CurrentType) { - potentialDisplayClass = potentialDisplayClass.DeclaringType; - if (potentialDisplayClass == null) - return false; - } - return true; - } - - public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data) - { - if (context.Settings.ExpressionTrees && ExpressionTreeConverter.CouldBeExpressionTree(invocationExpression)) { - Expression converted = ExpressionTreeConverter.TryConvert(context, invocationExpression); - if (converted != null) { - invocationExpression.ReplaceWith(converted); - return converted.AcceptVisitor(this, data); - } - } - return base.VisitInvocationExpression(invocationExpression, data); - } - - #region Track current variables - public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) - { - Debug.Assert(currentlyUsedVariableNames.Count == 0); - try { - currentlyUsedVariableNames.AddRange(methodDeclaration.Parameters.Select(p => p.Name)); - return base.VisitMethodDeclaration(methodDeclaration, data); - } finally { - currentlyUsedVariableNames.Clear(); - } - } - - public override object VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, object data) - { - Debug.Assert(currentlyUsedVariableNames.Count == 0); - try { - currentlyUsedVariableNames.AddRange(operatorDeclaration.Parameters.Select(p => p.Name)); - return base.VisitOperatorDeclaration(operatorDeclaration, data); - } finally { - currentlyUsedVariableNames.Clear(); - } - } - - public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) - { - Debug.Assert(currentlyUsedVariableNames.Count == 0); - try { - currentlyUsedVariableNames.AddRange(constructorDeclaration.Parameters.Select(p => p.Name)); - return base.VisitConstructorDeclaration(constructorDeclaration, data); - } finally { - currentlyUsedVariableNames.Clear(); - } - } - - public override object VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration, object data) - { - Debug.Assert(currentlyUsedVariableNames.Count == 0); - try { - currentlyUsedVariableNames.AddRange(indexerDeclaration.Parameters.Select(p => p.Name)); - return base.VisitIndexerDeclaration(indexerDeclaration, data); - } finally { - currentlyUsedVariableNames.Clear(); - } - } - - public override object VisitAccessor(Accessor accessor, object data) - { - try { - currentlyUsedVariableNames.Add("value"); - return base.VisitAccessor(accessor, data); - } finally { - currentlyUsedVariableNames.RemoveAt(currentlyUsedVariableNames.Count - 1); - } - } - - public override object VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, object data) - { - foreach (VariableInitializer v in variableDeclarationStatement.Variables) - currentlyUsedVariableNames.Add(v.Name); - return base.VisitVariableDeclarationStatement(variableDeclarationStatement, data); - } - - public override object VisitFixedStatement(FixedStatement fixedStatement, object data) - { - foreach (VariableInitializer v in fixedStatement.Variables) - currentlyUsedVariableNames.Add(v.Name); - return base.VisitFixedStatement(fixedStatement, data); - } - #endregion - - static readonly ExpressionStatement displayClassAssignmentPattern = - new ExpressionStatement(new AssignmentExpression( - new NamedNode("variable", new IdentifierExpression(Pattern.AnyString)), - new ObjectCreateExpression { Type = new AnyNode("type") } - )); - - public override object VisitBlockStatement(BlockStatement blockStatement, object data) - { - int numberOfVariablesOutsideBlock = currentlyUsedVariableNames.Count; - base.VisitBlockStatement(blockStatement, data); - foreach (ExpressionStatement stmt in blockStatement.Statements.OfType<ExpressionStatement>().ToArray()) { - Match displayClassAssignmentMatch = displayClassAssignmentPattern.Match(stmt); - if (!displayClassAssignmentMatch.Success) - continue; - - ILVariable variable = displayClassAssignmentMatch.Get<AstNode>("variable").Single().Annotation<ILVariable>(); - if (variable == null) - continue; - TypeDefinition type = variable.Type.ResolveWithinSameModule(); - if (!IsPotentialClosure(context, type)) - continue; - if (displayClassAssignmentMatch.Get<AstType>("type").Single().Annotation<TypeReference>().ResolveWithinSameModule() != type) - continue; - - // Looks like we found a display class creation. Now let's verify that the variable is used only for field accesses: - bool ok = true; - foreach (var identExpr in blockStatement.Descendants.OfType<IdentifierExpression>()) { - if (identExpr.Identifier == variable.Name && identExpr != displayClassAssignmentMatch.Get("variable").Single()) { - if (!(identExpr.Parent is MemberReferenceExpression && identExpr.Parent.Annotation<FieldReference>() != null)) - ok = false; - } - } - if (!ok) - continue; - Dictionary<FieldReference, AstNode> dict = new Dictionary<FieldReference, AstNode>(); - - // Delete the variable declaration statement: - VariableDeclarationStatement displayClassVarDecl = PatternStatementTransform.FindVariableDeclaration(stmt, variable.Name); - if (displayClassVarDecl != null) - displayClassVarDecl.Remove(); - - // Delete the assignment statement: - AstNode cur = stmt.NextSibling; - stmt.Remove(); - - // Delete any following statements as long as they assign parameters to the display class - BlockStatement rootBlock = blockStatement.Ancestors.OfType<BlockStatement>().LastOrDefault() ?? blockStatement; - List<ILVariable> parameterOccurrances = rootBlock.Descendants.OfType<IdentifierExpression>() - .Select(n => n.Annotation<ILVariable>()).Where(p => p != null && p.IsParameter).ToList(); - AstNode next; - for (; cur != null; cur = next) { - next = cur.NextSibling; - - // Test for the pattern: - // "variableName.MemberName = right;" - ExpressionStatement closureFieldAssignmentPattern = new ExpressionStatement( - new AssignmentExpression( - new NamedNode("left", new MemberReferenceExpression { - Target = new IdentifierExpression(variable.Name), - MemberName = Pattern.AnyString - }), - new AnyNode("right") - ) - ); - Match m = closureFieldAssignmentPattern.Match(cur); - if (m.Success) { - FieldDefinition fieldDef = m.Get<MemberReferenceExpression>("left").Single().Annotation<FieldReference>().ResolveWithinSameModule(); - AstNode right = m.Get<AstNode>("right").Single(); - bool isParameter = false; - bool isDisplayClassParentPointerAssignment = false; - if (right is ThisReferenceExpression) { - isParameter = true; - } else if (right is IdentifierExpression) { - // handle parameters only if the whole method contains no other occurrence except for 'right' - ILVariable v = right.Annotation<ILVariable>(); - isParameter = v.IsParameter && parameterOccurrances.Count(c => c == v) == 1; - if (!isParameter && IsPotentialClosure(context, v.Type.ResolveWithinSameModule())) { - // parent display class within the same method - // (closure2.localsX = closure1;) - isDisplayClassParentPointerAssignment = true; - } - } else if (right is MemberReferenceExpression) { - // copy of parent display class reference from an outer lambda - // closure2.localsX = this.localsY - MemberReferenceExpression mre = m.Get<MemberReferenceExpression>("right").Single(); - do { - // descend into the targets of the mre as long as the field types are closures - FieldDefinition fieldDef2 = mre.Annotation<FieldReference>().ResolveWithinSameModule(); - if (fieldDef2 == null || !IsPotentialClosure(context, fieldDef2.FieldType.ResolveWithinSameModule())) { - break; - } - // if we finally get to a this reference, it's copying a display class parent pointer - if (mre.Target is ThisReferenceExpression) { - isDisplayClassParentPointerAssignment = true; - } - mre = mre.Target as MemberReferenceExpression; - } while (mre != null); - } - if (isParameter || isDisplayClassParentPointerAssignment) { - dict[fieldDef] = right; - cur.Remove(); - } else { - break; - } - } else { - break; - } - } - - // Now create variables for all fields of the display class (except for those that we already handled as parameters) - List<Tuple<AstType, ILVariable>> variablesToDeclare = new List<Tuple<AstType, ILVariable>>(); - foreach (FieldDefinition field in type.Fields) { - if (field.IsStatic) - continue; // skip static fields - if (dict.ContainsKey(field)) // skip field if it already was handled as parameter - continue; - string capturedVariableName = field.Name; - if (capturedVariableName.StartsWith("$VB$Local_", StringComparison.Ordinal) && capturedVariableName.Length > 10) - capturedVariableName = capturedVariableName.Substring(10); - EnsureVariableNameIsAvailable(blockStatement, capturedVariableName); - currentlyUsedVariableNames.Add(capturedVariableName); - ILVariable ilVar = new ILVariable - { - IsGenerated = true, - Name = capturedVariableName, - Type = field.FieldType, - }; - variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), ilVar)); - dict[field] = new IdentifierExpression(capturedVariableName).WithAnnotation(ilVar); - } - - // Now figure out where the closure was accessed and use the simpler replacement expression there: - foreach (var identExpr in blockStatement.Descendants.OfType<IdentifierExpression>()) { - if (identExpr.Identifier == variable.Name) { - MemberReferenceExpression mre = (MemberReferenceExpression)identExpr.Parent; - AstNode replacement; - if (dict.TryGetValue(mre.Annotation<FieldReference>().ResolveWithinSameModule(), out replacement)) { - mre.ReplaceWith(replacement.Clone()); - } - } - } - // Now insert the variable declarations (we can do this after the replacements only so that the scope detection works): - Statement insertionPoint = blockStatement.Statements.FirstOrDefault(); - foreach (var tuple in variablesToDeclare) { - var newVarDecl = new VariableDeclarationStatement(tuple.Item1, tuple.Item2.Name); - newVarDecl.Variables.Single().AddAnnotation(new CapturedVariableAnnotation()); - newVarDecl.Variables.Single().AddAnnotation(tuple.Item2); - blockStatement.Statements.InsertBefore(insertionPoint, newVarDecl); - } - } - currentlyUsedVariableNames.RemoveRange(numberOfVariablesOutsideBlock, currentlyUsedVariableNames.Count - numberOfVariablesOutsideBlock); - return null; - } - - void EnsureVariableNameIsAvailable(AstNode currentNode, string name) - { - int pos = currentlyUsedVariableNames.IndexOf(name); - if (pos < 0) { - // name is still available - return; - } - // Naming conflict. Let's rename the existing variable so that the field keeps the name from metadata. - NameVariables nv = new NameVariables(); - // Add currently used variable and parameter names - foreach (string nameInUse in currentlyUsedVariableNames) - nv.AddExistingName(nameInUse); - // variables declared in child nodes of this block - foreach (VariableInitializer vi in currentNode.Descendants.OfType<VariableInitializer>()) - nv.AddExistingName(vi.Name); - // parameters in child lambdas - foreach (ParameterDeclaration pd in currentNode.Descendants.OfType<ParameterDeclaration>()) - nv.AddExistingName(pd.Name); - - string newName = nv.GetAlternativeName(name); - currentlyUsedVariableNames[pos] = newName; - - // find top-most block - AstNode topMostBlock = currentNode.Ancestors.OfType<BlockStatement>().LastOrDefault() ?? currentNode; - - // rename identifiers - foreach (IdentifierExpression ident in topMostBlock.Descendants.OfType<IdentifierExpression>()) { - if (ident.Identifier == name) { - ident.Identifier = newName; - ILVariable v = ident.Annotation<ILVariable>(); - if (v != null) - v.Name = newName; - } - } - // rename variable declarations - foreach (VariableInitializer vi in topMostBlock.Descendants.OfType<VariableInitializer>()) { - if (vi.Name == name) { - vi.Name = newName; - ILVariable v = vi.Annotation<ILVariable>(); - if (v != null) - v.Name = newName; - } - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs deleted file mode 100644 index ffd6f5ac76..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs +++ /dev/null @@ -1,875 +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.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Reflection; -using ICSharpCode.Decompiler.ILAst; -using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.PatternMatching; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - public class ExpressionTreeConverter - { - #region static TryConvert method - public static bool CouldBeExpressionTree(InvocationExpression expr) - { - if (expr != null && expr.Arguments.Count == 2) { - MethodReference mr = expr.Annotation<MethodReference>(); - return mr != null && mr.Name == "Lambda" && mr.DeclaringType.FullName == "System.Linq.Expressions.Expression"; - } - return false; - } - - public static Expression TryConvert(DecompilerContext context, Expression expr) - { - Expression converted = new ExpressionTreeConverter(context).Convert(expr); - if (converted != null) { - converted.AddAnnotation(new ExpressionTreeLambdaAnnotation()); - } - return converted; - } - #endregion - - readonly DecompilerContext context; - Stack<LambdaExpression> activeLambdas = new Stack<LambdaExpression>(); - - private ExpressionTreeConverter(DecompilerContext context) - { - this.context = context; - } - - #region Main Convert method - Expression Convert(Expression expr) - { - InvocationExpression invocation = expr as InvocationExpression; - if (invocation != null) { - MethodReference mr = invocation.Annotation<MethodReference>(); - if (mr != null && mr.DeclaringType.FullName == "System.Linq.Expressions.Expression") { - switch (mr.Name) { - case "Add": - return ConvertBinaryOperator(invocation, BinaryOperatorType.Add, false); - case "AddChecked": - return ConvertBinaryOperator(invocation, BinaryOperatorType.Add, true); - case "AddAssign": - return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Add, false); - case "AddAssignChecked": - return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Add, true); - case "And": - return ConvertBinaryOperator(invocation, BinaryOperatorType.BitwiseAnd); - case "AndAlso": - return ConvertBinaryOperator(invocation, BinaryOperatorType.ConditionalAnd); - case "AndAssign": - return ConvertAssignmentOperator(invocation, AssignmentOperatorType.BitwiseAnd); - case "ArrayAccess": - case "ArrayIndex": - return ConvertArrayIndex(invocation); - case "ArrayLength": - return ConvertArrayLength(invocation); - case "Assign": - return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Assign); - case "Call": - return ConvertCall(invocation); - case "Coalesce": - return ConvertBinaryOperator(invocation, BinaryOperatorType.NullCoalescing); - case "Condition": - return ConvertCondition(invocation); - case "Constant": - if (invocation.Arguments.Count >= 1) - return invocation.Arguments.First().Clone(); - else - return NotSupported(expr); - case "Convert": - return ConvertCast(invocation, false); - case "ConvertChecked": - return ConvertCast(invocation, true); - case "Divide": - return ConvertBinaryOperator(invocation, BinaryOperatorType.Divide); - case "DivideAssign": - return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Divide); - case "Equal": - return ConvertBinaryOperator(invocation, BinaryOperatorType.Equality); - case "ExclusiveOr": - return ConvertBinaryOperator(invocation, BinaryOperatorType.ExclusiveOr); - case "ExclusiveOrAssign": - return ConvertAssignmentOperator(invocation, AssignmentOperatorType.ExclusiveOr); - case "Field": - return ConvertField(invocation); - case "GreaterThan": - return ConvertBinaryOperator(invocation, BinaryOperatorType.GreaterThan); - case "GreaterThanOrEqual": - return ConvertBinaryOperator(invocation, BinaryOperatorType.GreaterThanOrEqual); - case "Invoke": - return ConvertInvoke(invocation); - case "Lambda": - return ConvertLambda(invocation); - case "LeftShift": - return ConvertBinaryOperator(invocation, BinaryOperatorType.ShiftLeft); - case "LeftShiftAssign": - return ConvertAssignmentOperator(invocation, AssignmentOperatorType.ShiftLeft); - case "LessThan": - return ConvertBinaryOperator(invocation, BinaryOperatorType.LessThan); - case "LessThanOrEqual": - return ConvertBinaryOperator(invocation, BinaryOperatorType.LessThanOrEqual); - case "ListInit": - return ConvertListInit(invocation); - case "MemberInit": - return ConvertMemberInit(invocation); - case "Modulo": - return ConvertBinaryOperator(invocation, BinaryOperatorType.Modulus); - case "ModuloAssign": - return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Modulus); - case "Multiply": - return ConvertBinaryOperator(invocation, BinaryOperatorType.Multiply, false); - case "MultiplyChecked": - return ConvertBinaryOperator(invocation, BinaryOperatorType.Multiply, true); - case "MultiplyAssign": - return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Multiply, false); - case "MultiplyAssignChecked": - return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Multiply, true); - case "Negate": - return ConvertUnaryOperator(invocation, UnaryOperatorType.Minus, false); - case "NegateChecked": - return ConvertUnaryOperator(invocation, UnaryOperatorType.Minus, true); - case "New": - return ConvertNewObject(invocation); - case "NewArrayBounds": - return ConvertNewArrayBounds(invocation); - case "NewArrayInit": - return ConvertNewArrayInit(invocation); - case "Not": - return ConvertUnaryOperator(invocation, UnaryOperatorType.Not); - case "NotEqual": - return ConvertBinaryOperator(invocation, BinaryOperatorType.InEquality); - case "OnesComplement": - return ConvertUnaryOperator(invocation, UnaryOperatorType.BitNot); - case "Or": - return ConvertBinaryOperator(invocation, BinaryOperatorType.BitwiseOr); - case "OrAssign": - return ConvertAssignmentOperator(invocation, AssignmentOperatorType.BitwiseOr); - case "OrElse": - return ConvertBinaryOperator(invocation, BinaryOperatorType.ConditionalOr); - case "Property": - return ConvertProperty(invocation); - case "Quote": - if (invocation.Arguments.Count == 1) - return Convert(invocation.Arguments.Single()); - else - return NotSupported(invocation); - case "RightShift": - return ConvertBinaryOperator(invocation, BinaryOperatorType.ShiftRight); - case "RightShiftAssign": - return ConvertAssignmentOperator(invocation, AssignmentOperatorType.ShiftRight); - case "Subtract": - return ConvertBinaryOperator(invocation, BinaryOperatorType.Subtract, false); - case "SubtractChecked": - return ConvertBinaryOperator(invocation, BinaryOperatorType.Subtract, true); - case "SubtractAssign": - return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Subtract, false); - case "SubtractAssignChecked": - return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Subtract, true); - case "TypeAs": - return ConvertTypeAs(invocation); - case "TypeIs": - return ConvertTypeIs(invocation); - } - } - } - IdentifierExpression ident = expr as IdentifierExpression; - if (ident != null) { - ILVariable v = ident.Annotation<ILVariable>(); - if (v != null) { - foreach (LambdaExpression lambda in activeLambdas) { - foreach (ParameterDeclaration p in lambda.Parameters) { - if (p.Annotation<ILVariable>() == v) - return new IdentifierExpression(p.Name).WithAnnotation(v); - } - } - } - } - return NotSupported(expr); - } - - Expression NotSupported(Expression expr) - { - Debug.WriteLine("Expression Tree Conversion Failed: '" + expr + "' is not supported"); - return null; - } - #endregion - - #region Convert Lambda - static readonly Expression emptyArrayPattern = new ArrayCreateExpression { - Type = new AnyNode(), - Arguments = { new PrimitiveExpression(0) } - }; - - Expression ConvertLambda(InvocationExpression invocation) - { - if (invocation.Arguments.Count != 2) - return NotSupported(invocation); - LambdaExpression lambda = new LambdaExpression(); - Expression body = invocation.Arguments.First(); - ArrayCreateExpression parameterArray = invocation.Arguments.Last() as ArrayCreateExpression; - if (parameterArray == null) - return NotSupported(invocation); - - var annotation = body.Annotation<ParameterDeclarationAnnotation>(); - if (annotation != null) { - lambda.Parameters.AddRange(annotation.Parameters); - } else { - // No parameter declaration annotation found. - if (!emptyArrayPattern.IsMatch(parameterArray)) - return null; - } - - activeLambdas.Push(lambda); - Expression convertedBody = Convert(body); - activeLambdas.Pop(); - if (convertedBody == null) - return null; - lambda.Body = convertedBody; - return lambda; - } - #endregion - - #region Convert Field - static readonly Expression getFieldFromHandlePattern = - new TypePattern(typeof(FieldInfo)).ToType().Invoke( - "GetFieldFromHandle", - new LdTokenPattern("field").ToExpression().Member("FieldHandle"), - new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle")) - ); - - Expression ConvertField(InvocationExpression invocation) - { - if (invocation.Arguments.Count != 2) - return NotSupported(invocation); - - Expression fieldInfoExpr = invocation.Arguments.ElementAt(1); - Match m = getFieldFromHandlePattern.Match(fieldInfoExpr); - if (!m.Success) - return NotSupported(invocation); - - FieldReference fr = m.Get<AstNode>("field").Single().Annotation<FieldReference>(); - if (fr == null) - return null; - - Expression target = invocation.Arguments.ElementAt(0); - Expression convertedTarget; - if (target is NullReferenceExpression) { - if (m.Has("declaringType")) - convertedTarget = new TypeReferenceExpression(m.Get<AstType>("declaringType").Single().Clone()); - else - convertedTarget = new TypeReferenceExpression(AstBuilder.ConvertType(fr.DeclaringType)); - } else { - convertedTarget = Convert(target); - if (convertedTarget == null) - return null; - } - - return convertedTarget.Member(fr.Name).WithAnnotation(fr); - } - #endregion - - #region Convert Property - static readonly Expression getMethodFromHandlePattern = - new TypePattern(typeof(MethodBase)).ToType().Invoke( - "GetMethodFromHandle", - new LdTokenPattern("method").ToExpression().Member("MethodHandle"), - new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle")) - ).CastTo(new TypePattern(typeof(MethodInfo))); - - Expression ConvertProperty(InvocationExpression invocation) - { - if (invocation.Arguments.Count != 2) - return NotSupported(invocation); - - Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(1)); - if (!m.Success) - return NotSupported(invocation); - - MethodReference mr = m.Get<AstNode>("method").Single().Annotation<MethodReference>(); - if (mr == null) - return null; - - Expression target = invocation.Arguments.ElementAt(0); - Expression convertedTarget; - if (target is NullReferenceExpression) { - if (m.Has("declaringType")) - convertedTarget = new TypeReferenceExpression(m.Get<AstType>("declaringType").Single().Clone()); - else - convertedTarget = new TypeReferenceExpression(AstBuilder.ConvertType(mr.DeclaringType)); - } else { - convertedTarget = Convert(target); - if (convertedTarget == null) - return null; - } - - return convertedTarget.Member(GetPropertyName(mr)).WithAnnotation(mr); - } - - string GetPropertyName(MethodReference accessor) - { - string name = accessor.Name; - if (name.StartsWith("get_", StringComparison.Ordinal) || name.StartsWith("set_", StringComparison.Ordinal)) - name = name.Substring(4); - return name; - } - #endregion - - #region Convert Call - Expression ConvertCall(InvocationExpression invocation) - { - if (invocation.Arguments.Count < 2) - return NotSupported(invocation); - - Expression target; - int firstArgumentPosition; - - Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(0)); - if (m.Success) { - target = null; - firstArgumentPosition = 1; - } else { - m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(1)); - if (!m.Success) - return NotSupported(invocation); - target = invocation.Arguments.ElementAt(0); - firstArgumentPosition = 2; - } - - MethodReference mr = m.Get<AstNode>("method").Single().Annotation<MethodReference>(); - if (mr == null) - return null; - - Expression convertedTarget; - if (target == null || target is NullReferenceExpression) { - // static method - if (m.Has("declaringType")) - convertedTarget = new TypeReferenceExpression(m.Get<AstType>("declaringType").Single().Clone()); - else - convertedTarget = new TypeReferenceExpression(AstBuilder.ConvertType(mr.DeclaringType)); - } else { - convertedTarget = Convert(target); - if (convertedTarget == null) - return null; - } - - MemberReferenceExpression mre = convertedTarget.Member(mr.Name); - GenericInstanceMethod gim = mr as GenericInstanceMethod; - if (gim != null) { - foreach (TypeReference tr in gim.GenericArguments) { - mre.TypeArguments.Add(AstBuilder.ConvertType(tr)); - } - } - IList<Expression> arguments = null; - if (invocation.Arguments.Count == firstArgumentPosition + 1) { - Expression argumentArray = invocation.Arguments.ElementAt(firstArgumentPosition); - arguments = ConvertExpressionsArray(argumentArray); - } - if (arguments == null) { - arguments = new List<Expression>(); - foreach (Expression argument in invocation.Arguments.Skip(firstArgumentPosition)) { - Expression convertedArgument = Convert(argument); - if (convertedArgument == null) - return null; - arguments.Add(convertedArgument); - } - } - MethodDefinition methodDef = mr.Resolve(); - if (methodDef != null && methodDef.IsGetter) { - PropertyDefinition indexer = AstMethodBodyBuilder.GetIndexer(methodDef); - if (indexer != null) - return new IndexerExpression(mre.Target.Detach(), arguments).WithAnnotation(indexer); - } - return new InvocationExpression(mre, arguments).WithAnnotation(mr); - } - - Expression ConvertInvoke(InvocationExpression invocation) - { - if (invocation.Arguments.Count != 2) - return NotSupported(invocation); - - Expression convertedTarget = Convert(invocation.Arguments.ElementAt(0)); - IList<Expression> convertedArguments = ConvertExpressionsArray(invocation.Arguments.ElementAt(1)); - if (convertedTarget != null && convertedArguments != null) - return new InvocationExpression(convertedTarget, convertedArguments); - else - return null; - } - #endregion - - #region Convert Binary Operator - static readonly Pattern trueOrFalse = new Choice { - new PrimitiveExpression(true), - new PrimitiveExpression(false) - }; - - Expression ConvertBinaryOperator(InvocationExpression invocation, BinaryOperatorType op, bool? isChecked = null) - { - if (invocation.Arguments.Count < 2) - return NotSupported(invocation); - - Expression left = Convert(invocation.Arguments.ElementAt(0)); - if (left == null) - return null; - Expression right = Convert(invocation.Arguments.ElementAt(1)); - if (right == null) - return null; - - BinaryOperatorExpression boe = new BinaryOperatorExpression(left, op, right); - if (isChecked != null) - boe.AddAnnotation(isChecked.Value ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); - - switch (invocation.Arguments.Count) { - case 2: - return boe; - case 3: - Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(2)); - if (m.Success) - return boe.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>()); - else - return null; - case 4: - if (!trueOrFalse.IsMatch(invocation.Arguments.ElementAt(2))) - return null; - m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(3)); - if (m.Success) - return boe.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>()); - else - return null; - default: - return NotSupported(invocation); - } - } - #endregion - - #region Convert Assignment Operator - Expression ConvertAssignmentOperator(InvocationExpression invocation, AssignmentOperatorType op, bool? isChecked = null) - { - return NotSupported(invocation); - } - #endregion - - #region Convert Unary Operator - Expression ConvertUnaryOperator(InvocationExpression invocation, UnaryOperatorType op, bool? isChecked = null) - { - if (invocation.Arguments.Count < 1) - return NotSupported(invocation); - - Expression expr = Convert(invocation.Arguments.ElementAt(0)); - if (expr == null) - return null; - - UnaryOperatorExpression uoe = new UnaryOperatorExpression(op, expr); - if (isChecked != null) - uoe.AddAnnotation(isChecked.Value ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); - - switch (invocation.Arguments.Count) { - case 1: - return uoe; - case 2: - Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(1)); - if (m.Success) - return uoe.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>()); - else - return null; - default: - return NotSupported(invocation); - } - } - #endregion - - #region Convert Condition Operator - Expression ConvertCondition(InvocationExpression invocation) - { - if (invocation.Arguments.Count != 3) - return NotSupported(invocation); - - Expression condition = Convert(invocation.Arguments.ElementAt(0)); - Expression trueExpr = Convert(invocation.Arguments.ElementAt(1)); - Expression falseExpr = Convert(invocation.Arguments.ElementAt(2)); - if (condition != null && trueExpr != null && falseExpr != null) - return new ConditionalExpression(condition, trueExpr, falseExpr); - else - return null; - } - #endregion - - #region Convert New Object - static readonly Expression newObjectCtorPattern = new TypePattern(typeof(MethodBase)).ToType().Invoke - ( - "GetMethodFromHandle", - new LdTokenPattern("ctor").ToExpression().Member("MethodHandle"), - new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle")) - ).CastTo(new TypePattern(typeof(ConstructorInfo))); - - Expression ConvertNewObject(InvocationExpression invocation) - { - if (invocation.Arguments.Count < 1 || invocation.Arguments.Count > 3) - return NotSupported(invocation); - - Match m = newObjectCtorPattern.Match(invocation.Arguments.First()); - if (!m.Success) - return NotSupported(invocation); - - MethodReference ctor = m.Get<AstNode>("ctor").Single().Annotation<MethodReference>(); - if (ctor == null) - return null; - - AstType declaringTypeNode; - TypeReference declaringType; - if (m.Has("declaringType")) { - declaringTypeNode = m.Get<AstType>("declaringType").Single().Clone(); - declaringType = declaringTypeNode.Annotation<TypeReference>(); - } else { - declaringTypeNode = AstBuilder.ConvertType(ctor.DeclaringType); - declaringType = ctor.DeclaringType; - } - if (declaringTypeNode == null) - return null; - - ObjectCreateExpression oce = new ObjectCreateExpression(declaringTypeNode); - if (invocation.Arguments.Count >= 2) { - IList<Expression> arguments = ConvertExpressionsArray(invocation.Arguments.ElementAtOrDefault(1)); - if (arguments == null) - return null; - oce.Arguments.AddRange(arguments); - } - if (invocation.Arguments.Count >= 3 && declaringType.IsAnonymousType()) { - MethodDefinition resolvedCtor = ctor.Resolve(); - if (resolvedCtor == null || resolvedCtor.Parameters.Count != oce.Arguments.Count) - return null; - AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression(); - var arguments = oce.Arguments.ToArray(); - if (AstMethodBodyBuilder.CanInferAnonymousTypePropertyNamesFromArguments(arguments, resolvedCtor.Parameters)) { - oce.Arguments.MoveTo(atce.Initializers); - } else { - for (int i = 0; i < resolvedCtor.Parameters.Count; i++) { - atce.Initializers.Add( - new NamedExpression { - Name = resolvedCtor.Parameters[i].Name, - Expression = arguments[i].Detach() - }); - } - } - return atce; - } - - return oce; - } - #endregion - - #region Convert ListInit - static readonly Pattern elementInitArrayPattern = ArrayInitializationPattern( - typeof(System.Linq.Expressions.ElementInit), - new TypePattern(typeof(System.Linq.Expressions.Expression)).ToType().Invoke("ElementInit", new AnyNode("methodInfos"), new AnyNode("addArgumentsArrays")) - ); - - Expression ConvertListInit(InvocationExpression invocation) - { - if (invocation.Arguments.Count != 2) - return NotSupported(invocation); - ObjectCreateExpression oce = Convert(invocation.Arguments.ElementAt(0)) as ObjectCreateExpression; - if (oce == null) - return null; - Expression elementsArray = invocation.Arguments.ElementAt(1); - ArrayInitializerExpression initializer = ConvertElementInit(elementsArray); - if (initializer != null) { - oce.Initializer = initializer; - return oce; - } else { - return null; - } - } - - ArrayInitializerExpression ConvertElementInit(Expression elementsArray) - { - IList<Expression> elements = ConvertExpressionsArray(elementsArray); - if (elements != null) { - return new ArrayInitializerExpression(elements); - } - Match m = elementInitArrayPattern.Match(elementsArray); - if (!m.Success) - return null; - ArrayInitializerExpression result = new ArrayInitializerExpression(); - foreach (var elementInit in m.Get<Expression>("addArgumentsArrays")) { - IList<Expression> arguments = ConvertExpressionsArray(elementInit); - if (arguments == null) - return null; - result.Elements.Add(new ArrayInitializerExpression(arguments)); - } - return result; - } - #endregion - - #region Convert MemberInit - Expression ConvertMemberInit(InvocationExpression invocation) - { - if (invocation.Arguments.Count != 2) - return NotSupported(invocation); - ObjectCreateExpression oce = Convert(invocation.Arguments.ElementAt(0)) as ObjectCreateExpression; - if (oce == null) - return null; - Expression elementsArray = invocation.Arguments.ElementAt(1); - ArrayInitializerExpression bindings = ConvertMemberBindings(elementsArray); - if (bindings == null) - return null; - oce.Initializer = bindings; - return oce; - } - - static readonly Pattern memberBindingArrayPattern = ArrayInitializationPattern(typeof(System.Linq.Expressions.MemberBinding), new AnyNode("binding")); - static readonly INode expressionTypeReference = new TypeReferenceExpression(new TypePattern(typeof(System.Linq.Expressions.Expression))); - - ArrayInitializerExpression ConvertMemberBindings(Expression elementsArray) - { - Match m = memberBindingArrayPattern.Match(elementsArray); - if (!m.Success) - return null; - ArrayInitializerExpression result = new ArrayInitializerExpression(); - foreach (var binding in m.Get<Expression>("binding")) { - InvocationExpression bindingInvocation = binding as InvocationExpression; - if (bindingInvocation == null || bindingInvocation.Arguments.Count != 2) - return null; - MemberReferenceExpression bindingMRE = bindingInvocation.Target as MemberReferenceExpression; - if (bindingMRE == null || !expressionTypeReference.IsMatch(bindingMRE.Target)) - return null; - - Expression bindingTarget = bindingInvocation.Arguments.ElementAt(0); - Expression bindingValue = bindingInvocation.Arguments.ElementAt(1); - - string memberName; - Match m2 = getMethodFromHandlePattern.Match(bindingTarget); - if (m2.Success) { - MethodReference setter = m2.Get<AstNode>("method").Single().Annotation<MethodReference>(); - if (setter == null) - return null; - memberName = GetPropertyName(setter); - } else { - return null; - } - - Expression convertedValue; - switch (bindingMRE.MemberName) { - case "Bind": - convertedValue = Convert(bindingValue); - break; - case "MemberBind": - convertedValue = ConvertMemberBindings(bindingValue); - break; - case "ListBind": - convertedValue = ConvertElementInit(bindingValue); - break; - default: - return null; - } - if (convertedValue == null) - return null; - result.Elements.Add(new NamedExpression(memberName, convertedValue)); - } - return result; - } - #endregion - - #region Convert Cast - Expression ConvertCast(InvocationExpression invocation, bool isChecked) - { - if (invocation.Arguments.Count < 2) - return null; - Expression converted = Convert(invocation.Arguments.ElementAt(0)); - AstType type = ConvertTypeReference(invocation.Arguments.ElementAt(1)); - if (converted != null && type != null) { - CastExpression cast = converted.CastTo(type); - cast.AddAnnotation(isChecked ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); - switch (invocation.Arguments.Count) { - case 2: - return cast; - case 3: - Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(2)); - if (m.Success) - return cast.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>()); - else - return null; - } - } - return null; - } - #endregion - - #region ConvertExpressionsArray - static Pattern ArrayInitializationPattern(Type arrayElementType, INode elementPattern) - { - return new Choice { - new ArrayCreateExpression { - Type = new TypePattern(arrayElementType), - Arguments = { new PrimitiveExpression(0) } - }, - new ArrayCreateExpression { - Type = new TypePattern(arrayElementType), - AdditionalArraySpecifiers = { new ArraySpecifier() }, - Initializer = new ArrayInitializerExpression { - Elements = { new Repeat(elementPattern) } - } - } - }; - } - - static readonly Pattern expressionArrayPattern = ArrayInitializationPattern(typeof(System.Linq.Expressions.Expression), new AnyNode("elements")); - - IList<Expression> ConvertExpressionsArray(Expression arrayExpression) - { - Match m = expressionArrayPattern.Match(arrayExpression); - if (m.Success) { - List<Expression> result = new List<Expression>(); - foreach (Expression expr in m.Get<Expression>("elements")) { - Expression converted = Convert(expr); - if (converted == null) - return null; - result.Add(converted); - } - return result; - } - return null; - } - #endregion - - #region Convert TypeAs/TypeIs - static readonly TypeOfPattern typeOfPattern = new TypeOfPattern("type"); - - AstType ConvertTypeReference(Expression typeOfExpression) - { - Match m = typeOfPattern.Match(typeOfExpression); - if (m.Success) - return m.Get<AstType>("type").Single().Clone(); - else - return null; - } - - Expression ConvertTypeAs(InvocationExpression invocation) - { - if (invocation.Arguments.Count != 2) - return null; - Expression converted = Convert(invocation.Arguments.ElementAt(0)); - AstType type = ConvertTypeReference(invocation.Arguments.ElementAt(1)); - if (converted != null && type != null) - return new AsExpression(converted, type); - return null; - } - - Expression ConvertTypeIs(InvocationExpression invocation) - { - if (invocation.Arguments.Count != 2) - return null; - Expression converted = Convert(invocation.Arguments.ElementAt(0)); - AstType type = ConvertTypeReference(invocation.Arguments.ElementAt(1)); - if (converted != null && type != null) - return new IsExpression { Expression = converted, Type = type }; - return null; - } - #endregion - - #region Convert Array - Expression ConvertArrayIndex(InvocationExpression invocation) - { - if (invocation.Arguments.Count != 2) - return NotSupported(invocation); - - Expression targetConverted = Convert(invocation.Arguments.First()); - if (targetConverted == null) - return null; - - Expression index = invocation.Arguments.ElementAt(1); - Expression indexConverted = Convert(index); - if (indexConverted != null) { - return new IndexerExpression(targetConverted, indexConverted); - } - IList<Expression> indexesConverted = ConvertExpressionsArray(index); - if (indexesConverted != null) { - return new IndexerExpression(targetConverted, indexesConverted); - } - return null; - } - - Expression ConvertArrayLength(InvocationExpression invocation) - { - if (invocation.Arguments.Count != 1) - return NotSupported(invocation); - - Expression targetConverted = Convert(invocation.Arguments.Single()); - if (targetConverted != null) - return targetConverted.Member("Length"); - else - return null; - } - - Expression ConvertNewArrayInit(InvocationExpression invocation) - { - if (invocation.Arguments.Count != 2) - return NotSupported(invocation); - - AstType elementType = ConvertTypeReference(invocation.Arguments.ElementAt(0)); - IList<Expression> elements = ConvertExpressionsArray(invocation.Arguments.ElementAt(1)); - if (elementType != null && elements != null) { - if (ContainsAnonymousType(elementType)) { - elementType = null; - } - return new ArrayCreateExpression { - Type = elementType, - AdditionalArraySpecifiers = { new ArraySpecifier() }, - Initializer = new ArrayInitializerExpression(elements) - }; - } - return null; - } - - Expression ConvertNewArrayBounds(InvocationExpression invocation) - { - if (invocation.Arguments.Count != 2) - return NotSupported(invocation); - - AstType elementType = ConvertTypeReference(invocation.Arguments.ElementAt(0)); - IList<Expression> arguments = ConvertExpressionsArray(invocation.Arguments.ElementAt(1)); - if (elementType != null && arguments != null) { - if (ContainsAnonymousType(elementType)) { - elementType = null; - } - ArrayCreateExpression ace = new ArrayCreateExpression(); - ace.Type = elementType; - ace.Arguments.AddRange(arguments); - return ace; - } - return null; - } - - bool ContainsAnonymousType(AstType type) - { - foreach (AstType t in type.DescendantsAndSelf.OfType<AstType>()) { - TypeReference tr = t.Annotation<TypeReference>(); - if (tr != null && tr.IsAnonymousType()) - return true; - } - return false; - } - #endregion - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/FlattenSwitchBlocks.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/FlattenSwitchBlocks.cs deleted file mode 100644 index d7ce11e837..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/FlattenSwitchBlocks.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using ICSharpCode.NRefactory.CSharp; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - class FlattenSwitchBlocks : IAstTransform - { - public void Run(AstNode compilationUnit) - { - foreach (var switchSection in compilationUnit.Descendants.OfType<SwitchSection>()) - { - if (switchSection.Statements.Count != 1) - continue; - - var blockStatement = switchSection.Statements.First() as BlockStatement; - if (blockStatement == null || blockStatement.Statements.Any(st => st is VariableDeclarationStatement)) - continue; - - blockStatement.Remove(); - blockStatement.Statements.MoveTo(switchSection.Statements); - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceExtensionMethods.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceExtensionMethods.cs deleted file mode 100644 index 9f05285ecb..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceExtensionMethods.cs +++ /dev/null @@ -1,66 +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.Linq; -using ICSharpCode.NRefactory.CSharp; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - /// <summary> - /// Converts extension method calls into infix syntax. - /// </summary> - public class IntroduceExtensionMethods : IAstTransform - { - readonly DecompilerContext context; - - public IntroduceExtensionMethods(DecompilerContext context) - { - this.context = context; - } - - public void Run(AstNode compilationUnit) - { - foreach (InvocationExpression invocation in compilationUnit.Descendants.OfType<InvocationExpression>()) { - MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression; - MethodReference methodReference = invocation.Annotation<MethodReference>(); - if (mre != null && mre.Target is TypeReferenceExpression && methodReference != null && invocation.Arguments.Any()) { - MethodDefinition d = methodReference.Resolve(); - if (d != null) { - foreach (var ca in d.CustomAttributes) { - if (ca.AttributeType.Name == "ExtensionAttribute" && ca.AttributeType.Namespace == "System.Runtime.CompilerServices") { - var firstArgument = invocation.Arguments.First(); - if (firstArgument is NullReferenceExpression) - firstArgument = firstArgument.ReplaceWith(expr => expr.CastTo(AstBuilder.ConvertType(d.Parameters.First().ParameterType))); - else - mre.Target = firstArgument.Detach(); - if (invocation.Arguments.Any()) { - // HACK: removing type arguments should be done indepently from whether a method is an extension method, - // just by testing whether the arguments can be inferred - mre.TypeArguments.Clear(); - } - break; - } - } - } - } - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceQueryExpressions.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceQueryExpressions.cs deleted file mode 100644 index 0da56fe911..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceQueryExpressions.cs +++ /dev/null @@ -1,295 +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.Diagnostics; -using System.Linq; -using ICSharpCode.NRefactory.CSharp; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - /// <summary> - /// Decompiles query expressions. - /// Based on C# 4.0 spec, §7.16.2 Query expression translation - /// </summary> - public class IntroduceQueryExpressions : IAstTransform - { - readonly DecompilerContext context; - - public IntroduceQueryExpressions(DecompilerContext context) - { - this.context = context; - } - - public void Run(AstNode compilationUnit) - { - if (!context.Settings.QueryExpressions) - return; - DecompileQueries(compilationUnit); - // After all queries were decompiled, detect degenerate queries (queries not property terminated with 'select' or 'group') - // and fix them, either by adding a degenerate select, or by combining them with another query. - foreach (QueryExpression query in compilationUnit.Descendants.OfType<QueryExpression>()) { - QueryFromClause fromClause = (QueryFromClause)query.Clauses.First(); - if (IsDegenerateQuery(query)) { - // introduce select for degenerate query - query.Clauses.Add(new QuerySelectClause { Expression = new IdentifierExpression(fromClause.Identifier) }); - } - // See if the data source of this query is a degenerate query, - // and combine the queries if possible. - QueryExpression innerQuery = fromClause.Expression as QueryExpression; - while (IsDegenerateQuery(innerQuery)) { - QueryFromClause innerFromClause = (QueryFromClause)innerQuery.Clauses.First(); - if (fromClause.Identifier != innerFromClause.Identifier) - break; - // Replace the fromClause with all clauses from the inner query - fromClause.Remove(); - QueryClause insertionPos = null; - foreach (var clause in innerQuery.Clauses) { - query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach()); - } - fromClause = innerFromClause; - innerQuery = fromClause.Expression as QueryExpression; - } - } - } - - bool IsDegenerateQuery(QueryExpression query) - { - if (query == null) - return false; - var lastClause = query.Clauses.LastOrDefault(); - return !(lastClause is QuerySelectClause || lastClause is QueryGroupClause); - } - - void DecompileQueries(AstNode node) - { - QueryExpression query = DecompileQuery(node as InvocationExpression); - if (query != null) - node.ReplaceWith(query); - for (AstNode child = (query ?? node).FirstChild; child != null; child = child.NextSibling) { - DecompileQueries(child); - } - } - - QueryExpression DecompileQuery(InvocationExpression invocation) - { - if (invocation == null) - return null; - MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression; - if (mre == null) - return null; - switch (mre.MemberName) { - case "Select": - { - if (invocation.Arguments.Count != 1) - return null; - string parameterName; - Expression body; - if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body)) { - QueryExpression query = new QueryExpression(); - query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = mre.Target.Detach() }); - query.Clauses.Add(new QuerySelectClause { Expression = body.Detach() }); - return query; - } - return null; - } - case "GroupBy": - { - if (invocation.Arguments.Count == 2) { - string parameterName1, parameterName2; - Expression keySelector, elementSelector; - if (MatchSimpleLambda(invocation.Arguments.ElementAt(0), out parameterName1, out keySelector) - && MatchSimpleLambda(invocation.Arguments.ElementAt(1), out parameterName2, out elementSelector) - && parameterName1 == parameterName2) - { - QueryExpression query = new QueryExpression(); - query.Clauses.Add(new QueryFromClause { Identifier = parameterName1, Expression = mre.Target.Detach() }); - query.Clauses.Add(new QueryGroupClause { Projection = elementSelector.Detach(), Key = keySelector.Detach() }); - return query; - } - } else if (invocation.Arguments.Count == 1) { - string parameterName; - Expression keySelector; - if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out keySelector)) { - QueryExpression query = new QueryExpression(); - query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = mre.Target.Detach() }); - query.Clauses.Add(new QueryGroupClause { Projection = new IdentifierExpression(parameterName), Key = keySelector.Detach() }); - return query; - } - } - return null; - } - case "SelectMany": - { - if (invocation.Arguments.Count != 2) - return null; - string parameterName; - Expression collectionSelector; - if (!MatchSimpleLambda(invocation.Arguments.ElementAt(0), out parameterName, out collectionSelector)) - return null; - LambdaExpression lambda = invocation.Arguments.ElementAt(1) as LambdaExpression; - if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression) { - ParameterDeclaration p1 = lambda.Parameters.ElementAt(0); - ParameterDeclaration p2 = lambda.Parameters.ElementAt(1); - if (p1.Name == parameterName) { - QueryExpression query = new QueryExpression(); - query.Clauses.Add(new QueryFromClause { Identifier = p1.Name, Expression = mre.Target.Detach() }); - query.Clauses.Add(new QueryFromClause { Identifier = p2.Name, Expression = collectionSelector.Detach() }); - query.Clauses.Add(new QuerySelectClause { Expression = ((Expression)lambda.Body).Detach() }); - return query; - } - } - return null; - } - case "Where": - { - if (invocation.Arguments.Count != 1) - return null; - string parameterName; - Expression body; - if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body)) { - QueryExpression query = new QueryExpression(); - query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = mre.Target.Detach() }); - query.Clauses.Add(new QueryWhereClause { Condition = body.Detach() }); - return query; - } - return null; - } - case "OrderBy": - case "OrderByDescending": - case "ThenBy": - case "ThenByDescending": - { - if (invocation.Arguments.Count != 1) - return null; - string parameterName; - Expression orderExpression; - if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out orderExpression)) { - if (ValidateThenByChain(invocation, parameterName)) { - QueryOrderClause orderClause = new QueryOrderClause(); - InvocationExpression tmp = invocation; - while (mre.MemberName == "ThenBy" || mre.MemberName == "ThenByDescending") { - // insert new ordering at beginning - orderClause.Orderings.InsertAfter( - null, new QueryOrdering { - Expression = orderExpression.Detach(), - Direction = (mre.MemberName == "ThenBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending) - }); - - tmp = (InvocationExpression)mre.Target; - mre = (MemberReferenceExpression)tmp.Target; - MatchSimpleLambda(tmp.Arguments.Single(), out parameterName, out orderExpression); - } - // insert new ordering at beginning - orderClause.Orderings.InsertAfter( - null, new QueryOrdering { - Expression = orderExpression.Detach(), - Direction = (mre.MemberName == "OrderBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending) - }); - - QueryExpression query = new QueryExpression(); - query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = mre.Target.Detach() }); - query.Clauses.Add(orderClause); - return query; - } - } - return null; - } - case "Join": - case "GroupJoin": - { - if (invocation.Arguments.Count != 4) - return null; - Expression source1 = mre.Target; - Expression source2 = invocation.Arguments.ElementAt(0); - string elementName1, elementName2; - Expression key1, key2; - if (!MatchSimpleLambda(invocation.Arguments.ElementAt(1), out elementName1, out key1)) - return null; - if (!MatchSimpleLambda(invocation.Arguments.ElementAt(2), out elementName2, out key2)) - return null; - LambdaExpression lambda = invocation.Arguments.ElementAt(3) as LambdaExpression; - if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression) { - ParameterDeclaration p1 = lambda.Parameters.ElementAt(0); - ParameterDeclaration p2 = lambda.Parameters.ElementAt(1); - if (p1.Name == elementName1 && (p2.Name == elementName2 || mre.MemberName == "GroupJoin")) { - QueryExpression query = new QueryExpression(); - query.Clauses.Add(new QueryFromClause { Identifier = elementName1, Expression = source1.Detach() }); - QueryJoinClause joinClause = new QueryJoinClause(); - joinClause.JoinIdentifier = elementName2; // join elementName2 - joinClause.InExpression = source2.Detach(); // in source2 - joinClause.OnExpression = key1.Detach(); // on key1 - joinClause.EqualsExpression = key2.Detach(); // equals key2 - if (mre.MemberName == "GroupJoin") { - joinClause.IntoIdentifier = p2.Name; // into p2.Name - } - query.Clauses.Add(joinClause); - query.Clauses.Add(new QuerySelectClause { Expression = ((Expression)lambda.Body).Detach() }); - return query; - } - } - return null; - } - default: - return null; - } - } - - /// <summary> - /// Ensure that all ThenBy's are correct, and that the list of ThenBy's is terminated by an 'OrderBy' invocation. - /// </summary> - bool ValidateThenByChain(InvocationExpression invocation, string expectedParameterName) - { - if (invocation == null || invocation.Arguments.Count != 1) - return false; - MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression; - if (mre == null) - return false; - string parameterName; - Expression body; - if (!MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body)) - return false; - if (parameterName != expectedParameterName) - return false; - - if (mre.MemberName == "OrderBy" || mre.MemberName == "OrderByDescending") - return true; - else if (mre.MemberName == "ThenBy" || mre.MemberName == "ThenByDescending") - return ValidateThenByChain(mre.Target as InvocationExpression, expectedParameterName); - else - return false; - } - - /// <summary>Matches simple lambdas of the form "a => b"</summary> - bool MatchSimpleLambda(Expression expr, out string parameterName, out Expression body) - { - LambdaExpression lambda = expr as LambdaExpression; - if (lambda != null && lambda.Parameters.Count == 1 && lambda.Body is Expression) { - ParameterDeclaration p = lambda.Parameters.Single(); - if (p.ParameterModifier == ParameterModifier.None) { - parameterName = p.Name; - body = (Expression)lambda.Body; - return true; - } - } - parameterName = null; - body = null; - return false; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs deleted file mode 100644 index a9e72564f3..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs +++ /dev/null @@ -1,110 +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 ICSharpCode.NRefactory.CSharp; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - public class IntroduceUnsafeModifier : DepthFirstAstVisitor<object, bool>, IAstTransform - { - public static readonly object PointerArithmeticAnnotation = new PointerArithmetic(); - - sealed class PointerArithmetic {} - - public void Run(AstNode compilationUnit) - { - compilationUnit.AcceptVisitor(this, null); - } - - protected override bool VisitChildren(AstNode node, object data) - { - bool result = false; - AstNode next; - for (AstNode child = node.FirstChild; child != null; child = next) { - // Store next to allow the loop to continue - // if the visitor removes/replaces child. - next = child.NextSibling; - result |= child.AcceptVisitor(this, data); - } - if (result && node is EntityDeclaration && !(node is Accessor)) { - ((EntityDeclaration)node).Modifiers |= Modifiers.Unsafe; - return false; - } - return result; - } - - public override bool VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, object data) - { - base.VisitPointerReferenceExpression(pointerReferenceExpression, data); - return true; - } - - public override bool VisitComposedType(ComposedType composedType, object data) - { - if (composedType.PointerRank > 0) - return true; - else - return base.VisitComposedType(composedType, data); - } - - public override bool VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, object data) - { - bool result = base.VisitUnaryOperatorExpression(unaryOperatorExpression, data); - if (unaryOperatorExpression.Operator == UnaryOperatorType.Dereference) { - BinaryOperatorExpression bop = unaryOperatorExpression.Expression as BinaryOperatorExpression; - if (bop != null && bop.Operator == BinaryOperatorType.Add && bop.Annotation<PointerArithmetic>() != null) { - // transform "*(ptr + int)" to "ptr[int]" - IndexerExpression indexer = new IndexerExpression(); - indexer.Target = bop.Left.Detach(); - indexer.Arguments.Add(bop.Right.Detach()); - indexer.CopyAnnotationsFrom(unaryOperatorExpression); - indexer.CopyAnnotationsFrom(bop); - unaryOperatorExpression.ReplaceWith(indexer); - } - return true; - } else if (unaryOperatorExpression.Operator == UnaryOperatorType.AddressOf) { - return true; - } else { - return result; - } - } - - public override bool VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, object data) - { - bool result = base.VisitMemberReferenceExpression(memberReferenceExpression, data); - UnaryOperatorExpression uoe = memberReferenceExpression.Target as UnaryOperatorExpression; - if (uoe != null && uoe.Operator == UnaryOperatorType.Dereference) { - PointerReferenceExpression pre = new PointerReferenceExpression(); - pre.Target = uoe.Expression.Detach(); - pre.MemberName = memberReferenceExpression.MemberName; - memberReferenceExpression.TypeArguments.MoveTo(pre.TypeArguments); - pre.CopyAnnotationsFrom(uoe); - pre.CopyAnnotationsFrom(memberReferenceExpression); - memberReferenceExpression.ReplaceWith(pre); - } - return result; - } - - public override bool VisitStackAllocExpression(StackAllocExpression stackAllocExpression, object data) - { - base.VisitStackAllocExpression(stackAllocExpression, data); - return true; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs deleted file mode 100644 index 4182eec8f2..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs +++ /dev/null @@ -1,359 +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.Collections.Generic; -using System.Linq; -using ICSharpCode.NRefactory.CSharp; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - /// <summary> - /// Introduces using declarations. - /// </summary> - public class IntroduceUsingDeclarations : IAstTransform - { - DecompilerContext context; - - public IntroduceUsingDeclarations(DecompilerContext context) - { - this.context = context; - } - - public void Run(AstNode compilationUnit) - { - // First determine all the namespaces that need to be imported: - compilationUnit.AcceptVisitor(new FindRequiredImports(this), null); - - importedNamespaces.Add("System"); // always import System, even when not necessary - - if (context.Settings.UsingDeclarations) { - // Now add using declarations for those namespaces: - foreach (string ns in importedNamespaces.OrderByDescending(n => n)) { - // we go backwards (OrderByDescending) through the list of namespaces because we insert them backwards - // (always inserting at the start of the list) - string[] parts = ns.Split('.'); - AstType nsType = new SimpleType(parts[0]); - for (int i = 1; i < parts.Length; i++) { - nsType = new MemberType { Target = nsType, MemberName = parts[i] }; - } - compilationUnit.InsertChildAfter(null, new UsingDeclaration { Import = nsType }, SyntaxTree.MemberRole); - } - } - - if (!context.Settings.FullyQualifyAmbiguousTypeNames) - return; - - FindAmbiguousTypeNames(context.CurrentModule, internalsVisible: true); - foreach (AssemblyNameReference r in context.CurrentModule.AssemblyReferences) { - AssemblyDefinition d = context.CurrentModule.AssemblyResolver.Resolve(r); - if (d != null) - FindAmbiguousTypeNames(d.MainModule, internalsVisible: false); - } - - // verify that the SimpleTypes refer to the correct type (no ambiguities) - compilationUnit.AcceptVisitor(new FullyQualifyAmbiguousTypeNamesVisitor(this), null); - } - - readonly HashSet<string> declaredNamespaces = new HashSet<string>() { string.Empty }; - readonly HashSet<string> importedNamespaces = new HashSet<string>(); - - // Note that we store type names with `n suffix, so we automatically disambiguate based on number of type parameters. - readonly HashSet<string> availableTypeNames = new HashSet<string>(); - readonly HashSet<string> ambiguousTypeNames = new HashSet<string>(); - - sealed class FindRequiredImports : DepthFirstAstVisitor<object, object> - { - readonly IntroduceUsingDeclarations transform; - string currentNamespace; - - public FindRequiredImports(IntroduceUsingDeclarations transform) - { - this.transform = transform; - this.currentNamespace = transform.context.CurrentType != null ? transform.context.CurrentType.Namespace : string.Empty; - } - - bool IsParentOfCurrentNamespace(string ns) - { - if (ns.Length == 0) - return true; - if (currentNamespace.StartsWith(ns, StringComparison.Ordinal)) { - if (currentNamespace.Length == ns.Length) - return true; - if (currentNamespace[ns.Length] == '.') - return true; - } - return false; - } - - public override object VisitSimpleType(SimpleType simpleType, object data) - { - TypeReference tr = simpleType.Annotation<TypeReference>(); - if (tr != null && !IsParentOfCurrentNamespace(tr.Namespace)) { - transform.importedNamespaces.Add(tr.Namespace); - } - return base.VisitSimpleType(simpleType, data); // also visit type arguments - } - - public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data) - { - string oldNamespace = currentNamespace; - foreach (string ident in namespaceDeclaration.Identifiers) { - currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident); - transform.declaredNamespaces.Add(currentNamespace); - } - base.VisitNamespaceDeclaration(namespaceDeclaration, data); - currentNamespace = oldNamespace; - return null; - } - } - - void FindAmbiguousTypeNames(ModuleDefinition module, bool internalsVisible) - { - foreach (TypeDefinition type in module.Types) { - if (internalsVisible || type.IsPublic) { - if (importedNamespaces.Contains(type.Namespace) || declaredNamespaces.Contains(type.Namespace)) { - if (!availableTypeNames.Add(type.Name)) - ambiguousTypeNames.Add(type.Name); - } - } - } - } - - sealed class FullyQualifyAmbiguousTypeNamesVisitor : DepthFirstAstVisitor<object, object> - { - readonly IntroduceUsingDeclarations transform; - string currentNamespace; - HashSet<string> currentMemberTypes; - Dictionary<string, MemberReference> currentMembers; - bool isWithinTypeReferenceExpression; - - public FullyQualifyAmbiguousTypeNamesVisitor(IntroduceUsingDeclarations transform) - { - this.transform = transform; - this.currentNamespace = transform.context.CurrentType != null ? transform.context.CurrentType.Namespace : string.Empty; - } - - public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data) - { - string oldNamespace = currentNamespace; - foreach (string ident in namespaceDeclaration.Identifiers) { - currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident); - } - base.VisitNamespaceDeclaration(namespaceDeclaration, data); - currentNamespace = oldNamespace; - return null; - } - - public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) - { - HashSet<string> oldMemberTypes = currentMemberTypes; - currentMemberTypes = currentMemberTypes != null ? new HashSet<string>(currentMemberTypes) : new HashSet<string>(); - - Dictionary<string, MemberReference> oldMembers = currentMembers; - currentMembers = new Dictionary<string, MemberReference>(); - - TypeDefinition typeDef = typeDeclaration.Annotation<TypeDefinition>(); - bool privateMembersVisible = true; - ModuleDefinition internalMembersVisibleInModule = typeDef.Module; - while (typeDef != null) { - foreach (GenericParameter gp in typeDef.GenericParameters) { - currentMemberTypes.Add(gp.Name); - } - foreach (TypeDefinition t in typeDef.NestedTypes) { - if (privateMembersVisible || IsVisible(t, internalMembersVisibleInModule)) - currentMemberTypes.Add(t.Name.Substring(t.Name.LastIndexOf('+') + 1)); - } - - foreach (MethodDefinition method in typeDef.Methods) { - if (privateMembersVisible || IsVisible(method, internalMembersVisibleInModule)) - AddCurrentMember(method); - } - foreach (PropertyDefinition property in typeDef.Properties) { - if (privateMembersVisible || IsVisible(property.GetMethod, internalMembersVisibleInModule) || IsVisible(property.SetMethod, internalMembersVisibleInModule)) - AddCurrentMember(property); - } - foreach (EventDefinition ev in typeDef.Events) { - if (privateMembersVisible || IsVisible(ev.AddMethod, internalMembersVisibleInModule) || IsVisible(ev.RemoveMethod, internalMembersVisibleInModule)) - AddCurrentMember(ev); - } - foreach (FieldDefinition f in typeDef.Fields) { - if (privateMembersVisible || IsVisible(f, internalMembersVisibleInModule)) - AddCurrentMember(f); - } - // repeat with base class: - if (typeDef.BaseType != null) - typeDef = typeDef.BaseType.Resolve(); - else - typeDef = null; - privateMembersVisible = false; - } - - // Now add current members from outer classes: - if (oldMembers != null) { - foreach (var pair in oldMembers) { - // add members from outer classes only if the inner class doesn't define the member - if (!currentMembers.ContainsKey(pair.Key)) - currentMembers.Add(pair.Key, pair.Value); - } - } - - base.VisitTypeDeclaration(typeDeclaration, data); - currentMembers = oldMembers; - return null; - } - - void AddCurrentMember(MemberReference m) - { - MemberReference existingMember; - if (currentMembers.TryGetValue(m.Name, out existingMember)) { - // We keep the existing member assignment if it was from another class (=from a derived class), - // because members in derived classes have precedence over members in base classes. - if (existingMember != null && existingMember.DeclaringType == m.DeclaringType) { - // Use null as value to signalize multiple members with the same name - currentMembers[m.Name] = null; - } - } else { - currentMembers.Add(m.Name, m); - } - } - - bool IsVisible(MethodDefinition m, ModuleDefinition internalMembersVisibleInModule) - { - if (m == null) - return false; - switch (m.Attributes & MethodAttributes.MemberAccessMask) { - case MethodAttributes.FamANDAssem: - case MethodAttributes.Assembly: - return m.Module == internalMembersVisibleInModule; - case MethodAttributes.Family: - case MethodAttributes.FamORAssem: - case MethodAttributes.Public: - return true; - default: - return false; - } - } - - bool IsVisible(FieldDefinition f, ModuleDefinition internalMembersVisibleInModule) - { - if (f == null) - return false; - switch (f.Attributes & FieldAttributes.FieldAccessMask) { - case FieldAttributes.FamANDAssem: - case FieldAttributes.Assembly: - return f.Module == internalMembersVisibleInModule; - case FieldAttributes.Family: - case FieldAttributes.FamORAssem: - case FieldAttributes.Public: - return true; - default: - return false; - } - } - - bool IsVisible(TypeDefinition t, ModuleDefinition internalMembersVisibleInModule) - { - if (t == null) - return false; - switch (t.Attributes & TypeAttributes.VisibilityMask) { - case TypeAttributes.NotPublic: - case TypeAttributes.NestedAssembly: - case TypeAttributes.NestedFamANDAssem: - return t.Module == internalMembersVisibleInModule; - case TypeAttributes.NestedFamily: - case TypeAttributes.NestedFamORAssem: - case TypeAttributes.NestedPublic: - case TypeAttributes.Public: - return true; - default: - return false; - } - } - - public override object VisitSimpleType(SimpleType simpleType, object data) - { - // Handle type arguments first, so that the fixed-up type arguments get moved over to the MemberType, - // if we're also creating one here. - base.VisitSimpleType(simpleType, data); - TypeReference tr = simpleType.Annotation<TypeReference>(); - // Fully qualify any ambiguous type names. - if (tr != null && IsAmbiguous(tr.Namespace, tr.Name)) { - AstType ns; - if (string.IsNullOrEmpty(tr.Namespace)) { - ns = new SimpleType("global"); - } else { - string[] parts = tr.Namespace.Split('.'); - if (IsAmbiguous(string.Empty, parts[0])) { - // conflict between namespace and type name/member name - ns = new MemberType { Target = new SimpleType("global"), IsDoubleColon = true, MemberName = parts[0] }; - } else { - ns = new SimpleType(parts[0]); - } - for (int i = 1; i < parts.Length; i++) { - ns = new MemberType { Target = ns, MemberName = parts[i] }; - } - } - MemberType mt = new MemberType(); - mt.Target = ns; - mt.IsDoubleColon = string.IsNullOrEmpty(tr.Namespace); - mt.MemberName = simpleType.Identifier; - mt.CopyAnnotationsFrom(simpleType); - simpleType.TypeArguments.MoveTo(mt.TypeArguments); - simpleType.ReplaceWith(mt); - } - return null; - } - - public override object VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, object data) - { - isWithinTypeReferenceExpression = true; - base.VisitTypeReferenceExpression(typeReferenceExpression, data); - isWithinTypeReferenceExpression = false; - return null; - } - - bool IsAmbiguous(string ns, string name) - { - // If the type name conflicts with an inner class/type parameter, we need to fully-qualify it: - if (currentMemberTypes != null && currentMemberTypes.Contains(name)) - return true; - // If the type name conflicts with a field/property etc. on the current class, we need to fully-qualify it, - // if we're inside an expression. - if (isWithinTypeReferenceExpression && currentMembers != null) { - MemberReference mr; - if (currentMembers.TryGetValue(name, out mr)) { - // However, in the special case where the member is a field or property with the same type - // as is requested, then we can use the short name (if it's not otherwise ambiguous) - PropertyDefinition prop = mr as PropertyDefinition; - FieldDefinition field = mr as FieldDefinition; - if (!(prop != null && prop.PropertyType.Namespace == ns && prop.PropertyType.Name == name) - && !(field != null && field.FieldType.Namespace == ns && field.FieldType.Name == name)) - return true; - } - } - // If the type is defined in the current namespace, - // then we can use the short name even if we imported type with same name from another namespace. - if (ns == currentNamespace && !string.IsNullOrEmpty(ns)) - return false; - return transform.ambiguousTypeNames.Contains(name); - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs deleted file mode 100644 index bff2cc6b95..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ /dev/null @@ -1,1123 +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.Collections.Generic; -using System.Diagnostics; -using System.Linq; - -using ICSharpCode.Decompiler.ILAst; -using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.CSharp.Analysis; -using ICSharpCode.NRefactory.PatternMatching; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - /// <summary> - /// Finds the expanded form of using statements using pattern matching and replaces it with a UsingStatement. - /// </summary> - public sealed class PatternStatementTransform : ContextTrackingVisitor<AstNode>, IAstTransform - { - public PatternStatementTransform(DecompilerContext context) : base(context) - { - } - - #region Visitor Overrides - protected override AstNode VisitChildren(AstNode node, object data) - { - // Go through the children, and keep visiting a node as long as it changes. - // Because some transforms delete/replace nodes before and after the node being transformed, we rely - // on the transform's return value to know where we need to keep iterating. - for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { - AstNode oldChild; - do { - oldChild = child; - child = child.AcceptVisitor(this, data); - Debug.Assert(child != null && child.Parent == node); - } while (child != oldChild); - } - return node; - } - - public override AstNode VisitExpressionStatement(ExpressionStatement expressionStatement, object data) - { - AstNode result; - if (context.Settings.UsingStatement) { - result = TransformUsings(expressionStatement); - if (result != null) - return result; - result = TransformNonGenericForEach(expressionStatement); - if (result != null) - return result; - } - result = TransformFor(expressionStatement); - if (result != null) - return result; - if (context.Settings.LockStatement) { - result = TransformLock(expressionStatement); - if (result != null) - return result; - } - return base.VisitExpressionStatement(expressionStatement, data); - } - - public override AstNode VisitUsingStatement(UsingStatement usingStatement, object data) - { - if (context.Settings.ForEachStatement) { - AstNode result = TransformForeach(usingStatement); - if (result != null) - return result; - } - return base.VisitUsingStatement(usingStatement, data); - } - - public override AstNode VisitWhileStatement(WhileStatement whileStatement, object data) - { - return TransformDoWhile(whileStatement) ?? base.VisitWhileStatement(whileStatement, data); - } - - public override AstNode VisitIfElseStatement(IfElseStatement ifElseStatement, object data) - { - if (context.Settings.SwitchStatementOnString) { - AstNode result = TransformSwitchOnString(ifElseStatement); - if (result != null) - return result; - } - AstNode simplifiedIfElse = SimplifyCascadingIfElseStatements(ifElseStatement); - if (simplifiedIfElse != null) - return simplifiedIfElse; - return base.VisitIfElseStatement(ifElseStatement, data); - } - - public override AstNode VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, object data) - { - if (context.Settings.AutomaticProperties) { - AstNode result = TransformAutomaticProperties(propertyDeclaration); - if (result != null) - return result; - } - return base.VisitPropertyDeclaration(propertyDeclaration, data); - } - - public override AstNode VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration, object data) - { - // first apply transforms to the accessor bodies - base.VisitCustomEventDeclaration(eventDeclaration, data); - if (context.Settings.AutomaticEvents) { - AstNode result = TransformAutomaticEvents(eventDeclaration); - if (result != null) - return result; - } - return eventDeclaration; - } - - public override AstNode VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) - { - return TransformDestructor(methodDeclaration) ?? base.VisitMethodDeclaration(methodDeclaration, data); - } - - public override AstNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement, object data) - { - return TransformTryCatchFinally(tryCatchStatement) ?? base.VisitTryCatchStatement(tryCatchStatement, data); - } - #endregion - - /// <summary> - /// $variable = $initializer; - /// </summary> - static readonly AstNode variableAssignPattern = new ExpressionStatement( - new AssignmentExpression( - new NamedNode("variable", new IdentifierExpression(Pattern.AnyString)), - new AnyNode("initializer") - )); - - #region using - static Expression InvokeDispose(Expression identifier) - { - return new Choice { - identifier.Invoke("Dispose"), - identifier.Clone().CastTo(new TypePattern(typeof(IDisposable))).Invoke("Dispose") - }; - } - - static readonly AstNode usingTryCatchPattern = new TryCatchStatement { - TryBlock = new AnyNode(), - FinallyBlock = new BlockStatement { - new Choice { - { "valueType", - new ExpressionStatement(InvokeDispose(new NamedNode("ident", new IdentifierExpression(Pattern.AnyString)))) - }, - { "referenceType", - new IfElseStatement { - Condition = new BinaryOperatorExpression( - new NamedNode("ident", new IdentifierExpression(Pattern.AnyString)), - BinaryOperatorType.InEquality, - new NullReferenceExpression() - ), - TrueStatement = new BlockStatement { - new ExpressionStatement(InvokeDispose(new Backreference("ident"))) - } - } - } - }.ToStatement() - } - }; - - public UsingStatement TransformUsings(ExpressionStatement node) - { - Match m1 = variableAssignPattern.Match(node); - if (!m1.Success) return null; - TryCatchStatement tryCatch = node.NextSibling as TryCatchStatement; - Match m2 = usingTryCatchPattern.Match(tryCatch); - if (!m2.Success) return null; - string variableName = m1.Get<IdentifierExpression>("variable").Single().Identifier; - if (variableName != m2.Get<IdentifierExpression>("ident").Single().Identifier) - return null; - if (m2.Has("valueType")) { - // if there's no if(x!=null), then it must be a value type - ILVariable v = m1.Get<AstNode>("variable").Single().Annotation<ILVariable>(); - if (v == null || v.Type == null || !v.Type.IsValueType) - return null; - } - - // There are two variants of the using statement: - // "using (var a = init)" and "using (expr)". - // The former declares a read-only variable 'a', and the latter declares an unnamed read-only variable - // to store the original value of 'expr'. - // This means that in order to introduce a using statement, in both cases we need to detect a read-only - // variable that is used only within that block. - - if (HasAssignment(tryCatch, variableName)) - return null; - - VariableDeclarationStatement varDecl = FindVariableDeclaration(node, variableName); - if (varDecl == null || !(varDecl.Parent is BlockStatement)) - return null; - - // Validate that the variable is not used after the using statement: - if (!IsVariableValueUnused(varDecl, tryCatch)) - return null; - - node.Remove(); - - UsingStatement usingStatement = new UsingStatement(); - usingStatement.EmbeddedStatement = tryCatch.TryBlock.Detach(); - tryCatch.ReplaceWith(usingStatement); - - // If possible, we'll eliminate the variable completely: - if (usingStatement.EmbeddedStatement.Descendants.OfType<IdentifierExpression>().Any(ident => ident.Identifier == variableName)) { - // variable is used, so we'll create a variable declaration - usingStatement.ResourceAcquisition = new VariableDeclarationStatement { - Type = (AstType)varDecl.Type.Clone(), - Variables = { - new VariableInitializer { - Name = variableName, - Initializer = m1.Get<Expression>("initializer").Single().Detach() - }.CopyAnnotationsFrom(node.Expression) - .WithAnnotation(m1.Get<AstNode>("variable").Single().Annotation<ILVariable>()) - } - }.CopyAnnotationsFrom(node); - } else { - // the variable is never used; eliminate it: - usingStatement.ResourceAcquisition = m1.Get<Expression>("initializer").Single().Detach(); - } - return usingStatement; - } - - internal static VariableDeclarationStatement FindVariableDeclaration(AstNode node, string identifier) - { - while (node != null) { - while (node.PrevSibling != null) { - node = node.PrevSibling; - VariableDeclarationStatement varDecl = node as VariableDeclarationStatement; - if (varDecl != null && varDecl.Variables.Count == 1 && varDecl.Variables.Single().Name == identifier) { - return varDecl; - } - } - node = node.Parent; - } - return null; - } - - /// <summary> - /// Gets whether the old variable value (assigned inside 'targetStatement' or earlier) - /// is read anywhere in the remaining scope of the variable declaration. - /// </summary> - bool IsVariableValueUnused(VariableDeclarationStatement varDecl, Statement targetStatement) - { - Debug.Assert(targetStatement.Ancestors.Contains(varDecl.Parent)); - BlockStatement block = (BlockStatement)varDecl.Parent; - DefiniteAssignmentAnalysis daa = new DefiniteAssignmentAnalysis(block, context.CancellationToken); - daa.SetAnalyzedRange(targetStatement, block, startInclusive: false); - daa.Analyze(varDecl.Variables.Single().Name); - return daa.UnassignedVariableUses.Count == 0; - } - - // I used this in the first implementation of the using-statement transform, but now no longer - // because there were problems when multiple using statements were using the same variable - // - no single using statement could be transformed without making the C# code invalid, - // but transforming both would work. - // We now use 'IsVariableValueUnused' which will perform the transform - // even if it results in two variables with the same name and overlapping scopes. - // (this issue could be fixed later by renaming one of the variables) - - // I'm not sure whether the other consumers of 'CanMoveVariableDeclarationIntoStatement' should be changed the same way. - bool CanMoveVariableDeclarationIntoStatement(VariableDeclarationStatement varDecl, Statement targetStatement, out Statement declarationPoint) - { - Debug.Assert(targetStatement.Ancestors.Contains(varDecl.Parent)); - // Find all blocks between targetStatement and varDecl.Parent - List<BlockStatement> blocks = targetStatement.Ancestors.TakeWhile(block => block != varDecl.Parent).OfType<BlockStatement>().ToList(); - blocks.Add((BlockStatement)varDecl.Parent); // also handle the varDecl.Parent block itself - blocks.Reverse(); // go from parent blocks to child blocks - DefiniteAssignmentAnalysis daa = new DefiniteAssignmentAnalysis(blocks[0], context.CancellationToken); - declarationPoint = null; - foreach (BlockStatement block in blocks) { - if (!DeclareVariables.FindDeclarationPoint(daa, varDecl, block, out declarationPoint)) { - return false; - } - } - return true; - } - - /// <summary> - /// Gets whether there is an assignment to 'variableName' anywhere within the given node. - /// </summary> - bool HasAssignment(AstNode root, string variableName) - { - foreach (AstNode node in root.DescendantsAndSelf) { - IdentifierExpression ident = node as IdentifierExpression; - if (ident != null && ident.Identifier == variableName) { - if (ident.Parent is AssignmentExpression && ident.Role == AssignmentExpression.LeftRole - || ident.Parent is DirectionExpression) - { - return true; - } - } - } - return false; - } - #endregion - - #region foreach (generic) - static readonly UsingStatement genericForeachPattern = new UsingStatement { - ResourceAcquisition = new VariableDeclarationStatement { - Type = new AnyNode("enumeratorType"), - Variables = { - new NamedNode( - "enumeratorVariable", - new VariableInitializer { - Name = Pattern.AnyString, - Initializer = new AnyNode("collection").ToExpression().Invoke("GetEnumerator") - } - ) - } - }, - EmbeddedStatement = new BlockStatement { - new Repeat( - new VariableDeclarationStatement { Type = new AnyNode(), Variables = { new VariableInitializer(Pattern.AnyString) } }.WithName("variablesOutsideLoop") - ).ToStatement(), - new WhileStatement { - Condition = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Invoke("MoveNext"), - EmbeddedStatement = new BlockStatement { - new Repeat( - new VariableDeclarationStatement { - Type = new AnyNode(), - Variables = { new VariableInitializer(Pattern.AnyString) } - }.WithName("variablesInsideLoop") - ).ToStatement(), - new AssignmentExpression { - Left = new IdentifierExpression(Pattern.AnyString).WithName("itemVariable"), - Operator = AssignmentOperatorType.Assign, - Right = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Member("Current") - }, - new Repeat(new AnyNode("statement")).ToStatement() - } - }.WithName("loop") - }}; - - public ForeachStatement TransformForeach(UsingStatement node) - { - Match m = genericForeachPattern.Match(node); - if (!m.Success) - return null; - if (!(node.Parent is BlockStatement) && m.Has("variablesOutsideLoop")) { - // if there are variables outside the loop, we need to put those into the parent block, and that won't work if the direct parent isn't a block - return null; - } - VariableInitializer enumeratorVar = m.Get<VariableInitializer>("enumeratorVariable").Single(); - IdentifierExpression itemVar = m.Get<IdentifierExpression>("itemVariable").Single(); - WhileStatement loop = m.Get<WhileStatement>("loop").Single(); - - // Find the declaration of the item variable: - // Because we look only outside the loop, we won't make the mistake of moving a captured variable across the loop boundary - VariableDeclarationStatement itemVarDecl = FindVariableDeclaration(loop, itemVar.Identifier); - if (itemVarDecl == null || !(itemVarDecl.Parent is BlockStatement)) - return null; - - // Now verify that we can move the variable declaration in front of the loop: - Statement declarationPoint; - CanMoveVariableDeclarationIntoStatement(itemVarDecl, loop, out declarationPoint); - // We ignore the return value because we don't care whether we can move the variable into the loop - // (that is possible only with non-captured variables). - // We just care that we can move it in front of the loop: - if (declarationPoint != loop) - return null; - - BlockStatement newBody = new BlockStatement(); - foreach (Statement stmt in m.Get<Statement>("variablesInsideLoop")) - newBody.Add(stmt.Detach()); - foreach (Statement stmt in m.Get<Statement>("statement")) - newBody.Add(stmt.Detach()); - - ForeachStatement foreachStatement = new ForeachStatement { - VariableType = (AstType)itemVarDecl.Type.Clone(), - VariableName = itemVar.Identifier, - InExpression = m.Get<Expression>("collection").Single().Detach(), - EmbeddedStatement = newBody - }.WithAnnotation(itemVarDecl.Variables.Single().Annotation<ILVariable>()); - if (foreachStatement.InExpression is BaseReferenceExpression) { - foreachStatement.InExpression = new ThisReferenceExpression().CopyAnnotationsFrom(foreachStatement.InExpression); - } - node.ReplaceWith(foreachStatement); - foreach (Statement stmt in m.Get<Statement>("variablesOutsideLoop")) { - ((BlockStatement)foreachStatement.Parent).Statements.InsertAfter(null, stmt.Detach()); - } - return foreachStatement; - } - #endregion - - #region foreach (non-generic) - ExpressionStatement getEnumeratorPattern = new ExpressionStatement( - new AssignmentExpression( - new NamedNode("left", new IdentifierExpression(Pattern.AnyString)), - new AnyNode("collection").ToExpression().Invoke("GetEnumerator") - )); - - TryCatchStatement nonGenericForeachPattern = new TryCatchStatement { - TryBlock = new BlockStatement { - new WhileStatement { - Condition = new IdentifierExpression(Pattern.AnyString).WithName("enumerator").Invoke("MoveNext"), - EmbeddedStatement = new BlockStatement { - new AssignmentExpression( - new IdentifierExpression(Pattern.AnyString).WithName("itemVar"), - new Choice { - new Backreference("enumerator").ToExpression().Member("Current"), - new CastExpression { - Type = new AnyNode("castType"), - Expression = new Backreference("enumerator").ToExpression().Member("Current") - } - } - ), - new Repeat(new AnyNode("stmt")).ToStatement() - } - }.WithName("loop") - }, - FinallyBlock = new BlockStatement { - new AssignmentExpression( - new IdentifierExpression(Pattern.AnyString).WithName("disposable"), - new Backreference("enumerator").ToExpression().CastAs(new TypePattern(typeof(IDisposable))) - ), - new IfElseStatement { - Condition = new BinaryOperatorExpression { - Left = new Backreference("disposable"), - Operator = BinaryOperatorType.InEquality, - Right = new NullReferenceExpression() - }, - TrueStatement = new BlockStatement { - new Backreference("disposable").ToExpression().Invoke("Dispose") - } - } - }}; - - public ForeachStatement TransformNonGenericForEach(ExpressionStatement node) - { - Match m1 = getEnumeratorPattern.Match(node); - if (!m1.Success) return null; - AstNode tryCatch = node.NextSibling; - Match m2 = nonGenericForeachPattern.Match(tryCatch); - if (!m2.Success) return null; - - IdentifierExpression enumeratorVar = m2.Get<IdentifierExpression>("enumerator").Single(); - IdentifierExpression itemVar = m2.Get<IdentifierExpression>("itemVar").Single(); - WhileStatement loop = m2.Get<WhileStatement>("loop").Single(); - - // verify that the getEnumeratorPattern assigns to the same variable as the nonGenericForeachPattern is reading from - if (!enumeratorVar.IsMatch(m1.Get("left").Single())) - return null; - - VariableDeclarationStatement enumeratorVarDecl = FindVariableDeclaration(loop, enumeratorVar.Identifier); - if (enumeratorVarDecl == null || !(enumeratorVarDecl.Parent is BlockStatement)) - return null; - - // Find the declaration of the item variable: - // Because we look only outside the loop, we won't make the mistake of moving a captured variable across the loop boundary - VariableDeclarationStatement itemVarDecl = FindVariableDeclaration(loop, itemVar.Identifier); - if (itemVarDecl == null || !(itemVarDecl.Parent is BlockStatement)) - return null; - - // Now verify that we can move the variable declaration in front of the loop: - Statement declarationPoint; - CanMoveVariableDeclarationIntoStatement(itemVarDecl, loop, out declarationPoint); - // We ignore the return value because we don't care whether we can move the variable into the loop - // (that is possible only with non-captured variables). - // We just care that we can move it in front of the loop: - if (declarationPoint != loop) - return null; - - ForeachStatement foreachStatement = new ForeachStatement - { - VariableType = itemVarDecl.Type.Clone(), - VariableName = itemVar.Identifier, - }.WithAnnotation(itemVarDecl.Variables.Single().Annotation<ILVariable>()); - BlockStatement body = new BlockStatement(); - foreachStatement.EmbeddedStatement = body; - ((BlockStatement)node.Parent).Statements.InsertBefore(node, foreachStatement); - - body.Add(node.Detach()); - body.Add((Statement)tryCatch.Detach()); - - // Now that we moved the whole try-catch into the foreach loop; verify that we can - // move the enumerator into the foreach loop: - CanMoveVariableDeclarationIntoStatement(enumeratorVarDecl, foreachStatement, out declarationPoint); - if (declarationPoint != foreachStatement) { - // oops, the enumerator variable can't be moved into the foreach loop - // Undo our AST changes: - ((BlockStatement)foreachStatement.Parent).Statements.InsertBefore(foreachStatement, node.Detach()); - foreachStatement.ReplaceWith(tryCatch); - return null; - } - - // Now create the correct body for the foreach statement: - foreachStatement.InExpression = m1.Get<Expression>("collection").Single().Detach(); - if (foreachStatement.InExpression is BaseReferenceExpression) { - foreachStatement.InExpression = new ThisReferenceExpression().CopyAnnotationsFrom(foreachStatement.InExpression); - } - body.Statements.Clear(); - body.Statements.AddRange(m2.Get<Statement>("stmt").Select(stmt => stmt.Detach())); - - return foreachStatement; - } - #endregion - - #region for - static readonly WhileStatement forPattern = new WhileStatement { - Condition = new BinaryOperatorExpression { - Left = new NamedNode("ident", new IdentifierExpression(Pattern.AnyString)), - Operator = BinaryOperatorType.Any, - Right = new AnyNode("endExpr") - }, - EmbeddedStatement = new BlockStatement { - Statements = { - new Repeat(new AnyNode("statement")), - new NamedNode( - "increment", - new ExpressionStatement( - new AssignmentExpression { - Left = new Backreference("ident"), - Operator = AssignmentOperatorType.Any, - Right = new AnyNode() - })) - } - }}; - - public ForStatement TransformFor(ExpressionStatement node) - { - Match m1 = variableAssignPattern.Match(node); - if (!m1.Success) return null; - AstNode next = node.NextSibling; - Match m2 = forPattern.Match(next); - if (!m2.Success) return null; - // ensure the variable in the for pattern is the same as in the declaration - if (m1.Get<IdentifierExpression>("variable").Single().Identifier != m2.Get<IdentifierExpression>("ident").Single().Identifier) - return null; - WhileStatement loop = (WhileStatement)next; - node.Remove(); - BlockStatement newBody = new BlockStatement(); - foreach (Statement stmt in m2.Get<Statement>("statement")) - newBody.Add(stmt.Detach()); - ForStatement forStatement = new ForStatement(); - forStatement.Initializers.Add(node); - forStatement.Condition = loop.Condition.Detach(); - forStatement.Iterators.Add(m2.Get<Statement>("increment").Single().Detach()); - forStatement.EmbeddedStatement = newBody; - loop.ReplaceWith(forStatement); - return forStatement; - } - #endregion - - #region doWhile - static readonly WhileStatement doWhilePattern = new WhileStatement { - Condition = new PrimitiveExpression(true), - EmbeddedStatement = new BlockStatement { - Statements = { - new Repeat(new AnyNode("statement")), - new IfElseStatement { - Condition = new AnyNode("condition"), - TrueStatement = new BlockStatement { new BreakStatement() } - } - } - }}; - - public DoWhileStatement TransformDoWhile(WhileStatement whileLoop) - { - Match m = doWhilePattern.Match(whileLoop); - if (m.Success) { - DoWhileStatement doLoop = new DoWhileStatement(); - doLoop.Condition = new UnaryOperatorExpression(UnaryOperatorType.Not, m.Get<Expression>("condition").Single().Detach()); - doLoop.Condition.AcceptVisitor(new PushNegation(), null); - BlockStatement block = (BlockStatement)whileLoop.EmbeddedStatement; - block.Statements.Last().Remove(); // remove if statement - doLoop.EmbeddedStatement = block.Detach(); - whileLoop.ReplaceWith(doLoop); - - // we may have to extract variable definitions out of the loop if they were used in the condition: - foreach (var varDecl in block.Statements.OfType<VariableDeclarationStatement>()) { - VariableInitializer v = varDecl.Variables.Single(); - if (doLoop.Condition.DescendantsAndSelf.OfType<IdentifierExpression>().Any(i => i.Identifier == v.Name)) { - AssignmentExpression assign = new AssignmentExpression(new IdentifierExpression(v.Name), v.Initializer.Detach()); - // move annotations from v to assign: - assign.CopyAnnotationsFrom(v); - v.RemoveAnnotations<object>(); - // remove varDecl with assignment; and move annotations from varDecl to the ExpressionStatement: - varDecl.ReplaceWith(new ExpressionStatement(assign).CopyAnnotationsFrom(varDecl)); - varDecl.RemoveAnnotations<object>(); - - // insert the varDecl above the do-while loop: - doLoop.Parent.InsertChildBefore(doLoop, varDecl, BlockStatement.StatementRole); - } - } - return doLoop; - } - return null; - } - #endregion - - #region lock - static readonly AstNode lockFlagInitPattern = new ExpressionStatement( - new AssignmentExpression( - new NamedNode("variable", new IdentifierExpression(Pattern.AnyString)), - new PrimitiveExpression(false) - )); - - static readonly AstNode lockTryCatchPattern = new TryCatchStatement { - TryBlock = new BlockStatement { - new OptionalNode(new VariableDeclarationStatement()).ToStatement(), - new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke( - "Enter", new AnyNode("enter"), - new DirectionExpression { - FieldDirection = FieldDirection.Ref, - Expression = new NamedNode("flag", new IdentifierExpression(Pattern.AnyString)) - }), - new Repeat(new AnyNode()).ToStatement() - }, - FinallyBlock = new BlockStatement { - new IfElseStatement { - Condition = new Backreference("flag"), - TrueStatement = new BlockStatement { - new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke("Exit", new AnyNode("exit")) - } - } - }}; - - static readonly AstNode oldMonitorCallPattern = new ExpressionStatement( - new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke("Enter", new AnyNode("enter")) - ); - - static readonly AstNode oldLockTryCatchPattern = new TryCatchStatement - { - TryBlock = new BlockStatement { - new Repeat(new AnyNode()).ToStatement() - }, - FinallyBlock = new BlockStatement { - new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke("Exit", new AnyNode("exit")) - } - }; - - bool AnalyzeLockV2(ExpressionStatement node, out Expression enter, out Expression exit) - { - enter = null; - exit = null; - Match m1 = oldMonitorCallPattern.Match(node); - if (!m1.Success) return false; - Match m2 = oldLockTryCatchPattern.Match(node.NextSibling); - if (!m2.Success) return false; - enter = m1.Get<Expression>("enter").Single(); - exit = m2.Get<Expression>("exit").Single(); - return true; - } - - bool AnalyzeLockV4(ExpressionStatement node, out Expression enter, out Expression exit) - { - enter = null; - exit = null; - Match m1 = lockFlagInitPattern.Match(node); - if (!m1.Success) return false; - Match m2 = lockTryCatchPattern.Match(node.NextSibling); - if (!m2.Success) return false; - enter = m2.Get<Expression>("enter").Single(); - exit = m2.Get<Expression>("exit").Single(); - return m1.Get<IdentifierExpression>("variable").Single().Identifier == m2.Get<IdentifierExpression>("flag").Single().Identifier; - } - - public LockStatement TransformLock(ExpressionStatement node) - { - Expression enter, exit; - bool isV2 = AnalyzeLockV2(node, out enter, out exit); - if (isV2 || AnalyzeLockV4(node, out enter, out exit)) { - AstNode tryCatch = node.NextSibling; - if (!exit.IsMatch(enter)) { - // If exit and enter are not the same, then enter must be "exit = ..." - AssignmentExpression assign = enter as AssignmentExpression; - if (assign == null) - return null; - if (!exit.IsMatch(assign.Left)) - return null; - enter = assign.Right; - // TODO: verify that 'obj' variable can be removed - } - // TODO: verify that 'flag' variable can be removed - // transform the code into a lock statement: - LockStatement l = new LockStatement(); - l.Expression = enter.Detach(); - l.EmbeddedStatement = ((TryCatchStatement)tryCatch).TryBlock.Detach(); - if (!isV2) // Remove 'Enter()' call - ((BlockStatement)l.EmbeddedStatement).Statements.First().Remove(); - tryCatch.ReplaceWith(l); - node.Remove(); // remove flag variable - return l; - } - return null; - } - #endregion - - #region switch on strings - static readonly IfElseStatement switchOnStringPattern = new IfElseStatement { - Condition = new BinaryOperatorExpression { - Left = new AnyNode("switchExpr"), - Operator = BinaryOperatorType.InEquality, - Right = new NullReferenceExpression() - }, - TrueStatement = new BlockStatement { - new IfElseStatement { - Condition = new BinaryOperatorExpression { - Left = new AnyNode("cachedDict"), - Operator = BinaryOperatorType.Equality, - Right = new NullReferenceExpression() - }, - TrueStatement = new AnyNode("dictCreation") - }, - new IfElseStatement { - Condition = new Backreference("cachedDict").ToExpression().Invoke( - "TryGetValue", - new NamedNode("switchVar", new IdentifierExpression(Pattern.AnyString)), - new DirectionExpression { - FieldDirection = FieldDirection.Out, - Expression = new IdentifierExpression(Pattern.AnyString).WithName("intVar") - }), - TrueStatement = new BlockStatement { - Statements = { - new NamedNode( - "switch", new SwitchStatement { - Expression = new IdentifierExpressionBackreference("intVar"), - SwitchSections = { new Repeat(new AnyNode()) } - }) - } - } - }, - new Repeat(new AnyNode("nonNullDefaultStmt")).ToStatement() - }, - FalseStatement = new OptionalNode("nullStmt", new BlockStatement { Statements = { new Repeat(new AnyNode()) } }) - }; - - public SwitchStatement TransformSwitchOnString(IfElseStatement node) - { - Match m = switchOnStringPattern.Match(node); - if (!m.Success) - return null; - // switchVar must be the same as switchExpr; or switchExpr must be an assignment and switchVar the left side of that assignment - if (!m.Get("switchVar").Single().IsMatch(m.Get("switchExpr").Single())) { - AssignmentExpression assign = m.Get("switchExpr").Single() as AssignmentExpression; - if (!(assign != null && m.Get("switchVar").Single().IsMatch(assign.Left))) - return null; - } - FieldReference cachedDictField = m.Get<AstNode>("cachedDict").Single().Annotation<FieldReference>(); - if (cachedDictField == null) - return null; - List<Statement> dictCreation = m.Get<BlockStatement>("dictCreation").Single().Statements.ToList(); - List<KeyValuePair<string, int>> dict = BuildDictionary(dictCreation); - SwitchStatement sw = m.Get<SwitchStatement>("switch").Single(); - sw.Expression = m.Get<Expression>("switchExpr").Single().Detach(); - foreach (SwitchSection section in sw.SwitchSections) { - List<CaseLabel> labels = section.CaseLabels.ToList(); - section.CaseLabels.Clear(); - foreach (CaseLabel label in labels) { - PrimitiveExpression expr = label.Expression as PrimitiveExpression; - if (expr == null || !(expr.Value is int)) - continue; - int val = (int)expr.Value; - foreach (var pair in dict) { - if (pair.Value == val) - section.CaseLabels.Add(new CaseLabel { Expression = new PrimitiveExpression(pair.Key) }); - } - } - } - if (m.Has("nullStmt")) { - SwitchSection section = new SwitchSection(); - section.CaseLabels.Add(new CaseLabel { Expression = new NullReferenceExpression() }); - BlockStatement block = m.Get<BlockStatement>("nullStmt").Single(); - block.Statements.Add(new BreakStatement()); - section.Statements.Add(block.Detach()); - sw.SwitchSections.Add(section); - } else if (m.Has("nonNullDefaultStmt")) { - sw.SwitchSections.Add( - new SwitchSection { - CaseLabels = { new CaseLabel { Expression = new NullReferenceExpression() } }, - Statements = { new BlockStatement { new BreakStatement() } } - }); - } - if (m.Has("nonNullDefaultStmt")) { - SwitchSection section = new SwitchSection(); - section.CaseLabels.Add(new CaseLabel()); - BlockStatement block = new BlockStatement(); - block.Statements.AddRange(m.Get<Statement>("nonNullDefaultStmt").Select(s => s.Detach())); - block.Add(new BreakStatement()); - section.Statements.Add(block); - sw.SwitchSections.Add(section); - } - node.ReplaceWith(sw); - return sw; - } - - List<KeyValuePair<string, int>> BuildDictionary(List<Statement> dictCreation) - { - if (context.Settings.ObjectOrCollectionInitializers && dictCreation.Count == 1) - return BuildDictionaryFromInitializer(dictCreation[0]); - - return BuildDictionaryFromAddMethodCalls(dictCreation); - } - - static readonly Statement assignInitializedDictionary = new ExpressionStatement { - Expression = new AssignmentExpression { - Left = new AnyNode().ToExpression(), - Right = new ObjectCreateExpression { - Type = new AnyNode(), - Arguments = { new Repeat(new AnyNode()) }, - Initializer = new ArrayInitializerExpression { - Elements = { new Repeat(new AnyNode("dictJumpTable")) } - } - }, - }, - }; - - private List<KeyValuePair<string, int>> BuildDictionaryFromInitializer(Statement statement) - { - List<KeyValuePair<string, int>> dict = new List<KeyValuePair<string, int>>(); - Match m = assignInitializedDictionary.Match(statement); - if (!m.Success) - return dict; - - foreach (ArrayInitializerExpression initializer in m.Get<ArrayInitializerExpression>("dictJumpTable")) { - KeyValuePair<string, int> pair; - if (TryGetPairFrom(initializer.Elements, out pair)) - dict.Add(pair); - } - - return dict; - } - - private static List<KeyValuePair<string, int>> BuildDictionaryFromAddMethodCalls(List<Statement> dictCreation) - { - List<KeyValuePair<string, int>> dict = new List<KeyValuePair<string, int>>(); - for (int i = 0; i < dictCreation.Count; i++) { - ExpressionStatement es = dictCreation[i] as ExpressionStatement; - if (es == null) - continue; - InvocationExpression ie = es.Expression as InvocationExpression; - if (ie == null) - continue; - - KeyValuePair<string, int> pair; - if (TryGetPairFrom(ie.Arguments, out pair)) - dict.Add(pair); - } - return dict; - } - - private static bool TryGetPairFrom(AstNodeCollection<Expression> expressions, out KeyValuePair<string, int> pair) - { - PrimitiveExpression arg1 = expressions.ElementAtOrDefault(0) as PrimitiveExpression; - PrimitiveExpression arg2 = expressions.ElementAtOrDefault(1) as PrimitiveExpression; - if (arg1 != null && arg2 != null && arg1.Value is string && arg2.Value is int) { - pair = new KeyValuePair<string, int>((string)arg1.Value, (int)arg2.Value); - return true; - } - - pair = default(KeyValuePair<string, int>); - return false; - } - - #endregion - - #region Automatic Properties - static readonly PropertyDeclaration automaticPropertyPattern = new PropertyDeclaration { - Attributes = { new Repeat(new AnyNode()) }, - Modifiers = Modifiers.Any, - ReturnType = new AnyNode(), - PrivateImplementationType = new OptionalNode(new AnyNode()), - Name = Pattern.AnyString, - Getter = new Accessor { - Attributes = { new Repeat(new AnyNode()) }, - Modifiers = Modifiers.Any, - Body = new BlockStatement { - new ReturnStatement { - Expression = new AnyNode("fieldReference") - } - } - }, - Setter = new Accessor { - Attributes = { new Repeat(new AnyNode()) }, - Modifiers = Modifiers.Any, - Body = new BlockStatement { - new AssignmentExpression { - Left = new Backreference("fieldReference"), - Right = new IdentifierExpression("value") - } - }}}; - - PropertyDeclaration TransformAutomaticProperties(PropertyDeclaration property) - { - PropertyDefinition cecilProperty = property.Annotation<PropertyDefinition>(); - if (cecilProperty == null || cecilProperty.GetMethod == null || cecilProperty.SetMethod == null) - return null; - if (!(cecilProperty.GetMethod.IsCompilerGenerated() && cecilProperty.SetMethod.IsCompilerGenerated())) - return null; - Match m = automaticPropertyPattern.Match(property); - if (m.Success) { - FieldDefinition field = m.Get<AstNode>("fieldReference").Single().Annotation<FieldReference>().ResolveWithinSameModule(); - if (field.IsCompilerGenerated() && field.DeclaringType == cecilProperty.DeclaringType) { - RemoveCompilerGeneratedAttribute(property.Getter.Attributes); - RemoveCompilerGeneratedAttribute(property.Setter.Attributes); - property.Getter.Body = null; - property.Setter.Body = null; - } - } - // Since the event instance is not changed, we can continue in the visitor as usual, so return null - return null; - } - - void RemoveCompilerGeneratedAttribute(AstNodeCollection<AttributeSection> attributeSections) - { - foreach (AttributeSection section in attributeSections) { - foreach (var attr in section.Attributes) { - TypeReference tr = attr.Type.Annotation<TypeReference>(); - if (tr != null && tr.Namespace == "System.Runtime.CompilerServices" && tr.Name == "CompilerGeneratedAttribute") { - attr.Remove(); - } - } - if (section.Attributes.Count == 0) - section.Remove(); - } - } - #endregion - - #region Automatic Events - static readonly Accessor automaticEventPatternV4 = new Accessor { - Attributes = { new Repeat(new AnyNode()) }, - Body = new BlockStatement { - new VariableDeclarationStatement { Type = new AnyNode("type"), Variables = { new AnyNode() } }, - new VariableDeclarationStatement { Type = new Backreference("type"), Variables = { new AnyNode() } }, - new VariableDeclarationStatement { Type = new Backreference("type"), Variables = { new AnyNode() } }, - new AssignmentExpression { - Left = new NamedNode("var1", new IdentifierExpression(Pattern.AnyString)), - Operator = AssignmentOperatorType.Assign, - Right = new NamedNode( - "field", - new MemberReferenceExpression { - Target = new Choice { new ThisReferenceExpression(), new TypeReferenceExpression { Type = new AnyNode() } }, - MemberName = Pattern.AnyString - }) - }, - new DoWhileStatement { - EmbeddedStatement = new BlockStatement { - new AssignmentExpression(new NamedNode("var2", new IdentifierExpression(Pattern.AnyString)), new IdentifierExpressionBackreference("var1")), - new AssignmentExpression { - Left = new NamedNode("var3", new IdentifierExpression(Pattern.AnyString)), - Operator = AssignmentOperatorType.Assign, - Right = new AnyNode("delegateCombine").ToExpression().Invoke( - new IdentifierExpressionBackreference("var2"), - new IdentifierExpression("value") - ).CastTo(new Backreference("type")) - }, - new AssignmentExpression { - Left = new IdentifierExpressionBackreference("var1"), - Right = new TypePattern(typeof(System.Threading.Interlocked)).ToType().Invoke( - "CompareExchange", - new AstType[] { new Backreference("type") }, // type argument - new Expression[] { // arguments - new DirectionExpression { FieldDirection = FieldDirection.Ref, Expression = new Backreference("field") }, - new IdentifierExpressionBackreference("var3"), - new IdentifierExpressionBackreference("var2") - } - )} - }, - Condition = new BinaryOperatorExpression { - Left = new IdentifierExpressionBackreference("var1"), - Operator = BinaryOperatorType.InEquality, - Right = new IdentifierExpressionBackreference("var2") - }} - }}; - - bool CheckAutomaticEventV4Match(Match m, CustomEventDeclaration ev, bool isAddAccessor) - { - if (!m.Success) - return false; - if (m.Get<MemberReferenceExpression>("field").Single().MemberName != ev.Name) - return false; // field name must match event name - if (!ev.ReturnType.IsMatch(m.Get("type").Single())) - return false; // variable types must match event type - var combineMethod = m.Get<AstNode>("delegateCombine").Single().Parent.Annotation<MethodReference>(); - if (combineMethod == null || combineMethod.Name != (isAddAccessor ? "Combine" : "Remove")) - return false; - return combineMethod.DeclaringType.FullName == "System.Delegate"; - } - - EventDeclaration TransformAutomaticEvents(CustomEventDeclaration ev) - { - Match m1 = automaticEventPatternV4.Match(ev.AddAccessor); - if (!CheckAutomaticEventV4Match(m1, ev, true)) - return null; - Match m2 = automaticEventPatternV4.Match(ev.RemoveAccessor); - if (!CheckAutomaticEventV4Match(m2, ev, false)) - return null; - EventDeclaration ed = new EventDeclaration(); - ev.Attributes.MoveTo(ed.Attributes); - foreach (var attr in ev.AddAccessor.Attributes) { - attr.AttributeTarget = "method"; - ed.Attributes.Add(attr.Detach()); - } - ed.ReturnType = ev.ReturnType.Detach(); - ed.Modifiers = ev.Modifiers; - ed.Variables.Add(new VariableInitializer(ev.Name)); - ed.CopyAnnotationsFrom(ev); - - EventDefinition eventDef = ev.Annotation<EventDefinition>(); - if (eventDef != null) { - FieldDefinition field = eventDef.DeclaringType.Fields.FirstOrDefault(f => f.Name == ev.Name); - if (field != null) { - ed.AddAnnotation(field); - AstBuilder.ConvertAttributes(ed, field, "field"); - } - } - - ev.ReplaceWith(ed); - return ed; - } - #endregion - - #region Destructor - static readonly MethodDeclaration destructorPattern = new MethodDeclaration { - Attributes = { new Repeat(new AnyNode()) }, - Modifiers = Modifiers.Any, - ReturnType = new PrimitiveType("void"), - Name = "Finalize", - Body = new BlockStatement { - new TryCatchStatement { - TryBlock = new AnyNode("body"), - FinallyBlock = new BlockStatement { - new BaseReferenceExpression().Invoke("Finalize") - } - } - } - }; - - DestructorDeclaration TransformDestructor(MethodDeclaration methodDef) - { - Match m = destructorPattern.Match(methodDef); - if (m.Success) { - DestructorDeclaration dd = new DestructorDeclaration(); - methodDef.Attributes.MoveTo(dd.Attributes); - dd.Modifiers = methodDef.Modifiers & ~(Modifiers.Protected | Modifiers.Override); - dd.Body = m.Get<BlockStatement>("body").Single().Detach(); - dd.Name = AstBuilder.CleanName(context.CurrentType.Name); - methodDef.ReplaceWith(dd); - return dd; - } - return null; - } - #endregion - - #region Try-Catch-Finally - static readonly TryCatchStatement tryCatchFinallyPattern = new TryCatchStatement { - TryBlock = new BlockStatement { - new TryCatchStatement { - TryBlock = new AnyNode(), - CatchClauses = { new Repeat(new AnyNode()) } - } - }, - FinallyBlock = new AnyNode() - }; - - /// <summary> - /// Simplify nested 'try { try {} catch {} } finally {}'. - /// This transformation must run after the using/lock tranformations. - /// </summary> - TryCatchStatement TransformTryCatchFinally(TryCatchStatement tryFinally) - { - if (tryCatchFinallyPattern.IsMatch(tryFinally)) { - TryCatchStatement tryCatch = (TryCatchStatement)tryFinally.TryBlock.Statements.Single(); - tryFinally.TryBlock = tryCatch.TryBlock.Detach(); - tryCatch.CatchClauses.MoveTo(tryFinally.CatchClauses); - } - // Since the tryFinally instance is not changed, we can continue in the visitor as usual, so return null - return null; - } - #endregion - - #region Simplify cascading if-else-if statements - static readonly IfElseStatement cascadingIfElsePattern = new IfElseStatement - { - Condition = new AnyNode(), - TrueStatement = new AnyNode(), - FalseStatement = new BlockStatement { - Statements = { - new NamedNode( - "nestedIfStatement", - new IfElseStatement { - Condition = new AnyNode(), - TrueStatement = new AnyNode(), - FalseStatement = new OptionalNode(new AnyNode()) - } - ) - } - } - }; - - AstNode SimplifyCascadingIfElseStatements(IfElseStatement node) - { - Match m = cascadingIfElsePattern.Match(node); - if (m.Success) { - IfElseStatement elseIf = m.Get<IfElseStatement>("nestedIfStatement").Single(); - node.FalseStatement = elseIf.Detach(); - } - - return null; - } - #endregion - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs deleted file mode 100644 index 193c5e6923..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs +++ /dev/null @@ -1,164 +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.Collections.Generic; -using System.Linq; -using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.PatternMatching; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - public class PushNegation: DepthFirstAstVisitor<object, object>, IAstTransform - { - sealed class LiftedOperator { } - /// <summary> - /// Annotation for lifted operators that cannot be transformed by PushNegation - /// </summary> - public static readonly object LiftedOperatorAnnotation = new LiftedOperator(); - - public override object VisitUnaryOperatorExpression(UnaryOperatorExpression unary, object data) - { - // lifted operators can't be transformed - if (unary.Annotation<LiftedOperator>() != null || unary.Expression.Annotation<LiftedOperator>() != null) - return base.VisitUnaryOperatorExpression(unary, data); - - // Remove double negation - // !!a - if (unary.Operator == UnaryOperatorType.Not && - unary.Expression is UnaryOperatorExpression && - (unary.Expression as UnaryOperatorExpression).Operator == UnaryOperatorType.Not) - { - AstNode newNode = (unary.Expression as UnaryOperatorExpression).Expression; - unary.ReplaceWith(newNode); - return newNode.AcceptVisitor(this, data); - } - - // Push through binary operation - // !((a) op (b)) - BinaryOperatorExpression binaryOp = unary.Expression as BinaryOperatorExpression; - if (unary.Operator == UnaryOperatorType.Not && binaryOp != null) { - bool successful = true; - switch (binaryOp.Operator) { - case BinaryOperatorType.Equality: - binaryOp.Operator = BinaryOperatorType.InEquality; - break; - case BinaryOperatorType.InEquality: - binaryOp.Operator = BinaryOperatorType.Equality; - break; - case BinaryOperatorType.GreaterThan: // TODO: these are invalid for floats (stupid NaN) - binaryOp.Operator = BinaryOperatorType.LessThanOrEqual; - break; - case BinaryOperatorType.GreaterThanOrEqual: - binaryOp.Operator = BinaryOperatorType.LessThan; - break; - case BinaryOperatorType.LessThanOrEqual: - binaryOp.Operator = BinaryOperatorType.GreaterThan; - break; - case BinaryOperatorType.LessThan: - binaryOp.Operator = BinaryOperatorType.GreaterThanOrEqual; - break; - default: - successful = false; - break; - } - if (successful) { - unary.ReplaceWith(binaryOp); - return binaryOp.AcceptVisitor(this, data); - } - - successful = true; - switch (binaryOp.Operator) { - case BinaryOperatorType.ConditionalAnd: - binaryOp.Operator = BinaryOperatorType.ConditionalOr; - break; - case BinaryOperatorType.ConditionalOr: - binaryOp.Operator = BinaryOperatorType.ConditionalAnd; - break; - default: - successful = false; - break; - } - if (successful) { - binaryOp.Left.ReplaceWith(e => new UnaryOperatorExpression(UnaryOperatorType.Not, e)); - binaryOp.Right.ReplaceWith(e => new UnaryOperatorExpression(UnaryOperatorType.Not, e)); - unary.ReplaceWith(binaryOp); - return binaryOp.AcceptVisitor(this, data); - } - } - return base.VisitUnaryOperatorExpression(unary, data); - } - - readonly static AstNode asCastIsNullPattern = new BinaryOperatorExpression( - new AnyNode("expr").ToExpression().CastAs(new AnyNode("type")), - BinaryOperatorType.Equality, - new NullReferenceExpression() - ); - - readonly static AstNode asCastIsNotNullPattern = new BinaryOperatorExpression( - new AnyNode("expr").ToExpression().CastAs(new AnyNode("type")), - BinaryOperatorType.InEquality, - new NullReferenceExpression() - ); - - public override object VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data) - { - // lifted operators can't be transformed - if (binaryOperatorExpression.Annotation<LiftedOperator>() != null) - return base.VisitBinaryOperatorExpression(binaryOperatorExpression, data); - - BinaryOperatorType op = binaryOperatorExpression.Operator; - bool? rightOperand = null; - if (binaryOperatorExpression.Right is PrimitiveExpression) - rightOperand = ((PrimitiveExpression)binaryOperatorExpression.Right).Value as bool?; - if (op == BinaryOperatorType.Equality && rightOperand == true || op == BinaryOperatorType.InEquality && rightOperand == false) { - // 'b == true' or 'b != false' is useless - binaryOperatorExpression.Left.AcceptVisitor(this, data); - binaryOperatorExpression.ReplaceWith(binaryOperatorExpression.Left); - return null; - } else if (op == BinaryOperatorType.Equality && rightOperand == false || op == BinaryOperatorType.InEquality && rightOperand == true) { - // 'b == false' or 'b != true' is a negation: - Expression left = binaryOperatorExpression.Left; - left.Remove(); - UnaryOperatorExpression uoe = new UnaryOperatorExpression(UnaryOperatorType.Not, left); - binaryOperatorExpression.ReplaceWith(uoe); - return uoe.AcceptVisitor(this, data); - } else { - bool negate = false; - Match m = asCastIsNotNullPattern.Match(binaryOperatorExpression); - if (!m.Success) { - m = asCastIsNullPattern.Match(binaryOperatorExpression); - negate = true; - } - if (m.Success) { - Expression expr = m.Get<Expression>("expr").Single().Detach().IsType(m.Get<AstType>("type").Single().Detach()); - if (negate) - expr = new UnaryOperatorExpression(UnaryOperatorType.Not, expr); - binaryOperatorExpression.ReplaceWith(expr); - return expr.AcceptVisitor(this, data); - } else { - return base.VisitBinaryOperatorExpression(binaryOperatorExpression, data); - } - } - } - void IAstTransform.Run(AstNode node) - { - node.AcceptVisitor(this, null); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs deleted file mode 100644 index 6a3f8f9743..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs +++ /dev/null @@ -1,356 +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.Collections.Generic; -using System.Linq; -using System.Reflection; -using ICSharpCode.NRefactory.PatternMatching; -using Mono.Cecil; -using Ast = ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.CSharp; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - /// <summary> - /// Replaces method calls with the appropriate operator expressions. - /// Also simplifies "x = x op y" into "x op= y" where possible. - /// </summary> - public class ReplaceMethodCallsWithOperators : DepthFirstAstVisitor<object, object>, IAstTransform - { - static readonly MemberReferenceExpression typeHandleOnTypeOfPattern = new MemberReferenceExpression { - Target = new Choice { - new TypeOfExpression(new AnyNode()), - new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.RefType, Arguments = { new AnyNode() } } - }, - MemberName = "TypeHandle" - }; - - DecompilerContext context; - - public ReplaceMethodCallsWithOperators(DecompilerContext context) - { - this.context = context; - } - - public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data) - { - base.VisitInvocationExpression(invocationExpression, data); - ProcessInvocationExpression(invocationExpression); - return null; - } - - internal static void ProcessInvocationExpression(InvocationExpression invocationExpression) - { - MethodReference methodRef = invocationExpression.Annotation<MethodReference>(); - if (methodRef == null) - return; - var arguments = invocationExpression.Arguments.ToArray(); - - // Reduce "String.Concat(a, b)" to "a + b" - if (methodRef.Name == "Concat" && methodRef.DeclaringType.FullName == "System.String" && arguments.Length >= 2) - { - invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression - Expression expr = arguments[0]; - for (int i = 1; i < arguments.Length; i++) { - expr = new BinaryOperatorExpression(expr, BinaryOperatorType.Add, arguments[i]); - } - invocationExpression.ReplaceWith(expr); - return; - } - - switch (methodRef.FullName) { - case "System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)": - if (arguments.Length == 1) { - if (typeHandleOnTypeOfPattern.IsMatch(arguments[0])) { - invocationExpression.ReplaceWith(((MemberReferenceExpression)arguments[0]).Target); - return; - } - } - break; - case "System.Reflection.FieldInfo System.Reflection.FieldInfo::GetFieldFromHandle(System.RuntimeFieldHandle)": - if (arguments.Length == 1) { - MemberReferenceExpression mre = arguments[0] as MemberReferenceExpression; - if (mre != null && mre.MemberName == "FieldHandle" && mre.Target.Annotation<LdTokenAnnotation>() != null) { - invocationExpression.ReplaceWith(mre.Target); - return; - } - } - break; - case "System.Reflection.FieldInfo System.Reflection.FieldInfo::GetFieldFromHandle(System.RuntimeFieldHandle,System.RuntimeTypeHandle)": - if (arguments.Length == 2) { - MemberReferenceExpression mre1 = arguments[0] as MemberReferenceExpression; - MemberReferenceExpression mre2 = arguments[1] as MemberReferenceExpression; - if (mre1 != null && mre1.MemberName == "FieldHandle" && mre1.Target.Annotation<LdTokenAnnotation>() != null) { - if (mre2 != null && mre2.MemberName == "TypeHandle" && mre2.Target is TypeOfExpression) { - Expression oldArg = ((InvocationExpression)mre1.Target).Arguments.Single(); - FieldReference field = oldArg.Annotation<FieldReference>(); - if (field != null) { - AstType declaringType = ((TypeOfExpression)mre2.Target).Type.Detach(); - oldArg.ReplaceWith(declaringType.Member(field.Name).WithAnnotation(field)); - invocationExpression.ReplaceWith(mre1.Target); - return; - } - } - } - } - break; - } - - BinaryOperatorType? bop = GetBinaryOperatorTypeFromMetadataName(methodRef.Name); - if (bop != null && arguments.Length == 2) { - invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression - invocationExpression.ReplaceWith( - new BinaryOperatorExpression(arguments[0], bop.Value, arguments[1]).WithAnnotation(methodRef) - ); - return; - } - UnaryOperatorType? uop = GetUnaryOperatorTypeFromMetadataName(methodRef.Name); - if (uop != null && arguments.Length == 1) { - arguments[0].Remove(); // detach argument - invocationExpression.ReplaceWith( - new UnaryOperatorExpression(uop.Value, arguments[0]).WithAnnotation(methodRef) - ); - return; - } - if (methodRef.Name == "op_Explicit" && arguments.Length == 1) { - arguments[0].Remove(); // detach argument - invocationExpression.ReplaceWith( - arguments[0].CastTo(AstBuilder.ConvertType(methodRef.ReturnType, methodRef.MethodReturnType)) - .WithAnnotation(methodRef) - ); - return; - } - if (methodRef.Name == "op_Implicit" && arguments.Length == 1) { - invocationExpression.ReplaceWith(arguments[0]); - return; - } - if (methodRef.Name == "op_True" && arguments.Length == 1 && invocationExpression.Role == Roles.Condition) { - invocationExpression.ReplaceWith(arguments[0]); - return; - } - - return; - } - - static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name) - { - switch (name) { - case "op_Addition": - return BinaryOperatorType.Add; - case "op_Subtraction": - return BinaryOperatorType.Subtract; - case "op_Multiply": - return BinaryOperatorType.Multiply; - case "op_Division": - return BinaryOperatorType.Divide; - case "op_Modulus": - return BinaryOperatorType.Modulus; - case "op_BitwiseAnd": - return BinaryOperatorType.BitwiseAnd; - case "op_BitwiseOr": - return BinaryOperatorType.BitwiseOr; - case "op_ExclusiveOr": - return BinaryOperatorType.ExclusiveOr; - case "op_LeftShift": - return BinaryOperatorType.ShiftLeft; - case "op_RightShift": - return BinaryOperatorType.ShiftRight; - case "op_Equality": - return BinaryOperatorType.Equality; - case "op_Inequality": - return BinaryOperatorType.InEquality; - case "op_LessThan": - return BinaryOperatorType.LessThan; - case "op_LessThanOrEqual": - return BinaryOperatorType.LessThanOrEqual; - case "op_GreaterThan": - return BinaryOperatorType.GreaterThan; - case "op_GreaterThanOrEqual": - return BinaryOperatorType.GreaterThanOrEqual; - default: - return null; - } - } - - static UnaryOperatorType? GetUnaryOperatorTypeFromMetadataName(string name) - { - switch (name) { - case "op_LogicalNot": - return UnaryOperatorType.Not; - case "op_OnesComplement": - return UnaryOperatorType.BitNot; - case "op_UnaryNegation": - return UnaryOperatorType.Minus; - case "op_UnaryPlus": - return UnaryOperatorType.Plus; - case "op_Increment": - return UnaryOperatorType.Increment; - case "op_Decrement": - return UnaryOperatorType.Decrement; - default: - return null; - } - } - - /// <summary> - /// This annotation is used to convert a compound assignment "a += 2;" or increment operator "a++;" - /// back to the original "a = a + 2;". This is sometimes necessary when the checked/unchecked semantics - /// cannot be guaranteed otherwise (see CheckedUnchecked.ForWithCheckedInitializerAndUncheckedIterator test) - /// </summary> - public class RestoreOriginalAssignOperatorAnnotation - { - readonly BinaryOperatorExpression binaryOperatorExpression; - - public RestoreOriginalAssignOperatorAnnotation(BinaryOperatorExpression binaryOperatorExpression) - { - this.binaryOperatorExpression = binaryOperatorExpression; - } - - public AssignmentExpression Restore(Expression expression) - { - expression.RemoveAnnotations<RestoreOriginalAssignOperatorAnnotation>(); - AssignmentExpression assign = expression as AssignmentExpression; - if (assign == null) { - UnaryOperatorExpression uoe = (UnaryOperatorExpression)expression; - assign = new AssignmentExpression(uoe.Expression.Detach(), new PrimitiveExpression(1)); - } else { - assign.Operator = AssignmentOperatorType.Assign; - } - binaryOperatorExpression.Right = assign.Right.Detach(); - assign.Right = binaryOperatorExpression; - return assign; - } - } - - public override object VisitAssignmentExpression(AssignmentExpression assignment, object data) - { - base.VisitAssignmentExpression(assignment, data); - // Combine "x = x op y" into "x op= y" - BinaryOperatorExpression binary = assignment.Right as BinaryOperatorExpression; - if (binary != null && assignment.Operator == AssignmentOperatorType.Assign) { - if (CanConvertToCompoundAssignment(assignment.Left) && assignment.Left.IsMatch(binary.Left)) { - assignment.Operator = GetAssignmentOperatorForBinaryOperator(binary.Operator); - if (assignment.Operator != AssignmentOperatorType.Assign) { - // If we found a shorter operator, get rid of the BinaryOperatorExpression: - assignment.CopyAnnotationsFrom(binary); - assignment.Right = binary.Right; - assignment.AddAnnotation(new RestoreOriginalAssignOperatorAnnotation(binary)); - } - } - } - if (context.Settings.IntroduceIncrementAndDecrement && (assignment.Operator == AssignmentOperatorType.Add || assignment.Operator == AssignmentOperatorType.Subtract)) { - // detect increment/decrement - if (assignment.Right.IsMatch(new PrimitiveExpression(1))) { - // only if it's not a custom operator - if (assignment.Annotation<MethodReference>() == null) { - UnaryOperatorType type; - // When the parent is an expression statement, pre- or post-increment doesn't matter; - // so we can pick post-increment which is more commonly used (for (int i = 0; i < x; i++)) - if (assignment.Parent is ExpressionStatement) - type = (assignment.Operator == AssignmentOperatorType.Add) ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement; - else - type = (assignment.Operator == AssignmentOperatorType.Add) ? UnaryOperatorType.Increment : UnaryOperatorType.Decrement; - assignment.ReplaceWith(new UnaryOperatorExpression(type, assignment.Left.Detach()).CopyAnnotationsFrom(assignment)); - } - } - } - return null; - } - - public static AssignmentOperatorType GetAssignmentOperatorForBinaryOperator(BinaryOperatorType bop) - { - switch (bop) { - case BinaryOperatorType.Add: - return AssignmentOperatorType.Add; - case BinaryOperatorType.Subtract: - return AssignmentOperatorType.Subtract; - case BinaryOperatorType.Multiply: - return AssignmentOperatorType.Multiply; - case BinaryOperatorType.Divide: - return AssignmentOperatorType.Divide; - case BinaryOperatorType.Modulus: - return AssignmentOperatorType.Modulus; - case BinaryOperatorType.ShiftLeft: - return AssignmentOperatorType.ShiftLeft; - case BinaryOperatorType.ShiftRight: - return AssignmentOperatorType.ShiftRight; - case BinaryOperatorType.BitwiseAnd: - return AssignmentOperatorType.BitwiseAnd; - case BinaryOperatorType.BitwiseOr: - return AssignmentOperatorType.BitwiseOr; - case BinaryOperatorType.ExclusiveOr: - return AssignmentOperatorType.ExclusiveOr; - default: - return AssignmentOperatorType.Assign; - } - } - - static bool CanConvertToCompoundAssignment(Expression left) - { - MemberReferenceExpression mre = left as MemberReferenceExpression; - if (mre != null) - return IsWithoutSideEffects(mre.Target); - IndexerExpression ie = left as IndexerExpression; - if (ie != null) - return IsWithoutSideEffects(ie.Target) && ie.Arguments.All(IsWithoutSideEffects); - UnaryOperatorExpression uoe = left as UnaryOperatorExpression; - if (uoe != null && uoe.Operator == UnaryOperatorType.Dereference) - return IsWithoutSideEffects(uoe.Expression); - return IsWithoutSideEffects(left); - } - - static bool IsWithoutSideEffects(Expression left) - { - return left is ThisReferenceExpression || left is IdentifierExpression || left is TypeReferenceExpression || left is BaseReferenceExpression; - } - - static readonly Expression getMethodOrConstructorFromHandlePattern = - new TypePattern(typeof(MethodBase)).ToType().Invoke( - "GetMethodFromHandle", - new NamedNode("ldtokenNode", new LdTokenPattern("method")).ToExpression().Member("MethodHandle"), - new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle")) - ).CastTo(new Choice { - new TypePattern(typeof(MethodInfo)), - new TypePattern(typeof(ConstructorInfo)) - }); - - public override object VisitCastExpression(CastExpression castExpression, object data) - { - base.VisitCastExpression(castExpression, data); - // Handle methodof - Match m = getMethodOrConstructorFromHandlePattern.Match(castExpression); - if (m.Success) { - MethodReference method = m.Get<AstNode>("method").Single().Annotation<MethodReference>(); - if (m.Has("declaringType")) { - Expression newNode = m.Get<AstType>("declaringType").Single().Detach().Member(method.Name); - newNode = newNode.Invoke(method.Parameters.Select(p => new TypeReferenceExpression(AstBuilder.ConvertType(p.ParameterType, p)))); - newNode.AddAnnotation(method); - m.Get<AstNode>("method").Single().ReplaceWith(newNode); - } - castExpression.ReplaceWith(m.Get<AstNode>("ldtokenNode").Single()); - } - return null; - } - - void IAstTransform.Run(AstNode node) - { - node.AcceptVisitor(this, null); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs deleted file mode 100644 index 3091a10914..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs +++ /dev/null @@ -1,65 +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.Threading; -using ICSharpCode.NRefactory.CSharp; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - public interface IAstTransform - { - void Run(AstNode compilationUnit); - } - - public static class TransformationPipeline - { - public static IAstTransform[] CreatePipeline(DecompilerContext context) - { - return new IAstTransform[] { - new PushNegation(), - new DelegateConstruction(context), - new PatternStatementTransform(context), - new ReplaceMethodCallsWithOperators(context), - new IntroduceUnsafeModifier(), - new AddCheckedBlocks(), - new DeclareVariables(context), // should run after most transforms that modify statements - new ConvertConstructorCallIntoInitializer(), // must run after DeclareVariables - new DecimalConstantTransform(), - new IntroduceUsingDeclarations(context), - new IntroduceExtensionMethods(context), // must run after IntroduceUsingDeclarations - new IntroduceQueryExpressions(context), // must run after IntroduceExtensionMethods - new CombineQueryExpressions(context), - new FlattenSwitchBlocks(), - }; - } - - public static void RunTransformationsUntil(AstNode node, Predicate<IAstTransform> abortCondition, DecompilerContext context) - { - if (node == null) - return; - - foreach (var transform in CreatePipeline(context)) { - context.CancellationToken.ThrowIfCancellationRequested(); - if (abortCondition != null && abortCondition(transform)) - return; - transform.Run(node); - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs b/main/contrib/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs deleted file mode 100644 index f0f5ece532..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs +++ /dev/null @@ -1,526 +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.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.Ast -{ - public static class TypesHierarchyHelpers - { - public static bool IsBaseType(TypeDefinition baseType, TypeDefinition derivedType, bool resolveTypeArguments) - { - if (resolveTypeArguments) - return BaseTypes(derivedType).Any(t => t.Item == baseType); - else { - var comparableBaseType = baseType.Resolve(); - if (comparableBaseType == null) - return false; - while (derivedType.BaseType != null) { - var resolvedBaseType = derivedType.BaseType.Resolve(); - if (resolvedBaseType == null) - return false; - if (comparableBaseType == resolvedBaseType) - return true; - derivedType = resolvedBaseType; - } - return false; - } - } - - /// <summary> - /// Determines whether one method overrides or hides another method. - /// </summary> - /// <param name="parentMethod">The method declared in a base type.</param> - /// <param name="childMethod">The method declared in a derived type.</param> - /// <returns>true if <paramref name="childMethod"/> hides or overrides <paramref name="parentMethod"/>, - /// otherwise false.</returns> - public static bool IsBaseMethod(MethodDefinition parentMethod, MethodDefinition childMethod) - { - if (parentMethod == null) - throw new ArgumentNullException("parentMethod"); - if (childMethod == null) - throw new ArgumentNullException("childMethod"); - - if (parentMethod.Name != childMethod.Name) - return false; - - if (parentMethod.HasParameters || childMethod.HasParameters) - if (!parentMethod.HasParameters || !childMethod.HasParameters || parentMethod.Parameters.Count != childMethod.Parameters.Count) - return false; - - return FindBaseMethods(childMethod).Any(m => m == parentMethod);// || (parentMethod.HasGenericParameters && m.); - } - - /// <summary> - /// Determines whether a property overrides or hides another property. - /// </summary> - /// <param name="parentProperty">The property declared in a base type.</param> - /// <param name="childProperty">The property declared in a derived type.</param> - /// <returns>true if the <paramref name="childProperty"/> hides or overrides <paramref name="parentProperty"/>, - /// otherwise false.</returns> - public static bool IsBaseProperty(PropertyDefinition parentProperty, PropertyDefinition childProperty) - { - if (parentProperty == null) - throw new ArgumentNullException("parentProperty"); - if (childProperty == null) - throw new ArgumentNullException("childProperty"); - - if (parentProperty.Name != childProperty.Name) - return false; - - if (parentProperty.HasParameters || childProperty.HasParameters) - if (!parentProperty.HasParameters || !childProperty.HasParameters || parentProperty.Parameters.Count != childProperty.Parameters.Count) - return false; - - return FindBaseProperties(childProperty).Any(m => m == parentProperty); - } - - public static bool IsBaseEvent(EventDefinition parentEvent, EventDefinition childEvent) - { - if (parentEvent.Name != childEvent.Name) - return false; - - return FindBaseEvents(childEvent).Any(m => m == parentEvent); - } - - /// <summary> - /// Finds all methods from base types overridden or hidden by the specified method. - /// </summary> - /// <param name="method">The method which overrides or hides methods from base types.</param> - /// <returns>Methods overriden or hidden by the specified method.</returns> - public static IEnumerable<MethodDefinition> FindBaseMethods(MethodDefinition method) - { - if (method == null) - throw new ArgumentNullException("method"); - - var typeContext = CreateGenericContext(method.DeclaringType); - var gMethod = typeContext.ApplyTo(method); - - foreach (var baseType in BaseTypes(method.DeclaringType)) - foreach (var baseMethod in baseType.Item.Methods) - if (MatchMethod(baseType.ApplyTo(baseMethod), gMethod) && IsVisibleFromDerived(baseMethod, method.DeclaringType)) { - yield return baseMethod; - if (baseMethod.IsNewSlot == baseMethod.IsVirtual) - yield break; - } - } - - /// <summary> - /// Finds all properties from base types overridden or hidden by the specified property. - /// </summary> - /// <param name="property">The property which overrides or hides properties from base types.</param> - /// <returns>Properties overriden or hidden by the specified property.</returns> - public static IEnumerable<PropertyDefinition> FindBaseProperties(PropertyDefinition property) - { - if (property == null) - throw new ArgumentNullException("property"); - - if ((property.GetMethod ?? property.SetMethod).HasOverrides) - yield break; - - var typeContext = CreateGenericContext(property.DeclaringType); - var gProperty = typeContext.ApplyTo(property); - bool isIndexer = property.IsIndexer(); - - foreach (var baseType in BaseTypes(property.DeclaringType)) - foreach (var baseProperty in baseType.Item.Properties) - if (MatchProperty(baseType.ApplyTo(baseProperty), gProperty) - && IsVisibleFromDerived(baseProperty, property.DeclaringType)) { - if (isIndexer != baseProperty.IsIndexer()) - continue; - yield return baseProperty; - var anyPropertyAccessor = baseProperty.GetMethod ?? baseProperty.SetMethod; - if (anyPropertyAccessor.IsNewSlot == anyPropertyAccessor.IsVirtual) - yield break; - } - } - - public static IEnumerable<EventDefinition> FindBaseEvents(EventDefinition eventDef) - { - if (eventDef == null) - throw new ArgumentNullException("eventDef"); - - var typeContext = CreateGenericContext(eventDef.DeclaringType); - var gEvent = typeContext.ApplyTo(eventDef); - - foreach (var baseType in BaseTypes(eventDef.DeclaringType)) - foreach (var baseEvent in baseType.Item.Events) - if (MatchEvent(baseType.ApplyTo(baseEvent), gEvent) && IsVisibleFromDerived(baseEvent, eventDef.DeclaringType)) { - yield return baseEvent; - var anyEventAccessor = baseEvent.AddMethod ?? baseEvent.RemoveMethod; - if (anyEventAccessor.IsNewSlot == anyEventAccessor.IsVirtual) - yield break; - } - - } - - /// <summary> - /// Determinates whether member of the base type is visible from a derived type. - /// </summary> - /// <param name="baseMember">The member which visibility is checked.</param> - /// <param name="derivedType">The derived type.</param> - /// <returns>true if the member is visible from derived type, othewise false.</returns> - public static bool IsVisibleFromDerived(IMemberDefinition baseMember, TypeDefinition derivedType) - { - if (baseMember == null) - throw new ArgumentNullException("baseMember"); - if (derivedType == null) - throw new ArgumentNullException("derivedType"); - - MethodAttributes attrs = GetAccessAttributes(baseMember) & MethodAttributes.MemberAccessMask; - if (attrs == MethodAttributes.Private) - return false; - - if (baseMember.DeclaringType.Module == derivedType.Module) - return true; - - if (attrs == MethodAttributes.Assembly || attrs == MethodAttributes.FamANDAssem) { - var derivedTypeAsm = derivedType.Module.Assembly; - var asm = baseMember.DeclaringType.Module.Assembly; - - if (asm.HasCustomAttributes) { - var attributes = asm.CustomAttributes - .Where(attr => attr.AttributeType.FullName == "System.Runtime.CompilerServices.InternalsVisibleToAttribute"); - foreach (var attribute in attributes) { - string assemblyName = attribute.ConstructorArguments[0].Value as string; - assemblyName = assemblyName.Split(',')[0]; // strip off any public key info - if (assemblyName == derivedTypeAsm.Name.Name) - return true; - } - } - - return false; - } - - return true; - } - - private static MethodAttributes GetAccessAttributes(IMemberDefinition member) - { - var fld = member as FieldDefinition; - if (fld != null) - return (MethodAttributes)fld.Attributes; - - var method = member as MethodDefinition; - if (method != null) - return method.Attributes; - - var prop = member as PropertyDefinition; - if (prop != null) { - return (prop.GetMethod ?? prop.SetMethod).Attributes; - } - - var evnt = member as EventDefinition; - if (evnt != null) { - return (evnt.AddMethod ?? evnt.RemoveMethod).Attributes; - } - - var nestedType = member as TypeDefinition; - if (nestedType != null) { - if (nestedType.IsNestedPrivate) - return MethodAttributes.Private; - if (nestedType.IsNestedAssembly || nestedType.IsNestedFamilyAndAssembly) - return MethodAttributes.Assembly; - return MethodAttributes.Public; - } - - throw new NotSupportedException(); - } - - private static bool MatchMethod(GenericContext<MethodDefinition> candidate, GenericContext<MethodDefinition> method) - { - var mCandidate = candidate.Item; - var mMethod = method.Item; - if (mCandidate.Name != mMethod.Name) - return false; - - if (mCandidate.HasOverrides) - return false; - - if (mCandidate.IsSpecialName != method.Item.IsSpecialName) - return false; - - if (mCandidate.HasGenericParameters || mMethod.HasGenericParameters) { - if (!mCandidate.HasGenericParameters || !mMethod.HasGenericParameters || mCandidate.GenericParameters.Count != mMethod.GenericParameters.Count) - return false; - } - - if (mCandidate.HasParameters || mMethod.HasParameters) { - if (!mCandidate.HasParameters || !mMethod.HasParameters || mCandidate.Parameters.Count != mMethod.Parameters.Count) - return false; - - for (int index = 0; index < mCandidate.Parameters.Count; index++) { - if (!MatchParameters(candidate.ApplyTo(mCandidate.Parameters[index]), method.ApplyTo(mMethod.Parameters[index]))) - return false; - } - } - - return true; - } - - public static bool MatchInterfaceMethod(MethodDefinition candidate, MethodDefinition method, TypeReference interfaceContextType) - { - var candidateContext = CreateGenericContext(candidate.DeclaringType); - var gCandidate = candidateContext.ApplyTo(candidate); - - if (interfaceContextType is GenericInstanceType) { - var methodContext = new GenericContext<TypeDefinition>(interfaceContextType.Resolve(), ((GenericInstanceType)interfaceContextType).GenericArguments); - var gMethod = methodContext.ApplyTo(method); - return MatchMethod(gCandidate, gMethod); - } else { - var methodContext = CreateGenericContext(interfaceContextType.Resolve()); - var gMethod = candidateContext.ApplyTo(method); - return MatchMethod(gCandidate, gMethod); - } - } - - private static bool MatchProperty(GenericContext<PropertyDefinition> candidate, GenericContext<PropertyDefinition> property) - { - var mCandidate = candidate.Item; - var mProperty = property.Item; - if (mCandidate.Name != mProperty.Name) - return false; - - if ((mCandidate.GetMethod ?? mCandidate.SetMethod).HasOverrides) - return false; - - if (mCandidate.HasParameters || mProperty.HasParameters) { - if (!mCandidate.HasParameters || !mProperty.HasParameters || mCandidate.Parameters.Count != mProperty.Parameters.Count) - return false; - - for (int index = 0; index < mCandidate.Parameters.Count; index++) { - if (!MatchParameters(candidate.ApplyTo(mCandidate.Parameters[index]), property.ApplyTo(mProperty.Parameters[index]))) - return false; - } - } - - return true; - } - - private static bool MatchEvent(GenericContext<EventDefinition> candidate, GenericContext<EventDefinition> ev) - { - var mCandidate = candidate.Item; - var mEvent = ev.Item; - if (mCandidate.Name != mEvent.Name) - return false; - - if ((mCandidate.AddMethod ?? mCandidate.RemoveMethod).HasOverrides) - return false; - - if (!IsSameType(candidate.ResolveWithContext(mCandidate.EventType), ev.ResolveWithContext(mEvent.EventType))) - return false; - - return true; - } - - private static bool MatchParameters(GenericContext<ParameterDefinition> baseParameterType, GenericContext<ParameterDefinition> parameterType) - { - if (baseParameterType.Item.IsIn != parameterType.Item.IsIn || - baseParameterType.Item.IsOut != parameterType.Item.IsOut) - return false; - var baseParam = baseParameterType.ResolveWithContext(baseParameterType.Item.ParameterType); - var param = parameterType.ResolveWithContext(parameterType.Item.ParameterType); - return IsSameType(baseParam, param); - } - - private static bool IsSameType(TypeReference tr1, TypeReference tr2) - { - if (tr1 == tr2) - return true; - if (tr1 == null || tr2 == null) - return false; - - if (tr1.GetType() != tr2.GetType()) - return false; - - if (tr1.Name == tr2.Name && tr1.FullName == tr2.FullName) - return true; - - return false; - } - - private static IEnumerable<GenericContext<TypeDefinition>> BaseTypes(TypeDefinition type) - { - return BaseTypes(CreateGenericContext(type)); - } - - private static IEnumerable<GenericContext<TypeDefinition>> BaseTypes(GenericContext<TypeDefinition> type) - { - while (type.Item.BaseType != null) { - var baseType = type.Item.BaseType; - var genericBaseType = baseType as GenericInstanceType; - if (genericBaseType != null) { - type = new GenericContext<TypeDefinition>(genericBaseType.ResolveOrThrow(), - genericBaseType.GenericArguments.Select(t => type.ResolveWithContext(t))); - } else - type = new GenericContext<TypeDefinition>(baseType.ResolveOrThrow()); - yield return type; - } - } - - private static GenericContext<TypeDefinition> CreateGenericContext(TypeDefinition type) - { - return type.HasGenericParameters - ? new GenericContext<TypeDefinition>(type, type.GenericParameters) - : new GenericContext<TypeDefinition>(type); - } - - struct GenericContext<T> where T : class - { - private static readonly ReadOnlyCollection<TypeReference> Empty = new ReadOnlyCollection<TypeReference>(new List<TypeReference>()); - private static readonly GenericParameter UnresolvedGenericTypeParameter = - new DummyGenericParameterProvider(false).DummyParameter; - private static readonly GenericParameter UnresolvedGenericMethodParameter = - new DummyGenericParameterProvider(true).DummyParameter; - - public readonly T Item; - public readonly ReadOnlyCollection<TypeReference> TypeArguments; - - public GenericContext(T item) - { - if (item == null) - throw new ArgumentNullException("item"); - - Item = item; - TypeArguments = Empty; - } - - public GenericContext(T item, IEnumerable<TypeReference> typeArguments) - { - if (item == null) - throw new ArgumentNullException("item"); - - Item = item; - var list = new List<TypeReference>(); - foreach (var arg in typeArguments) { - var resolved = arg != null ? arg.Resolve() : arg; - list.Add(resolved != null ? resolved : arg); - } - TypeArguments = new ReadOnlyCollection<TypeReference>(list); - } - - private GenericContext(T item, ReadOnlyCollection<TypeReference> typeArguments) - { - Item = item; - TypeArguments = typeArguments; - } - - public TypeReference ResolveWithContext(TypeReference type) - { - var genericParameter = type as GenericParameter; - if (genericParameter != null) - if (genericParameter.Owner.GenericParameterType == GenericParameterType.Type) - return this.TypeArguments[genericParameter.Position]; - else - return genericParameter.Owner.GenericParameterType == GenericParameterType.Type - ? UnresolvedGenericTypeParameter : UnresolvedGenericMethodParameter; - var typeSpecification = type as TypeSpecification; - if (typeSpecification != null) { - var resolvedElementType = ResolveWithContext(typeSpecification.ElementType); - return ReplaceElementType(typeSpecification, resolvedElementType); - } - return type.ResolveOrThrow(); - } - - private TypeReference ReplaceElementType(TypeSpecification ts, TypeReference newElementType) - { - var arrayType = ts as ArrayType; - if (arrayType != null) { - if (newElementType == arrayType.ElementType) - return arrayType; - var newArrayType = new ArrayType(newElementType, arrayType.Rank); - for (int dimension = 0; dimension < arrayType.Rank; dimension++) - newArrayType.Dimensions[dimension] = arrayType.Dimensions[dimension]; - return newArrayType; - } - var byReferenceType = ts as ByReferenceType; - if (byReferenceType != null) { - return new ByReferenceType(newElementType); - } - // TODO: should we throw an exception instead calling Resolve method? - return ts.ResolveOrThrow(); - } - - public GenericContext<T2> ApplyTo<T2>(T2 item) where T2 : class - { - return new GenericContext<T2>(item, this.TypeArguments); - } - - private class DummyGenericParameterProvider : IGenericParameterProvider - { - readonly Mono.Cecil.GenericParameterType type; - readonly Mono.Collections.Generic.Collection<GenericParameter> parameters; - - public DummyGenericParameterProvider(bool methodTypeParameter) - { - type = methodTypeParameter ? Mono.Cecil.GenericParameterType.Method : - Mono.Cecil.GenericParameterType.Type; - parameters = new Mono.Collections.Generic.Collection<GenericParameter>(1); - parameters.Add(new GenericParameter(this)); - } - - public GenericParameter DummyParameter - { - get { return parameters[0]; } - } - - bool IGenericParameterProvider.HasGenericParameters - { - get { throw new NotImplementedException(); } - } - - bool IGenericParameterProvider.IsDefinition - { - get { throw new NotImplementedException(); } - } - - ModuleDefinition IGenericParameterProvider.Module - { - get { throw new NotImplementedException(); } - } - - Mono.Collections.Generic.Collection<GenericParameter> IGenericParameterProvider.GenericParameters - { - get { return parameters; } - } - - GenericParameterType IGenericParameterProvider.GenericParameterType - { - get { return type; } - } - - MetadataToken IMetadataTokenProvider.MetadataToken - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/CecilExtensions.cs b/main/contrib/ICSharpCode.Decompiler/CecilExtensions.cs deleted file mode 100644 index 0c7cb1e409..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/CecilExtensions.cs +++ /dev/null @@ -1,374 +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.Collections.Generic; -using System.Linq; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler -{ - /// <summary> - /// Cecil helper methods. - /// </summary> - public static class CecilExtensions - { - #region GetPushDelta / GetPopDelta - public static int GetPushDelta(this Instruction instruction) - { - OpCode code = instruction.OpCode; - switch (code.StackBehaviourPush) { - case StackBehaviour.Push0: - return 0; - - case StackBehaviour.Push1: - case StackBehaviour.Pushi: - case StackBehaviour.Pushi8: - case StackBehaviour.Pushr4: - case StackBehaviour.Pushr8: - case StackBehaviour.Pushref: - return 1; - - case StackBehaviour.Push1_push1: - return 2; - - case StackBehaviour.Varpush: - if (code.FlowControl != FlowControl.Call) - break; - - IMethodSignature method = (IMethodSignature) instruction.Operand; - return IsVoid (method.ReturnType) ? 0 : 1; - } - - throw new NotSupportedException (); - } - - public static int? GetPopDelta(this Instruction instruction, MethodDefinition methodDef) - { - OpCode code = instruction.OpCode; - switch (code.StackBehaviourPop) { - case StackBehaviour.Pop0: - return 0; - case StackBehaviour.Popi: - case StackBehaviour.Popref: - case StackBehaviour.Pop1: - return 1; - - case StackBehaviour.Pop1_pop1: - case StackBehaviour.Popi_pop1: - case StackBehaviour.Popi_popi: - case StackBehaviour.Popi_popi8: - case StackBehaviour.Popi_popr4: - case StackBehaviour.Popi_popr8: - case StackBehaviour.Popref_pop1: - case StackBehaviour.Popref_popi: - return 2; - - case StackBehaviour.Popi_popi_popi: - case StackBehaviour.Popref_popi_popi: - case StackBehaviour.Popref_popi_popi8: - case StackBehaviour.Popref_popi_popr4: - case StackBehaviour.Popref_popi_popr8: - case StackBehaviour.Popref_popi_popref: - return 3; - - case StackBehaviour.PopAll: - return null; - - case StackBehaviour.Varpop: - if (code == OpCodes.Ret) - return methodDef.ReturnType.IsVoid() ? 0 : 1; - - if (code.FlowControl != FlowControl.Call) - break; - - IMethodSignature method = (IMethodSignature) instruction.Operand; - int count = method.HasParameters ? method.Parameters.Count : 0; - if (method.HasThis && code != OpCodes.Newobj) - ++count; - if (code == OpCodes.Calli) - ++count; // calli takes a function pointer in additional to the normal args - - return count; - } - - throw new NotSupportedException (); - } - - public static bool IsVoid(this TypeReference type) - { - while (type is OptionalModifierType || type is RequiredModifierType) - type = ((TypeSpecification)type).ElementType; - return type.MetadataType == MetadataType.Void; - } - - public static bool IsValueTypeOrVoid(this TypeReference type) - { - while (type is OptionalModifierType || type is RequiredModifierType) - type = ((TypeSpecification)type).ElementType; - if (type is ArrayType) - return false; - return type.IsValueType || type.IsVoid(); - } - - /// <summary> - /// checks if the given TypeReference is one of the following types: - /// [sbyte, short, int, long, IntPtr] - /// </summary> - public static bool IsSignedIntegralType(this TypeReference type) - { - return type.MetadataType == MetadataType.SByte || - type.MetadataType == MetadataType.Int16 || - type.MetadataType == MetadataType.Int32 || - type.MetadataType == MetadataType.Int64 || - type.MetadataType == MetadataType.IntPtr; - } - - /// <summary> - /// checks if the given value is a numeric zero-value. - /// NOTE that this only works for types: [sbyte, short, int, long, IntPtr, byte, ushort, uint, ulong, float, double and decimal] - /// </summary> - public static bool IsZero(this object value) - { - return value.Equals((sbyte)0) || - value.Equals((short)0) || - value.Equals(0) || - value.Equals(0L) || - value.Equals(IntPtr.Zero) || - value.Equals((byte)0) || - value.Equals((ushort)0) || - value.Equals(0u) || - value.Equals(0UL) || - value.Equals(0.0f) || - value.Equals(0.0) || - value.Equals((decimal)0); - - } - - #endregion - - /// <summary> - /// Gets the (exclusive) end offset of this instruction. - /// </summary> - public static int GetEndOffset(this Instruction inst) - { - if (inst == null) - throw new ArgumentNullException("inst"); - return inst.Offset + inst.GetSize(); - } - - public static string OffsetToString(int offset) - { - return string.Format("IL_{0:x4}", offset); - } - - public static HashSet<MethodDefinition> GetAccessorMethods(this TypeDefinition type) - { - HashSet<MethodDefinition> accessorMethods = new HashSet<MethodDefinition>(); - foreach (var property in type.Properties) { - accessorMethods.Add(property.GetMethod); - accessorMethods.Add(property.SetMethod); - if (property.HasOtherMethods) { - foreach (var m in property.OtherMethods) - accessorMethods.Add(m); - } - } - foreach (EventDefinition ev in type.Events) { - accessorMethods.Add(ev.AddMethod); - accessorMethods.Add(ev.RemoveMethod); - accessorMethods.Add(ev.InvokeMethod); - if (ev.HasOtherMethods) { - foreach (var m in ev.OtherMethods) - accessorMethods.Add(m); - } - } - return accessorMethods; - } - - public static TypeDefinition ResolveWithinSameModule(this TypeReference type) - { - if (type != null && type.GetElementType().Module == type.Module) - return type.Resolve(); - else - return null; - } - - public static FieldDefinition ResolveWithinSameModule(this FieldReference field) - { - if (field != null && field.DeclaringType.GetElementType().Module == field.Module) - return field.Resolve(); - else - return null; - } - - public static MethodDefinition ResolveWithinSameModule(this MethodReference method) - { - if (method != null && method.DeclaringType.GetElementType().Module == method.Module) - return method.Resolve(); - else - return null; - } - - [Obsolete("throwing exceptions is considered a bug")] - public static TypeDefinition ResolveOrThrow(this TypeReference typeReference) - { - var resolved = typeReference.Resolve(); - if (resolved == null) - throw new ReferenceResolvingException(); - return resolved; - } - - public static bool IsCompilerGenerated(this ICustomAttributeProvider provider) - { - if (provider != null && provider.HasCustomAttributes) { - foreach (CustomAttribute a in provider.CustomAttributes) { - if (a.AttributeType.FullName == "System.Runtime.CompilerServices.CompilerGeneratedAttribute") - return true; - } - } - return false; - } - - public static bool IsCompilerGeneratedOrIsInCompilerGeneratedClass(this IMemberDefinition member) - { - if (member == null) - return false; - if (member.IsCompilerGenerated()) - return true; - return IsCompilerGeneratedOrIsInCompilerGeneratedClass(member.DeclaringType); - } - - public static TypeReference GetEnumUnderlyingType(this TypeDefinition type) - { - if (!type.IsEnum) - throw new ArgumentException("Type must be an enum", "type"); - - var fields = type.Fields; - - for (int i = 0; i < fields.Count; i++) - { - var field = fields[i]; - if (!field.IsStatic) - return field.FieldType; - } - - throw new NotSupportedException(); - } - - public static bool IsAnonymousType(this TypeReference type) - { - if (type == null) - return false; - if (string.IsNullOrEmpty(type.Namespace) && type.HasGeneratedName() && (type.Name.Contains("AnonType") || type.Name.Contains("AnonymousType"))) { - TypeDefinition td = type.Resolve(); - return td != null && td.IsCompilerGenerated(); - } - return false; - } - - public static bool HasGeneratedName(this MemberReference member) - { - return member.Name.StartsWith("<", StringComparison.Ordinal); - } - - public static bool ContainsAnonymousType(this TypeReference type) - { - GenericInstanceType git = type as GenericInstanceType; - if (git != null) { - if (IsAnonymousType(git)) - return true; - for (int i = 0; i < git.GenericArguments.Count; i++) { - if (git.GenericArguments[i].ContainsAnonymousType()) - return true; - } - return false; - } - TypeSpecification typeSpec = type as TypeSpecification; - if (typeSpec != null) - return typeSpec.ElementType.ContainsAnonymousType(); - else - return false; - } - - public static string GetDefaultMemberName(this TypeDefinition type) - { - CustomAttribute attr; - return type.GetDefaultMemberName(out attr); - } - - public static string GetDefaultMemberName(this TypeDefinition type, out CustomAttribute defaultMemberAttribute) - { - if (type.HasCustomAttributes) - foreach (CustomAttribute ca in type.CustomAttributes) - if (ca.Constructor.DeclaringType.Name == "DefaultMemberAttribute" && ca.Constructor.DeclaringType.Namespace == "System.Reflection" - && ca.Constructor.FullName == @"System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)") { - defaultMemberAttribute = ca; - return ca.ConstructorArguments[0].Value as string; - } - defaultMemberAttribute = null; - return null; - } - - public static bool IsIndexer(this PropertyDefinition property) - { - CustomAttribute attr; - return property.IsIndexer(out attr); - } - - public static bool IsIndexer(this PropertyDefinition property, out CustomAttribute defaultMemberAttribute) - { - defaultMemberAttribute = null; - if (property.HasParameters) { - var accessor = property.GetMethod ?? property.SetMethod; - PropertyDefinition basePropDef = property; - if (accessor.HasOverrides) { - // if the property is explicitly implementing an interface, look up the property in the interface: - MethodDefinition baseAccessor = accessor.Overrides.First().Resolve(); - if (baseAccessor != null) { - foreach (PropertyDefinition baseProp in baseAccessor.DeclaringType.Properties) { - if (baseProp.GetMethod == baseAccessor || baseProp.SetMethod == baseAccessor) { - basePropDef = baseProp; - break; - } - } - } else - return false; - } - CustomAttribute attr; - var defaultMemberName = basePropDef.DeclaringType.GetDefaultMemberName(out attr); - if (defaultMemberName == basePropDef.Name) { - defaultMemberAttribute = attr; - return true; - } - } - return false; - } - - public static bool IsDelegate(this TypeDefinition type) - { - if (type.BaseType != null && type.BaseType.Namespace == "System") { - if (type.BaseType.Name == "MulticastDelegate") - return true; - if (type.BaseType.Name == "Delegate" && type.Name != "MulticastDelegate") - return true; - } - return false; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/CodeMappings.cs b/main/contrib/ICSharpCode.Decompiler/CodeMappings.cs deleted file mode 100644 index b2dcab8621..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/CodeMappings.cs +++ /dev/null @@ -1,57 +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.Collections.Generic; -using System.Linq; -using ICSharpCode.Decompiler.ILAst; -using ICSharpCode.NRefactory; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler -{ - /// <summary> Maps method's source code to IL </summary> - public class MethodDebugSymbols - { - public MethodDefinition CecilMethod { get; set; } - public List<ILVariable> LocalVariables { get; set; } - public List<SequencePoint> SequencePoints { get; set; } - public TextLocation StartLocation { get; set; } - public TextLocation EndLocation { get; set; } - - public MethodDebugSymbols(MethodDefinition methodDef) - { - this.CecilMethod = methodDef; - this.LocalVariables = new List<ILVariable>(); - this.SequencePoints = new List<SequencePoint>(); - } - } - - public class SequencePoint - { - public ILRange[] ILRanges { get; set; } - public TextLocation StartLocation { get; set; } - public TextLocation EndLocation { get; set; } - public int ILOffset { get { return this.ILRanges[0].From; } } - - public override string ToString() - { - return string.Join(" ", this.ILRanges) + " " + this.StartLocation + "-" + this.EndLocation; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/DecompilerException.cs b/main/contrib/ICSharpCode.Decompiler/DecompilerException.cs deleted file mode 100644 index 5f5f022fc5..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/DecompilerException.cs +++ /dev/null @@ -1,42 +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.Runtime.Serialization; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler -{ - /// <summary> - /// Description of DecompilerException. - /// </summary> - public class DecompilerException : Exception, ISerializable - { - public MethodDefinition DecompiledMethod { get; set; } - - public DecompilerException(MethodDefinition decompiledMethod, Exception innerException) - : base("Error decompiling " + decompiledMethod.FullName + Environment.NewLine, innerException) - { - } - - // This constructor is needed for serialization. - protected DecompilerException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } - } -}
\ No newline at end of file diff --git a/main/contrib/ICSharpCode.Decompiler/DecompilerSettings.cs b/main/contrib/ICSharpCode.Decompiler/DecompilerSettings.cs deleted file mode 100644 index a354291ed2..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/DecompilerSettings.cs +++ /dev/null @@ -1,368 +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.ComponentModel; -using ICSharpCode.NRefactory.CSharp; - -namespace ICSharpCode.Decompiler -{ - /// <summary> - /// Settings for the decompiler. - /// </summary> - public class DecompilerSettings : INotifyPropertyChanged - { - bool anonymousMethods = true; - - /// <summary> - /// Decompile anonymous methods/lambdas. - /// </summary> - public bool AnonymousMethods { - get { return anonymousMethods; } - set { - if (anonymousMethods != value) { - anonymousMethods = value; - OnPropertyChanged("AnonymousMethods"); - } - } - } - - bool expressionTrees = true; - - /// <summary> - /// Decompile expression trees. - /// </summary> - public bool ExpressionTrees { - get { return expressionTrees; } - set { - if (expressionTrees != value) { - expressionTrees = value; - OnPropertyChanged("ExpressionTrees"); - } - } - } - - bool yieldReturn = true; - - /// <summary> - /// Decompile enumerators. - /// </summary> - public bool YieldReturn { - get { return yieldReturn; } - set { - if (yieldReturn != value) { - yieldReturn = value; - OnPropertyChanged("YieldReturn"); - } - } - } - - bool asyncAwait = true; - - /// <summary> - /// Decompile async methods. - /// </summary> - public bool AsyncAwait { - get { return asyncAwait; } - set { - if (asyncAwait != value) { - asyncAwait = value; - OnPropertyChanged("AsyncAwait"); - } - } - } - - bool automaticProperties = true; - - /// <summary> - /// Decompile automatic properties - /// </summary> - public bool AutomaticProperties { - get { return automaticProperties; } - set { - if (automaticProperties != value) { - automaticProperties = value; - OnPropertyChanged("AutomaticProperties"); - } - } - } - - bool automaticEvents = true; - - /// <summary> - /// Decompile automatic events - /// </summary> - public bool AutomaticEvents { - get { return automaticEvents; } - set { - if (automaticEvents != value) { - automaticEvents = value; - OnPropertyChanged("AutomaticEvents"); - } - } - } - - bool usingStatement = true; - - /// <summary> - /// Decompile using statements. - /// </summary> - public bool UsingStatement { - get { return usingStatement; } - set { - if (usingStatement != value) { - usingStatement = value; - OnPropertyChanged("UsingStatement"); - } - } - } - - bool forEachStatement = true; - - /// <summary> - /// Decompile foreach statements. - /// </summary> - public bool ForEachStatement { - get { return forEachStatement; } - set { - if (forEachStatement != value) { - forEachStatement = value; - OnPropertyChanged("ForEachStatement"); - } - } - } - - bool lockStatement = true; - - /// <summary> - /// Decompile lock statements. - /// </summary> - public bool LockStatement { - get { return lockStatement; } - set { - if (lockStatement != value) { - lockStatement = value; - OnPropertyChanged("LockStatement"); - } - } - } - - bool switchStatementOnString = true; - - public bool SwitchStatementOnString { - get { return switchStatementOnString; } - set { - if (switchStatementOnString != value) { - switchStatementOnString = value; - OnPropertyChanged("SwitchStatementOnString"); - } - } - } - - bool usingDeclarations = true; - - public bool UsingDeclarations { - get { return usingDeclarations; } - set { - if (usingDeclarations != value) { - usingDeclarations = value; - OnPropertyChanged("UsingDeclarations"); - } - } - } - - bool queryExpressions = true; - - public bool QueryExpressions { - get { return queryExpressions; } - set { - if (queryExpressions != value) { - queryExpressions = value; - OnPropertyChanged("QueryExpressions"); - } - } - } - - bool fullyQualifyAmbiguousTypeNames = true; - - public bool FullyQualifyAmbiguousTypeNames { - get { return fullyQualifyAmbiguousTypeNames; } - set { - if (fullyQualifyAmbiguousTypeNames != value) { - fullyQualifyAmbiguousTypeNames = value; - OnPropertyChanged("FullyQualifyAmbiguousTypeNames"); - } - } - } - - bool useDebugSymbols = true; - - /// <summary> - /// Gets/Sets whether to use variable names from debug symbols, if available. - /// </summary> - public bool UseDebugSymbols { - get { return useDebugSymbols; } - set { - if (useDebugSymbols != value) { - useDebugSymbols = value; - OnPropertyChanged("UseDebugSymbols"); - } - } - } - - bool objectCollectionInitializers = true; - - /// <summary> - /// Gets/Sets whether to use C# 3.0 object/collection initializers - /// </summary> - public bool ObjectOrCollectionInitializers { - get { return objectCollectionInitializers; } - set { - if (objectCollectionInitializers != value) { - objectCollectionInitializers = value; - OnPropertyChanged("ObjectCollectionInitializers"); - } - } - } - - bool showXmlDocumentation = true; - - /// <summary> - /// Gets/Sets whether to include XML documentation comments in the decompiled code - /// </summary> - public bool ShowXmlDocumentation { - get { return showXmlDocumentation; } - set { - if (showXmlDocumentation != value) { - showXmlDocumentation = value; - OnPropertyChanged("ShowXmlDocumentation"); - } - } - } - - bool foldBraces = false; - - public bool FoldBraces { - get { return foldBraces; } - set { - if (foldBraces != value) { - foldBraces = value; - OnPropertyChanged("FoldBraces"); - } - } - } - - bool hideNonPublicMembers = false; - - public bool HideNonPublicMembers { - get { return hideNonPublicMembers; } - set { - if (hideNonPublicMembers != value) { - hideNonPublicMembers = value; - OnPropertyChanged("HideNonPublicMembers"); - } - } - } - - #region Options to aid VB decompilation - bool introduceIncrementAndDecrement = true; - - /// <summary> - /// Gets/Sets whether to use increment and decrement operators - /// </summary> - public bool IntroduceIncrementAndDecrement { - get { return introduceIncrementAndDecrement; } - set { - if (introduceIncrementAndDecrement != value) { - introduceIncrementAndDecrement = value; - OnPropertyChanged("IntroduceIncrementAndDecrement"); - } - } - } - - bool makeAssignmentExpressions = true; - - /// <summary> - /// Gets/Sets whether to use assignment expressions such as in while ((count = Do()) != 0) ; - /// </summary> - public bool MakeAssignmentExpressions { - get { return makeAssignmentExpressions; } - set { - if (makeAssignmentExpressions != value) { - makeAssignmentExpressions = value; - OnPropertyChanged("MakeAssignmentExpressions"); - } - } - } - - bool alwaysGenerateExceptionVariableForCatchBlocks = false; - - /// <summary> - /// Gets/Sets whether to always generate exception variables in catch blocks - /// </summary> - public bool AlwaysGenerateExceptionVariableForCatchBlocks { - get { return alwaysGenerateExceptionVariableForCatchBlocks; } - set { - if (alwaysGenerateExceptionVariableForCatchBlocks != value) { - alwaysGenerateExceptionVariableForCatchBlocks = value; - OnPropertyChanged("AlwaysGenerateExceptionVariableForCatchBlocks"); - } - } - } - #endregion - - CSharpFormattingOptions csharpFormattingOptions; - - public CSharpFormattingOptions CSharpFormattingOptions { - get { - if (csharpFormattingOptions == null) { - csharpFormattingOptions = FormattingOptionsFactory.CreateAllman(); - csharpFormattingOptions.IndentSwitchBody = false; - csharpFormattingOptions.ArrayInitializerWrapping = Wrapping.WrapAlways; - } - return csharpFormattingOptions; - } - set { - if (value == null) - throw new ArgumentNullException(); - if (csharpFormattingOptions != value) { - csharpFormattingOptions = value; - OnPropertyChanged("CSharpFormattingOptions"); - } - } - } - - public event PropertyChangedEventHandler PropertyChanged; - - protected virtual void OnPropertyChanged(string propertyName) - { - if (PropertyChanged != null) { - PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); - } - } - - public DecompilerSettings Clone() - { - DecompilerSettings settings = (DecompilerSettings)MemberwiseClone(); - if (csharpFormattingOptions != null) - settings.csharpFormattingOptions = csharpFormattingOptions.Clone(); - settings.PropertyChanged = null; - return settings; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs b/main/contrib/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs deleted file mode 100644 index bf7be8cb96..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs +++ /dev/null @@ -1,447 +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.Collections.Generic; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.Disassembler -{ - public enum ILNameSyntax - { - /// <summary> - /// class/valuetype + TypeName (built-in types use keyword syntax) - /// </summary> - Signature, - /// <summary> - /// Like signature, but always refers to type parameters using their position - /// </summary> - SignatureNoNamedTypeParameters, - /// <summary> - /// [assembly]Full.Type.Name (even for built-in types) - /// </summary> - TypeName, - /// <summary> - /// Name (but built-in types use keyword syntax) - /// </summary> - ShortTypeName - } - - public static class DisassemblerHelpers - { - public static void WriteOffsetReference(ITextOutput writer, Instruction instruction) - { - writer.WriteReference(CecilExtensions.OffsetToString(instruction.Offset), instruction); - } - - public static void WriteTo(this ExceptionHandler exceptionHandler, ITextOutput writer) - { - writer.Write("Try "); - WriteOffsetReference(writer, exceptionHandler.TryStart); - writer.Write('-'); - WriteOffsetReference(writer, exceptionHandler.TryEnd); - writer.Write(' '); - writer.Write(exceptionHandler.HandlerType.ToString()); - if (exceptionHandler.FilterStart != null) { - writer.Write(' '); - WriteOffsetReference(writer, exceptionHandler.FilterStart); - writer.Write(" handler "); - } - if (exceptionHandler.CatchType != null) { - writer.Write(' '); - exceptionHandler.CatchType.WriteTo(writer); - } - writer.Write(' '); - WriteOffsetReference(writer, exceptionHandler.HandlerStart); - writer.Write('-'); - WriteOffsetReference(writer, exceptionHandler.HandlerEnd); - } - - public static void WriteTo(this Instruction instruction, ITextOutput writer) - { - writer.WriteDefinition(CecilExtensions.OffsetToString(instruction.Offset), instruction); - writer.Write(": "); - writer.WriteReference(instruction.OpCode.Name, instruction.OpCode); - if (instruction.Operand != null) { - writer.Write(' '); - if (instruction.OpCode == OpCodes.Ldtoken) { - if (instruction.Operand is MethodReference) - writer.Write("method "); - else if (instruction.Operand is FieldReference) - writer.Write("field "); - } - WriteOperand(writer, instruction.Operand); - } - } - - static void WriteLabelList(ITextOutput writer, Instruction[] instructions) - { - writer.Write("("); - for(int i = 0; i < instructions.Length; i++) { - if(i != 0) writer.Write(", "); - WriteOffsetReference(writer, instructions[i]); - } - writer.Write(")"); - } - - static string ToInvariantCultureString(object value) - { - IConvertible convertible = value as IConvertible; - return(null != convertible) - ? convertible.ToString(System.Globalization.CultureInfo.InvariantCulture) - : value.ToString(); - } - - public static void WriteTo(this MethodReference method, ITextOutput writer) - { - if (method.ExplicitThis) { - writer.Write("instance explicit "); - } - else if (method.HasThis) { - writer.Write("instance "); - } - method.ReturnType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters); - writer.Write(' '); - if (method.DeclaringType != null) { - method.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName); - writer.Write("::"); - } - MethodDefinition md = method as MethodDefinition; - if (md != null && md.IsCompilerControlled) { - writer.WriteReference(Escape(method.Name + "$PST" + method.MetadataToken.ToInt32().ToString("X8")), method); - } else { - writer.WriteReference(Escape(method.Name), method); - } - GenericInstanceMethod gim = method as GenericInstanceMethod; - if (gim != null) { - writer.Write('<'); - for (int i = 0; i < gim.GenericArguments.Count; i++) { - if (i > 0) - writer.Write(", "); - gim.GenericArguments[i].WriteTo(writer); - } - writer.Write('>'); - } - writer.Write("("); - var parameters = method.Parameters; - for(int i = 0; i < parameters.Count; ++i) { - if (i > 0) writer.Write(", "); - parameters[i].ParameterType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters); - } - writer.Write(")"); - } - - static void WriteTo(this FieldReference field, ITextOutput writer) - { - field.FieldType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters); - writer.Write(' '); - field.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName); - writer.Write("::"); - writer.WriteReference(Escape(field.Name), field); - } - - static bool IsValidIdentifierCharacter(char c) - { - return c == '_' || c == '$' || c == '@' || c == '?' || c == '`'; - } - - static bool IsValidIdentifier(string identifier) - { - if (string.IsNullOrEmpty(identifier)) - return false; - if (!(char.IsLetter(identifier[0]) || IsValidIdentifierCharacter(identifier[0]))) { - // As a special case, .ctor and .cctor are valid despite starting with a dot - return identifier == ".ctor" || identifier == ".cctor"; - } - for (int i = 1; i < identifier.Length; i++) { - if (!(char.IsLetterOrDigit(identifier[i]) || IsValidIdentifierCharacter(identifier[i]) || identifier[i] == '.')) - return false; - } - return true; - } - - static readonly HashSet<string> ilKeywords = BuildKeywordList( - "abstract", "algorithm", "alignment", "ansi", "any", "arglist", - "array", "as", "assembly", "assert", "at", "auto", "autochar", "beforefieldinit", - "blob", "blob_object", "bool", "brnull", "brnull.s", "brzero", "brzero.s", "bstr", - "bytearray", "byvalstr", "callmostderived", "carray", "catch", "cdecl", "cf", - "char", "cil", "class", "clsid", "const", "currency", "custom", "date", "decimal", - "default", "demand", "deny", "endmac", "enum", "error", "explicit", "extends", "extern", - "false", "famandassem", "family", "famorassem", "fastcall", "fault", "field", "filetime", - "filter", "final", "finally", "fixed", "float", "float32", "float64", "forwardref", - "fromunmanaged", "handler", "hidebysig", "hresult", "idispatch", "il", "illegal", - "implements", "implicitcom", "implicitres", "import", "in", "inheritcheck", "init", - "initonly", "instance", "int", "int16", "int32", "int64", "int8", "interface", "internalcall", - "iunknown", "lasterr", "lcid", "linkcheck", "literal", "localloc", "lpstr", "lpstruct", "lptstr", - "lpvoid", "lpwstr", "managed", "marshal", "method", "modopt", "modreq", "native", "nested", - "newslot", "noappdomain", "noinlining", "nomachine", "nomangle", "nometadata", "noncasdemand", - "noncasinheritance", "noncaslinkdemand", "noprocess", "not", "not_in_gc_heap", "notremotable", - "notserialized", "null", "nullref", "object", "objectref", "opt", "optil", "out", - "permitonly", "pinned", "pinvokeimpl", "prefix1", "prefix2", "prefix3", "prefix4", "prefix5", "prefix6", - "prefix7", "prefixref", "prejitdeny", "prejitgrant", "preservesig", "private", "privatescope", "protected", - "public", "record", "refany", "reqmin", "reqopt", "reqrefuse", "reqsecobj", "request", "retval", - "rtspecialname", "runtime", "safearray", "sealed", "sequential", "serializable", "special", "specialname", - "static", "stdcall", "storage", "stored_object", "stream", "streamed_object", "string", "struct", - "synchronized", "syschar", "sysstring", "tbstr", "thiscall", "tls", "to", "true", "typedref", - "unicode", "unmanaged", "unmanagedexp", "unsigned", "unused", "userdefined", "value", "valuetype", - "vararg", "variant", "vector", "virtual", "void", "wchar", "winapi", "with", "wrapper", - - // These are not listed as keywords in spec, but ILAsm treats them as such - "property", "type", "flags", "callconv", "strict" - ); - - static HashSet<string> BuildKeywordList(params string[] keywords) - { - HashSet<string> s = new HashSet<string>(keywords); - foreach (var field in typeof(OpCodes).GetFields()) { - s.Add(((OpCode)field.GetValue(null)).Name); - } - return s; - } - - public static string Escape(string identifier) - { - if (IsValidIdentifier(identifier) && !ilKeywords.Contains(identifier)) { - return identifier; - } else { - // The ECMA specification says that ' inside SQString should be ecaped using an octal escape sequence, - // but we follow Microsoft's ILDasm and use \'. - return "'" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(identifier).Replace("'", "\\'") + "'"; - } - } - - public static void WriteTo(this TypeReference type, ITextOutput writer, ILNameSyntax syntax = ILNameSyntax.Signature) - { - ILNameSyntax syntaxForElementTypes = syntax == ILNameSyntax.SignatureNoNamedTypeParameters ? syntax : ILNameSyntax.Signature; - if (type is PinnedType) { - ((PinnedType)type).ElementType.WriteTo(writer, syntaxForElementTypes); - writer.Write(" pinned"); - } else if (type is ArrayType) { - ArrayType at = (ArrayType)type; - at.ElementType.WriteTo(writer, syntaxForElementTypes); - writer.Write('['); - writer.Write(string.Join(", ", at.Dimensions)); - writer.Write(']'); - } else if (type is GenericParameter) { - writer.Write('!'); - if (((GenericParameter)type).Owner.GenericParameterType == GenericParameterType.Method) - writer.Write('!'); - if (string.IsNullOrEmpty(type.Name) || type.Name[0] == '!' || syntax == ILNameSyntax.SignatureNoNamedTypeParameters) - writer.Write(((GenericParameter)type).Position.ToString()); - else - writer.Write(Escape(type.Name)); - } else if (type is ByReferenceType) { - ((ByReferenceType)type).ElementType.WriteTo(writer, syntaxForElementTypes); - writer.Write('&'); - } else if (type is PointerType) { - ((PointerType)type).ElementType.WriteTo(writer, syntaxForElementTypes); - writer.Write('*'); - } else if (type is GenericInstanceType) { - type.GetElementType().WriteTo(writer, syntaxForElementTypes); - writer.Write('<'); - var arguments = ((GenericInstanceType)type).GenericArguments; - for (int i = 0; i < arguments.Count; i++) { - if (i > 0) - writer.Write(", "); - arguments[i].WriteTo(writer, syntaxForElementTypes); - } - writer.Write('>'); - } else if (type is OptionalModifierType) { - ((OptionalModifierType)type).ElementType.WriteTo(writer, syntax); - writer.Write(" modopt("); - ((OptionalModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName); - writer.Write(") "); - } else if (type is RequiredModifierType) { - ((RequiredModifierType)type).ElementType.WriteTo(writer, syntax); - writer.Write(" modreq("); - ((RequiredModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName); - writer.Write(") "); - } else { - string name = PrimitiveTypeName(type.FullName); - if (syntax == ILNameSyntax.ShortTypeName) { - if (name != null) - writer.Write(name); - else - writer.WriteReference(Escape(type.Name), type); - } else if ((syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters) && name != null) { - writer.Write(name); - } else { - if (syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters) - writer.Write(type.IsValueType ? "valuetype " : "class "); - - if (type.DeclaringType != null) { - type.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName); - writer.Write('/'); - writer.WriteReference(Escape(type.Name), type); - } else { - if (!type.IsDefinition && type.Scope != null && !(type is TypeSpecification)) - writer.Write("[{0}]", Escape(type.Scope.Name)); - writer.WriteReference(Escape(type.FullName), type); - } - } - } - } - - public static void WriteOperand(ITextOutput writer, object operand) - { - if (operand == null) - throw new ArgumentNullException("operand"); - - Instruction targetInstruction = operand as Instruction; - if (targetInstruction != null) { - WriteOffsetReference(writer, targetInstruction); - return; - } - - Instruction[] targetInstructions = operand as Instruction[]; - if (targetInstructions != null) { - WriteLabelList(writer, targetInstructions); - return; - } - - VariableReference variableRef = operand as VariableReference; - if (variableRef != null) { - //if (string.IsNullOrEmpty(variableRef.Name)) - writer.WriteReference(variableRef.Index.ToString(), variableRef); - //else - // writer.WriteReference(Escape(variableRef.Name), variableRef); - return; - } - - ParameterReference paramRef = operand as ParameterReference; - if (paramRef != null) { - if (string.IsNullOrEmpty(paramRef.Name)) - writer.WriteReference(paramRef.Index.ToString(), paramRef); - else - writer.WriteReference(Escape(paramRef.Name), paramRef); - return; - } - - MethodReference methodRef = operand as MethodReference; - if (methodRef != null) { - methodRef.WriteTo(writer); - return; - } - - TypeReference typeRef = operand as TypeReference; - if (typeRef != null) { - typeRef.WriteTo(writer, ILNameSyntax.TypeName); - return; - } - - FieldReference fieldRef = operand as FieldReference; - if (fieldRef != null) { - fieldRef.WriteTo(writer); - return; - } - - string s = operand as string; - if (s != null) { - writer.Write("\"" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(s) + "\""); - } else if (operand is char) { - writer.Write(((int)(char)operand).ToString()); - } else if (operand is float) { - float val = (float)operand; - if (val == 0) { - if (1 / val == float.NegativeInfinity) { - // negative zero is a special case - writer.Write('-'); - } - writer.Write("0.0"); - } else if (float.IsInfinity(val) || float.IsNaN(val)) { - byte[] data = BitConverter.GetBytes(val); - writer.Write('('); - for (int i = 0; i < data.Length; i++) { - if (i > 0) - writer.Write(' '); - writer.Write(data[i].ToString("X2")); - } - writer.Write(')'); - } else { - writer.Write(val.ToString("R", System.Globalization.CultureInfo.InvariantCulture)); - } - } else if (operand is double) { - double val = (double)operand; - if (val == 0) { - if (1 / val == double.NegativeInfinity) { - // negative zero is a special case - writer.Write('-'); - } - writer.Write("0.0"); - } else if (double.IsInfinity(val) || double.IsNaN(val)) { - byte[] data = BitConverter.GetBytes(val); - writer.Write('('); - for (int i = 0; i < data.Length; i++) { - if (i > 0) - writer.Write(' '); - writer.Write(data[i].ToString("X2")); - } - writer.Write(')'); - } else { - writer.Write(val.ToString("R", System.Globalization.CultureInfo.InvariantCulture)); - } - } else if (operand is bool) { - writer.Write((bool)operand ? "true" : "false"); - } else { - s = ToInvariantCultureString(operand); - writer.Write(s); - } - } - - public static string PrimitiveTypeName(string fullName) - { - switch (fullName) { - case "System.SByte": - return "int8"; - case "System.Int16": - return "int16"; - case "System.Int32": - return "int32"; - case "System.Int64": - return "int64"; - case "System.Byte": - return "uint8"; - case "System.UInt16": - return "uint16"; - case "System.UInt32": - return "uint32"; - case "System.UInt64": - return "uint64"; - case "System.Single": - return "float32"; - case "System.Double": - return "float64"; - case "System.Void": - return "void"; - case "System.Boolean": - return "bool"; - case "System.String": - return "string"; - case "System.Char": - return "char"; - case "System.Object": - return "object"; - case "System.IntPtr": - return "native int"; - default: - return null; - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Disassembler/ILStructure.cs b/main/contrib/ICSharpCode.Decompiler/Disassembler/ILStructure.cs deleted file mode 100644 index 4c681e3c11..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Disassembler/ILStructure.cs +++ /dev/null @@ -1,228 +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.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using ICSharpCode.Decompiler.FlowAnalysis; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.Disassembler -{ - /// <summary> - /// Specifies the type of an IL structure. - /// </summary> - public enum ILStructureType - { - /// <summary> - /// The root block of the method - /// </summary> - Root, - /// <summary> - /// A nested control structure representing a loop. - /// </summary> - Loop, - /// <summary> - /// A nested control structure representing a try block. - /// </summary> - Try, - /// <summary> - /// A nested control structure representing a catch, finally, or fault block. - /// </summary> - Handler, - /// <summary> - /// A nested control structure representing an exception filter block. - /// </summary> - Filter - } - - /// <summary> - /// An IL structure. - /// </summary> - public class ILStructure - { - public readonly ILStructureType Type; - - /// <summary> - /// Start position of the structure. - /// </summary> - public readonly int StartOffset; - - /// <summary> - /// End position of the structure. (exclusive) - /// </summary> - public readonly int EndOffset; - - /// <summary> - /// The exception handler associated with the Try, Filter or Handler block. - /// </summary> - public readonly ExceptionHandler ExceptionHandler; - - /// <summary> - /// The loop's entry point. - /// </summary> - public readonly Instruction LoopEntryPoint; - - /// <summary> - /// The list of child structures. - /// </summary> - public readonly List<ILStructure> Children = new List<ILStructure>(); - - public ILStructure(MethodBody body) - : this(ILStructureType.Root, 0, body.CodeSize) - { - // Build the tree of exception structures: - for (int i = 0; i < body.ExceptionHandlers.Count; i++) { - ExceptionHandler eh = body.ExceptionHandlers[i]; - if (!body.ExceptionHandlers.Take(i).Any(oldEh => oldEh.TryStart == eh.TryStart && oldEh.TryEnd == eh.TryEnd)) - AddNestedStructure(new ILStructure(ILStructureType.Try, eh.TryStart.Offset, eh.TryEnd.Offset, eh)); - if (eh.HandlerType == ExceptionHandlerType.Filter) - AddNestedStructure(new ILStructure(ILStructureType.Filter, eh.FilterStart.Offset, eh.HandlerStart.Offset, eh)); - AddNestedStructure(new ILStructure(ILStructureType.Handler, eh.HandlerStart.Offset, eh.HandlerEnd == null ? body.CodeSize : eh.HandlerEnd.Offset, eh)); - } - // Very simple loop detection: look for backward branches - List<KeyValuePair<Instruction, Instruction>> allBranches = FindAllBranches(body); - // We go through the branches in reverse so that we find the biggest possible loop boundary first (think loops with "continue;") - for (int i = allBranches.Count - 1; i >= 0; i--) { - int loopEnd = allBranches[i].Key.GetEndOffset(); - int loopStart = allBranches[i].Value.Offset; - if (loopStart < loopEnd) { - // We found a backward branch. This is a potential loop. - // Check that is has only one entry point: - Instruction entryPoint = null; - - // entry point is first instruction in loop if prev inst isn't an unconditional branch - Instruction prev = allBranches[i].Value.Previous; - if (prev != null && !OpCodeInfo.IsUnconditionalBranch(prev.OpCode)) - entryPoint = allBranches[i].Value; - - bool multipleEntryPoints = false; - foreach (var pair in allBranches) { - if (pair.Key.Offset < loopStart || pair.Key.Offset >= loopEnd) { - if (loopStart <= pair.Value.Offset && pair.Value.Offset < loopEnd) { - // jump from outside the loop into the loop - if (entryPoint == null) - entryPoint = pair.Value; - else if (pair.Value != entryPoint) - multipleEntryPoints = true; - } - } - } - if (!multipleEntryPoints) { - AddNestedStructure(new ILStructure(ILStructureType.Loop, loopStart, loopEnd, entryPoint)); - } - } - } - SortChildren(); - } - - public ILStructure(ILStructureType type, int startOffset, int endOffset, ExceptionHandler handler = null) - { - Debug.Assert(startOffset < endOffset); - this.Type = type; - this.StartOffset = startOffset; - this.EndOffset = endOffset; - this.ExceptionHandler = handler; - } - - public ILStructure(ILStructureType type, int startOffset, int endOffset, Instruction loopEntryPoint) - { - Debug.Assert(startOffset < endOffset); - this.Type = type; - this.StartOffset = startOffset; - this.EndOffset = endOffset; - this.LoopEntryPoint = loopEntryPoint; - } - - bool AddNestedStructure(ILStructure newStructure) - { - // special case: don't consider the loop-like structure of "continue;" statements to be nested loops - if (this.Type == ILStructureType.Loop && newStructure.Type == ILStructureType.Loop && newStructure.StartOffset == this.StartOffset) - return false; - - // use <= for end-offset comparisons because both end and EndOffset are exclusive - Debug.Assert(StartOffset <= newStructure.StartOffset && newStructure.EndOffset <= EndOffset); - foreach (ILStructure child in this.Children) { - if (child.StartOffset <= newStructure.StartOffset && newStructure.EndOffset <= child.EndOffset) { - return child.AddNestedStructure(newStructure); - } else if (!(child.EndOffset <= newStructure.StartOffset || newStructure.EndOffset <= child.StartOffset)) { - // child and newStructure overlap - if (!(newStructure.StartOffset <= child.StartOffset && child.EndOffset <= newStructure.EndOffset)) { - // Invalid nesting, can't build a tree. -> Don't add the new structure. - return false; - } - } - } - // Move existing structures into the new structure: - for (int i = 0; i < this.Children.Count; i++) { - ILStructure child = this.Children[i]; - if (newStructure.StartOffset <= child.StartOffset && child.EndOffset <= newStructure.EndOffset) { - this.Children.RemoveAt(i--); - newStructure.Children.Add(child); - } - } - // Add the structure here: - this.Children.Add(newStructure); - return true; - } - - /// <summary> - /// Finds all branches. Returns list of source offset->target offset mapping. - /// Multiple entries for the same source offset are possible (switch statements). - /// The result is sorted by source offset. - /// </summary> - List<KeyValuePair<Instruction, Instruction>> FindAllBranches(MethodBody body) - { - var result = new List<KeyValuePair<Instruction, Instruction>>(); - foreach (Instruction inst in body.Instructions) { - switch (inst.OpCode.OperandType) { - case OperandType.InlineBrTarget: - case OperandType.ShortInlineBrTarget: - result.Add(new KeyValuePair<Instruction, Instruction>(inst, (Instruction)inst.Operand)); - break; - case OperandType.InlineSwitch: - foreach (Instruction target in (Instruction[])inst.Operand) - result.Add(new KeyValuePair<Instruction, Instruction>(inst, target)); - break; - } - } - return result; - } - - void SortChildren() - { - Children.Sort((a, b) => a.StartOffset.CompareTo(b.StartOffset)); - foreach (ILStructure child in Children) - child.SortChildren(); - } - - /// <summary> - /// Gets the innermost structure containing the specified offset. - /// </summary> - public ILStructure GetInnermost(int offset) - { - Debug.Assert(StartOffset <= offset && offset < EndOffset); - foreach (ILStructure child in this.Children) { - if (child.StartOffset <= offset && offset < child.EndOffset) - return child.GetInnermost(offset); - } - return this; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs b/main/contrib/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs deleted file mode 100644 index 8997c17c04..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs +++ /dev/null @@ -1,240 +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.Collections.Generic; -using System.Linq; -using System.Threading; - -using ICSharpCode.Decompiler; -using ICSharpCode.Decompiler.FlowAnalysis; -using ICSharpCode.Decompiler.ILAst; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.Disassembler -{ - /// <summary> - /// Disassembles a method body. - /// </summary> - public sealed class MethodBodyDisassembler - { - readonly ITextOutput output; - readonly bool detectControlStructure; - readonly CancellationToken cancellationToken; - - public MethodBodyDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken) - { - if (output == null) - throw new ArgumentNullException("output"); - this.output = output; - this.detectControlStructure = detectControlStructure; - this.cancellationToken = cancellationToken; - } - - public void Disassemble(MethodBody body, MethodDebugSymbols debugSymbols) - { - // start writing IL code - MethodDefinition method = body.Method; - output.WriteLine("// Method begins at RVA 0x{0:x4}", method.RVA); - output.WriteLine("// Code size {0} (0x{0:x})", body.CodeSize); - output.WriteLine(".maxstack {0}", body.MaxStackSize); - if (method.DeclaringType.Module.Assembly != null && method.DeclaringType.Module.Assembly.EntryPoint == method) - output.WriteLine (".entrypoint"); - - if (method.Body.HasVariables) { - output.Write(".locals "); - if (method.Body.InitLocals) - output.Write("init "); - output.WriteLine("("); - output.Indent(); - foreach (var v in method.Body.Variables) { - output.WriteDefinition("[" + v.Index + "] ", v); - v.VariableType.WriteTo(output); - string name; - if (body.Method.DebugInformation.TryGetName (v, out name)) { - output.Write(' '); - output.Write(DisassemblerHelpers.Escape(name)); - } - if (v.Index + 1 < method.Body.Variables.Count) - output.Write(','); - output.WriteLine(); - } - output.Unindent(); - output.WriteLine(")"); - } - output.WriteLine(); - - if (detectControlStructure && body.Instructions.Count > 0) { - Instruction inst = body.Instructions[0]; - HashSet<int> branchTargets = GetBranchTargets(body.Instructions); - WriteStructureBody(new ILStructure(body), branchTargets, ref inst, debugSymbols, method.Body.CodeSize); - } else { - foreach (var inst in method.Body.Instructions) { - var startLocation = output.Location; - inst.WriteTo(output); - - if (debugSymbols != null) { - // add IL code mappings - used in debugger - debugSymbols.SequencePoints.Add( - new SequencePoint() { - StartLocation = output.Location, - EndLocation = output.Location, - ILRanges = new ILRange[] { new ILRange(inst.Offset, inst.Next == null ? method.Body.CodeSize : inst.Next.Offset) } - }); - } - - output.WriteLine(); - } - if (method.Body.HasExceptionHandlers) { - output.WriteLine(); - foreach (var eh in method.Body.ExceptionHandlers) { - eh.WriteTo(output); - output.WriteLine(); - } - } - } - } - - HashSet<int> GetBranchTargets(IEnumerable<Instruction> instructions) - { - HashSet<int> branchTargets = new HashSet<int>(); - foreach (var inst in instructions) { - Instruction target = inst.Operand as Instruction; - if (target != null) - branchTargets.Add(target.Offset); - Instruction[] targets = inst.Operand as Instruction[]; - if (targets != null) - foreach (Instruction t in targets) - branchTargets.Add(t.Offset); - } - return branchTargets; - } - - void WriteStructureHeader(ILStructure s) - { - switch (s.Type) { - case ILStructureType.Loop: - output.Write("// loop start"); - if (s.LoopEntryPoint != null) { - output.Write(" (head: "); - DisassemblerHelpers.WriteOffsetReference(output, s.LoopEntryPoint); - output.Write(')'); - } - output.WriteLine(); - break; - case ILStructureType.Try: - output.WriteLine(".try"); - output.WriteLine("{"); - break; - case ILStructureType.Handler: - switch (s.ExceptionHandler.HandlerType) { - case Mono.Cecil.Cil.ExceptionHandlerType.Catch: - case Mono.Cecil.Cil.ExceptionHandlerType.Filter: - output.Write("catch"); - if (s.ExceptionHandler.CatchType != null) { - output.Write(' '); - s.ExceptionHandler.CatchType.WriteTo(output, ILNameSyntax.TypeName); - } - output.WriteLine(); - break; - case Mono.Cecil.Cil.ExceptionHandlerType.Finally: - output.WriteLine("finally"); - break; - case Mono.Cecil.Cil.ExceptionHandlerType.Fault: - output.WriteLine("fault"); - break; - default: - throw new NotSupportedException(); - } - output.WriteLine("{"); - break; - case ILStructureType.Filter: - output.WriteLine("filter"); - output.WriteLine("{"); - break; - default: - throw new NotSupportedException(); - } - output.Indent(); - } - - void WriteStructureBody(ILStructure s, HashSet<int> branchTargets, ref Instruction inst, MethodDebugSymbols debugSymbols, int codeSize) - { - bool isFirstInstructionInStructure = true; - bool prevInstructionWasBranch = false; - int childIndex = 0; - while (inst != null && inst.Offset < s.EndOffset) { - int offset = inst.Offset; - if (childIndex < s.Children.Count && s.Children[childIndex].StartOffset <= offset && offset < s.Children[childIndex].EndOffset) { - ILStructure child = s.Children[childIndex++]; - WriteStructureHeader(child); - WriteStructureBody(child, branchTargets, ref inst, debugSymbols, codeSize); - WriteStructureFooter(child); - } else { - if (!isFirstInstructionInStructure && (prevInstructionWasBranch || branchTargets.Contains(offset))) { - output.WriteLine(); // put an empty line after branches, and in front of branch targets - } - var startLocation = output.Location; - inst.WriteTo(output); - - // add IL code mappings - used in debugger - if (debugSymbols != null) { - debugSymbols.SequencePoints.Add( - new SequencePoint() { - StartLocation = startLocation, - EndLocation = output.Location, - ILRanges = new ILRange[] { new ILRange(inst.Offset, inst.Next == null ? codeSize : inst.Next.Offset) } - }); - } - - output.WriteLine(); - - prevInstructionWasBranch = inst.OpCode.FlowControl == FlowControl.Branch - || inst.OpCode.FlowControl == FlowControl.Cond_Branch - || inst.OpCode.FlowControl == FlowControl.Return - || inst.OpCode.FlowControl == FlowControl.Throw; - - inst = inst.Next; - } - isFirstInstructionInStructure = false; - } - } - - void WriteStructureFooter(ILStructure s) - { - output.Unindent(); - switch (s.Type) { - case ILStructureType.Loop: - output.WriteLine("// end loop"); - break; - case ILStructureType.Try: - output.WriteLine("} // end .try"); - break; - case ILStructureType.Handler: - output.WriteLine("} // end handler"); - break; - case ILStructureType.Filter: - output.WriteLine("} // end filter"); - break; - default: - throw new NotSupportedException(); - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/main/contrib/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs deleted file mode 100644 index 1518f1ca66..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ /dev/null @@ -1,1164 +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.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading; -using ICSharpCode.NRefactory; -using Mono.Cecil; -using Mono.Collections.Generic; - -namespace ICSharpCode.Decompiler.Disassembler -{ - /// <summary> - /// Disassembles type and member definitions. - /// </summary> - public sealed class ReflectionDisassembler - { - readonly ITextOutput output; - CancellationToken cancellationToken; - bool isInType; // whether we are currently disassembling a whole type (-> defaultCollapsed for foldings) - MethodBodyDisassembler methodBodyDisassembler; - MemberReference currentMember; - - public ReflectionDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken) - { - if (output == null) - throw new ArgumentNullException("output"); - this.output = output; - this.cancellationToken = cancellationToken; - this.methodBodyDisassembler = new MethodBodyDisassembler(output, detectControlStructure, cancellationToken); - } - - #region Disassemble Method - EnumNameCollection<MethodAttributes> methodAttributeFlags = new EnumNameCollection<MethodAttributes>() { - { MethodAttributes.Final, "final" }, - { MethodAttributes.HideBySig, "hidebysig" }, - { MethodAttributes.SpecialName, "specialname" }, - { MethodAttributes.PInvokeImpl, null }, // handled separately - { MethodAttributes.UnmanagedExport, "export" }, - { MethodAttributes.RTSpecialName, "rtspecialname" }, - { MethodAttributes.RequireSecObject, "reqsecobj" }, - { MethodAttributes.NewSlot, "newslot" }, - { MethodAttributes.CheckAccessOnOverride, "strict" }, - { MethodAttributes.Abstract, "abstract" }, - { MethodAttributes.Virtual, "virtual" }, - { MethodAttributes.Static, "static" }, - { MethodAttributes.HasSecurity, null }, // ?? also invisible in ILDasm - }; - - EnumNameCollection<MethodAttributes> methodVisibility = new EnumNameCollection<MethodAttributes>() { - { MethodAttributes.Private, "private" }, - { MethodAttributes.FamANDAssem, "famandassem" }, - { MethodAttributes.Assembly, "assembly" }, - { MethodAttributes.Family, "family" }, - { MethodAttributes.FamORAssem, "famorassem" }, - { MethodAttributes.Public, "public" }, - }; - - EnumNameCollection<MethodCallingConvention> callingConvention = new EnumNameCollection<MethodCallingConvention>() { - { MethodCallingConvention.C, "unmanaged cdecl" }, - { MethodCallingConvention.StdCall, "unmanaged stdcall" }, - { MethodCallingConvention.ThisCall, "unmanaged thiscall" }, - { MethodCallingConvention.FastCall, "unmanaged fastcall" }, - { MethodCallingConvention.VarArg, "vararg" }, - { MethodCallingConvention.Generic, null }, - }; - - EnumNameCollection<MethodImplAttributes> methodCodeType = new EnumNameCollection<MethodImplAttributes>() { - { MethodImplAttributes.IL, "cil" }, - { MethodImplAttributes.Native, "native" }, - { MethodImplAttributes.OPTIL, "optil" }, - { MethodImplAttributes.Runtime, "runtime" }, - }; - - EnumNameCollection<MethodImplAttributes> methodImpl = new EnumNameCollection<MethodImplAttributes>() { - { MethodImplAttributes.Synchronized, "synchronized" }, - { MethodImplAttributes.NoInlining, "noinlining" }, - { MethodImplAttributes.NoOptimization, "nooptimization" }, - { MethodImplAttributes.PreserveSig, "preservesig" }, - { MethodImplAttributes.InternalCall, "internalcall" }, - { MethodImplAttributes.ForwardRef, "forwardref" }, - }; - - public void DisassembleMethod(MethodDefinition method) - { - // set current member - currentMember = method; - - // write method header - output.WriteDefinition(".method ", method); - DisassembleMethodInternal(method); - } - - void DisassembleMethodInternal(MethodDefinition method) - { - // .method public hidebysig specialname - // instance default class [mscorlib]System.IO.TextWriter get_BaseWriter () cil managed - // - - TextLocation startLocation = output.Location; - - //emit flags - WriteEnum(method.Attributes & MethodAttributes.MemberAccessMask, methodVisibility); - WriteFlags(method.Attributes & ~MethodAttributes.MemberAccessMask, methodAttributeFlags); - if(method.IsCompilerControlled) output.Write("privatescope "); - - if ((method.Attributes & MethodAttributes.PInvokeImpl) == MethodAttributes.PInvokeImpl) { - output.Write("pinvokeimpl"); - if (method.HasPInvokeInfo && method.PInvokeInfo != null) { - PInvokeInfo info = method.PInvokeInfo; - output.Write("(\"" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(info.Module.Name) + "\""); - - if (!string.IsNullOrEmpty(info.EntryPoint) && info.EntryPoint != method.Name) - output.Write(" as \"" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(info.EntryPoint) + "\""); - - if (info.IsNoMangle) - output.Write(" nomangle"); - - if (info.IsCharSetAnsi) - output.Write(" ansi"); - else if (info.IsCharSetAuto) - output.Write(" autochar"); - else if (info.IsCharSetUnicode) - output.Write(" unicode"); - - if (info.SupportsLastError) - output.Write(" lasterr"); - - if (info.IsCallConvCdecl) - output.Write(" cdecl"); - else if (info.IsCallConvFastcall) - output.Write(" fastcall"); - else if (info.IsCallConvStdCall) - output.Write(" stdcall"); - else if (info.IsCallConvThiscall) - output.Write(" thiscall"); - else if (info.IsCallConvWinapi) - output.Write(" winapi"); - - output.Write(')'); - } - output.Write(' '); - } - - output.WriteLine(); - output.Indent(); - if (method.ExplicitThis) { - output.Write("instance explicit "); - } else if (method.HasThis) { - output.Write("instance "); - } - - //call convention - WriteEnum(method.CallingConvention & (MethodCallingConvention)0x1f, callingConvention); - - //return type - method.ReturnType.WriteTo(output); - output.Write(' '); - if (method.MethodReturnType.HasMarshalInfo) { - WriteMarshalInfo(method.MethodReturnType.MarshalInfo); - } - - if (method.IsCompilerControlled) { - output.Write(DisassemblerHelpers.Escape(method.Name + "$PST" + method.MetadataToken.ToInt32().ToString("X8"))); - } else { - output.Write(DisassemblerHelpers.Escape(method.Name)); - } - - WriteTypeParameters(output, method); - - //( params ) - output.Write(" ("); - if (method.HasParameters) { - output.WriteLine(); - output.Indent(); - WriteParameters(method.Parameters); - output.Unindent(); - } - output.Write(") "); - //cil managed - WriteEnum(method.ImplAttributes & MethodImplAttributes.CodeTypeMask, methodCodeType); - if ((method.ImplAttributes & MethodImplAttributes.ManagedMask) == MethodImplAttributes.Managed) - output.Write("managed "); - else - output.Write("unmanaged "); - WriteFlags(method.ImplAttributes & ~(MethodImplAttributes.CodeTypeMask | MethodImplAttributes.ManagedMask), methodImpl); - - output.Unindent(); - OpenBlock(defaultCollapsed: isInType); - WriteAttributes(method.CustomAttributes); - if (method.HasOverrides) { - foreach (var methodOverride in method.Overrides) { - output.Write(".override method "); - methodOverride.WriteTo(output); - output.WriteLine(); - } - } - WriteParameterAttributes(0, method.MethodReturnType, method.MethodReturnType); - foreach (var p in method.Parameters) { - WriteParameterAttributes(p.Index + 1, p, p); - } - WriteSecurityDeclarations(method); - - if (method.HasBody) { - // create IL code mappings - used in debugger - MethodDebugSymbols debugSymbols = new MethodDebugSymbols(method); - debugSymbols.StartLocation = startLocation; - methodBodyDisassembler.Disassemble(method.Body, debugSymbols); - debugSymbols.EndLocation = output.Location; - output.AddDebugSymbols(debugSymbols); - } - - CloseBlock("end of method " + DisassemblerHelpers.Escape(method.DeclaringType.Name) + "::" + DisassemblerHelpers.Escape(method.Name)); - } - - #region Write Security Declarations - void WriteSecurityDeclarations(ISecurityDeclarationProvider secDeclProvider) - { - if (!secDeclProvider.HasSecurityDeclarations) - return; - foreach (var secdecl in secDeclProvider.SecurityDeclarations) { - output.Write(".permissionset "); - switch (secdecl.Action) { - case SecurityAction.Request: - output.Write("request"); - break; - case SecurityAction.Demand: - output.Write("demand"); - break; - case SecurityAction.Assert: - output.Write("assert"); - break; - case SecurityAction.Deny: - output.Write("deny"); - break; - case SecurityAction.PermitOnly: - output.Write("permitonly"); - break; - case SecurityAction.LinkDemand: - output.Write("linkcheck"); - break; - case SecurityAction.InheritDemand: - output.Write("inheritcheck"); - break; - case SecurityAction.RequestMinimum: - output.Write("reqmin"); - break; - case SecurityAction.RequestOptional: - output.Write("reqopt"); - break; - case SecurityAction.RequestRefuse: - output.Write("reqrefuse"); - break; - case SecurityAction.PreJitGrant: - output.Write("prejitgrant"); - break; - case SecurityAction.PreJitDeny: - output.Write("prejitdeny"); - break; - case SecurityAction.NonCasDemand: - output.Write("noncasdemand"); - break; - case SecurityAction.NonCasLinkDemand: - output.Write("noncaslinkdemand"); - break; - case SecurityAction.NonCasInheritance: - output.Write("noncasinheritance"); - break; - default: - output.Write(secdecl.Action.ToString()); - break; - } - output.WriteLine(" = {"); - output.Indent(); - for (int i = 0; i < secdecl.SecurityAttributes.Count; i++) { - SecurityAttribute sa = secdecl.SecurityAttributes[i]; - if (sa.AttributeType.Scope == sa.AttributeType.Module) { - output.Write("class "); - output.Write(DisassemblerHelpers.Escape(GetAssemblyQualifiedName(sa.AttributeType))); - } else { - sa.AttributeType.WriteTo(output, ILNameSyntax.TypeName); - } - output.Write(" = {"); - if (sa.HasFields || sa.HasProperties) { - output.WriteLine(); - output.Indent(); - - foreach (CustomAttributeNamedArgument na in sa.Fields) { - output.Write("field "); - WriteSecurityDeclarationArgument(na); - output.WriteLine(); - } - - foreach (CustomAttributeNamedArgument na in sa.Properties) { - output.Write("property "); - WriteSecurityDeclarationArgument(na); - output.WriteLine(); - } - - output.Unindent(); - } - output.Write('}'); - - if (i + 1< secdecl.SecurityAttributes.Count) - output.Write(','); - output.WriteLine(); - } - output.Unindent(); - output.WriteLine("}"); - } - } - - void WriteSecurityDeclarationArgument(CustomAttributeNamedArgument na) - { - TypeReference type = na.Argument.Type; - if (type.MetadataType == MetadataType.Class || type.MetadataType == MetadataType.ValueType) { - output.Write("enum "); - if (type.Scope != type.Module) { - output.Write("class "); - output.Write(DisassemblerHelpers.Escape(GetAssemblyQualifiedName(type))); - } else { - type.WriteTo(output, ILNameSyntax.TypeName); - } - } else { - type.WriteTo(output); - } - output.Write(' '); - output.Write(DisassemblerHelpers.Escape(na.Name)); - output.Write(" = "); - if (na.Argument.Value is string) { - // secdecls use special syntax for strings - output.Write("string('{0}')", NRefactory.CSharp.TextWriterTokenWriter.ConvertString((string)na.Argument.Value).Replace("'", "\'")); - } else { - WriteConstant(na.Argument.Value); - } - } - - string GetAssemblyQualifiedName(TypeReference type) - { - AssemblyNameReference anr = type.Scope as AssemblyNameReference; - if (anr == null) { - ModuleDefinition md = type.Scope as ModuleDefinition; - if (md != null) { - anr = md.Assembly.Name; - } - } - if (anr != null) { - return type.FullName + ", " + anr.FullName; - } else { - return type.FullName; - } - } - #endregion - - #region WriteMarshalInfo - void WriteMarshalInfo(MarshalInfo marshalInfo) - { - output.Write("marshal("); - WriteNativeType(marshalInfo.NativeType, marshalInfo); - output.Write(") "); - } - - void WriteNativeType(NativeType nativeType, MarshalInfo marshalInfo = null) - { - switch (nativeType) { - case NativeType.None: - break; - case NativeType.Boolean: - output.Write("bool"); - break; - case NativeType.I1: - output.Write("int8"); - break; - case NativeType.U1: - output.Write("unsigned int8"); - break; - case NativeType.I2: - output.Write("int16"); - break; - case NativeType.U2: - output.Write("unsigned int16"); - break; - case NativeType.I4: - output.Write("int32"); - break; - case NativeType.U4: - output.Write("unsigned int32"); - break; - case NativeType.I8: - output.Write("int64"); - break; - case NativeType.U8: - output.Write("unsigned int64"); - break; - case NativeType.R4: - output.Write("float32"); - break; - case NativeType.R8: - output.Write("float64"); - break; - case NativeType.LPStr: - output.Write("lpstr"); - break; - case NativeType.Int: - output.Write("int"); - break; - case NativeType.UInt: - output.Write("unsigned int"); - break; - case NativeType.Func: - goto default; // ?? - case NativeType.Array: - ArrayMarshalInfo ami = (ArrayMarshalInfo)marshalInfo; - if (ami == null) - goto default; - if (ami.ElementType != NativeType.Max) - WriteNativeType(ami.ElementType); - output.Write('['); - if (ami.SizeParameterMultiplier == 0) { - output.Write(ami.Size.ToString()); - } else { - if (ami.Size >= 0) - output.Write(ami.Size.ToString()); - output.Write(" + "); - output.Write(ami.SizeParameterIndex.ToString()); - } - output.Write(']'); - break; - case NativeType.Currency: - output.Write("currency"); - break; - case NativeType.BStr: - output.Write("bstr"); - break; - case NativeType.LPWStr: - output.Write("lpwstr"); - break; - case NativeType.LPTStr: - output.Write("lptstr"); - break; - case NativeType.FixedSysString: - output.Write("fixed sysstring[{0}]", ((FixedSysStringMarshalInfo)marshalInfo).Size); - break; - case NativeType.IUnknown: - output.Write("iunknown"); - break; - case NativeType.IDispatch: - output.Write("idispatch"); - break; - case NativeType.Struct: - output.Write("struct"); - break; - case NativeType.IntF: - output.Write("interface"); - break; - case NativeType.SafeArray: - output.Write("safearray "); - SafeArrayMarshalInfo sami = marshalInfo as SafeArrayMarshalInfo; - if (sami != null) { - switch (sami.ElementType) { - case VariantType.None: - break; - case VariantType.I2: - output.Write("int16"); - break; - case VariantType.I4: - output.Write("int32"); - break; - case VariantType.R4: - output.Write("float32"); - break; - case VariantType.R8: - output.Write("float64"); - break; - case VariantType.CY: - output.Write("currency"); - break; - case VariantType.Date: - output.Write("date"); - break; - case VariantType.BStr: - output.Write("bstr"); - break; - case VariantType.Dispatch: - output.Write("idispatch"); - break; - case VariantType.Error: - output.Write("error"); - break; - case VariantType.Bool: - output.Write("bool"); - break; - case VariantType.Variant: - output.Write("variant"); - break; - case VariantType.Unknown: - output.Write("iunknown"); - break; - case VariantType.Decimal: - output.Write("decimal"); - break; - case VariantType.I1: - output.Write("int8"); - break; - case VariantType.UI1: - output.Write("unsigned int8"); - break; - case VariantType.UI2: - output.Write("unsigned int16"); - break; - case VariantType.UI4: - output.Write("unsigned int32"); - break; - case VariantType.Int: - output.Write("int"); - break; - case VariantType.UInt: - output.Write("unsigned int"); - break; - default: - output.Write(sami.ElementType.ToString()); - break; - } - } - break; - case NativeType.FixedArray: - output.Write("fixed array"); - FixedArrayMarshalInfo fami = marshalInfo as FixedArrayMarshalInfo; - if (fami != null) { - output.Write("[{0}]", fami.Size); - if (fami.ElementType != NativeType.None) { - output.Write(' '); - WriteNativeType(fami.ElementType); - } - } - break; - case NativeType.ByValStr: - output.Write("byvalstr"); - break; - case NativeType.ANSIBStr: - output.Write("ansi bstr"); - break; - case NativeType.TBStr: - output.Write("tbstr"); - break; - case NativeType.VariantBool: - output.Write("variant bool"); - break; - case NativeType.ASAny: - output.Write("as any"); - break; - case NativeType.LPStruct: - output.Write("lpstruct"); - break; - case NativeType.CustomMarshaler: - CustomMarshalInfo cmi = marshalInfo as CustomMarshalInfo; - if (cmi == null) - goto default; - output.Write("custom(\"{0}\", \"{1}\"", - NRefactory.CSharp.TextWriterTokenWriter.ConvertString(cmi.ManagedType.FullName), - NRefactory.CSharp.TextWriterTokenWriter.ConvertString(cmi.Cookie)); - if (cmi.Guid != Guid.Empty || !string.IsNullOrEmpty(cmi.UnmanagedType)) { - output.Write(", \"{0}\", \"{1}\"", cmi.Guid.ToString(), NRefactory.CSharp.TextWriterTokenWriter.ConvertString(cmi.UnmanagedType)); - } - output.Write(')'); - break; - case NativeType.Error: - output.Write("error"); - break; - default: - output.Write(nativeType.ToString()); - break; - } - } - #endregion - - void WriteParameters(Collection<ParameterDefinition> parameters) - { - for (int i = 0; i < parameters.Count; i++) { - var p = parameters[i]; - if (p.IsIn) - output.Write("[in] "); - if (p.IsOut) - output.Write("[out] "); - if (p.IsOptional) - output.Write("[opt] "); - p.ParameterType.WriteTo(output); - output.Write(' '); - if (p.HasMarshalInfo) { - WriteMarshalInfo(p.MarshalInfo); - } - output.WriteDefinition(DisassemblerHelpers.Escape(p.Name), p); - if (i < parameters.Count - 1) - output.Write(','); - output.WriteLine(); - } - } - - void WriteParameterAttributes(int index, IConstantProvider cp, ICustomAttributeProvider cap) - { - if (!cp.HasConstant && !cap.HasCustomAttributes) - return; - output.Write(".param [{0}]", index); - if (cp.HasConstant) { - output.Write(" = "); - WriteConstant(cp.Constant); - } - output.WriteLine(); - WriteAttributes(cap.CustomAttributes); - } - - void WriteConstant(object constant) - { - if (constant == null) { - output.Write("nullref"); - } else { - string typeName = DisassemblerHelpers.PrimitiveTypeName(constant.GetType().FullName); - if (typeName != null && typeName != "string") { - output.Write(typeName); - output.Write('('); - float? cf = constant as float?; - double? cd = constant as double?; - if (cf.HasValue && (float.IsNaN(cf.Value) || float.IsInfinity(cf.Value))) { - output.Write("0x{0:x8}", BitConverter.ToInt32(BitConverter.GetBytes(cf.Value), 0)); - } else if (cd.HasValue && (double.IsNaN(cd.Value) || double.IsInfinity(cd.Value))) { - output.Write("0x{0:x16}", BitConverter.DoubleToInt64Bits(cd.Value)); - } else { - DisassemblerHelpers.WriteOperand(output, constant); - } - output.Write(')'); - } else { - DisassemblerHelpers.WriteOperand(output, constant); - } - } - } - #endregion - - #region Disassemble Field - EnumNameCollection<FieldAttributes> fieldVisibility = new EnumNameCollection<FieldAttributes>() { - { FieldAttributes.Private, "private" }, - { FieldAttributes.FamANDAssem, "famandassem" }, - { FieldAttributes.Assembly, "assembly" }, - { FieldAttributes.Family, "family" }, - { FieldAttributes.FamORAssem, "famorassem" }, - { FieldAttributes.Public, "public" }, - }; - - EnumNameCollection<FieldAttributes> fieldAttributes = new EnumNameCollection<FieldAttributes>() { - { FieldAttributes.Static, "static" }, - { FieldAttributes.Literal, "literal" }, - { FieldAttributes.InitOnly, "initonly" }, - { FieldAttributes.SpecialName, "specialname" }, - { FieldAttributes.RTSpecialName, "rtspecialname" }, - { FieldAttributes.NotSerialized, "notserialized" }, - }; - - public void DisassembleField(FieldDefinition field) - { - output.WriteDefinition(".field ", field); - if (field.HasLayoutInfo) { - output.Write("[" + field.Offset + "] "); - } - WriteEnum(field.Attributes & FieldAttributes.FieldAccessMask, fieldVisibility); - const FieldAttributes hasXAttributes = FieldAttributes.HasDefault | FieldAttributes.HasFieldMarshal | FieldAttributes.HasFieldRVA; - WriteFlags(field.Attributes & ~(FieldAttributes.FieldAccessMask | hasXAttributes), fieldAttributes); - if (field.HasMarshalInfo) { - WriteMarshalInfo(field.MarshalInfo); - } - field.FieldType.WriteTo(output); - output.Write(' '); - output.Write(DisassemblerHelpers.Escape(field.Name)); - if ((field.Attributes & FieldAttributes.HasFieldRVA) == FieldAttributes.HasFieldRVA) { - output.Write(" at I_{0:x8}", field.RVA); - } - if (field.HasConstant) { - output.Write(" = "); - WriteConstant(field.Constant); - } - output.WriteLine(); - if (field.HasCustomAttributes) { - output.MarkFoldStart(); - WriteAttributes(field.CustomAttributes); - output.MarkFoldEnd(); - } - } - #endregion - - #region Disassemble Property - EnumNameCollection<PropertyAttributes> propertyAttributes = new EnumNameCollection<PropertyAttributes>() { - { PropertyAttributes.SpecialName, "specialname" }, - { PropertyAttributes.RTSpecialName, "rtspecialname" }, - { PropertyAttributes.HasDefault, "hasdefault" }, - }; - - public void DisassembleProperty(PropertyDefinition property) - { - // set current member - currentMember = property; - - output.WriteDefinition(".property ", property); - WriteFlags(property.Attributes, propertyAttributes); - if (property.HasThis) - output.Write("instance "); - property.PropertyType.WriteTo(output); - output.Write(' '); - output.Write(DisassemblerHelpers.Escape(property.Name)); - - output.Write("("); - if (property.HasParameters) { - output.WriteLine(); - output.Indent(); - WriteParameters(property.Parameters); - output.Unindent(); - } - output.Write(")"); - - OpenBlock(false); - WriteAttributes(property.CustomAttributes); - WriteNestedMethod(".get", property.GetMethod); - WriteNestedMethod(".set", property.SetMethod); - - foreach (var method in property.OtherMethods) { - WriteNestedMethod(".other", method); - } - CloseBlock(); - } - - void WriteNestedMethod(string keyword, MethodDefinition method) - { - if (method == null) - return; - - output.Write(keyword); - output.Write(' '); - method.WriteTo(output); - output.WriteLine(); - } - #endregion - - #region Disassemble Event - EnumNameCollection<EventAttributes> eventAttributes = new EnumNameCollection<EventAttributes>() { - { EventAttributes.SpecialName, "specialname" }, - { EventAttributes.RTSpecialName, "rtspecialname" }, - }; - - public void DisassembleEvent(EventDefinition ev) - { - // set current member - currentMember = ev; - - output.WriteDefinition(".event ", ev); - WriteFlags(ev.Attributes, eventAttributes); - ev.EventType.WriteTo(output, ILNameSyntax.TypeName); - output.Write(' '); - output.Write(DisassemblerHelpers.Escape(ev.Name)); - OpenBlock(false); - WriteAttributes(ev.CustomAttributes); - WriteNestedMethod(".addon", ev.AddMethod); - WriteNestedMethod(".removeon", ev.RemoveMethod); - WriteNestedMethod(".fire", ev.InvokeMethod); - foreach (var method in ev.OtherMethods) { - WriteNestedMethod(".other", method); - } - CloseBlock(); - } - #endregion - - #region Disassemble Type - EnumNameCollection<TypeAttributes> typeVisibility = new EnumNameCollection<TypeAttributes>() { - { TypeAttributes.Public, "public" }, - { TypeAttributes.NotPublic, "private" }, - { TypeAttributes.NestedPublic, "nested public" }, - { TypeAttributes.NestedPrivate, "nested private" }, - { TypeAttributes.NestedAssembly, "nested assembly" }, - { TypeAttributes.NestedFamily, "nested family" }, - { TypeAttributes.NestedFamANDAssem, "nested famandassem" }, - { TypeAttributes.NestedFamORAssem, "nested famorassem" }, - }; - - EnumNameCollection<TypeAttributes> typeLayout = new EnumNameCollection<TypeAttributes>() { - { TypeAttributes.AutoLayout, "auto" }, - { TypeAttributes.SequentialLayout, "sequential" }, - { TypeAttributes.ExplicitLayout, "explicit" }, - }; - - EnumNameCollection<TypeAttributes> typeStringFormat = new EnumNameCollection<TypeAttributes>() { - { TypeAttributes.AutoClass, "auto" }, - { TypeAttributes.AnsiClass, "ansi" }, - { TypeAttributes.UnicodeClass, "unicode" }, - }; - - EnumNameCollection<TypeAttributes> typeAttributes = new EnumNameCollection<TypeAttributes>() { - { TypeAttributes.Abstract, "abstract" }, - { TypeAttributes.Sealed, "sealed" }, - { TypeAttributes.SpecialName, "specialname" }, - { TypeAttributes.Import, "import" }, - { TypeAttributes.Serializable, "serializable" }, - { TypeAttributes.WindowsRuntime, "windowsruntime" }, - { TypeAttributes.BeforeFieldInit, "beforefieldinit" }, - { TypeAttributes.HasSecurity, null }, - }; - - public void DisassembleType(TypeDefinition type) - { - // start writing IL - output.WriteDefinition(".class ", type); - - if ((type.Attributes & TypeAttributes.ClassSemanticMask) == TypeAttributes.Interface) - output.Write("interface "); - WriteEnum(type.Attributes & TypeAttributes.VisibilityMask, typeVisibility); - WriteEnum(type.Attributes & TypeAttributes.LayoutMask, typeLayout); - WriteEnum(type.Attributes & TypeAttributes.StringFormatMask, typeStringFormat); - const TypeAttributes masks = TypeAttributes.ClassSemanticMask | TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.StringFormatMask; - WriteFlags(type.Attributes & ~masks, typeAttributes); - - output.Write(DisassemblerHelpers.Escape(type.DeclaringType != null ? type.Name : type.FullName)); - WriteTypeParameters(output, type); - output.MarkFoldStart(defaultCollapsed: isInType); - output.WriteLine(); - - if (type.BaseType != null) { - output.Indent(); - output.Write("extends "); - type.BaseType.WriteTo(output, ILNameSyntax.TypeName); - output.WriteLine(); - output.Unindent(); - } - if (type.HasInterfaces) { - output.Indent(); - for (int index = 0; index < type.Interfaces.Count; index++) { - if (index > 0) - output.WriteLine(","); - if (index == 0) - output.Write("implements "); - else - output.Write(" "); - type.Interfaces[index].InterfaceType.WriteTo(output, ILNameSyntax.TypeName); - } - output.WriteLine(); - output.Unindent(); - } - - output.WriteLine("{"); - output.Indent(); - bool oldIsInType = isInType; - isInType = true; - WriteAttributes(type.CustomAttributes); - WriteSecurityDeclarations(type); - if (type.HasLayoutInfo) { - output.WriteLine(".pack {0}", type.PackingSize); - output.WriteLine(".size {0}", type.ClassSize); - output.WriteLine(); - } - if (type.HasNestedTypes) { - output.WriteLine("// Nested Types"); - foreach (var nestedType in type.NestedTypes) { - cancellationToken.ThrowIfCancellationRequested(); - DisassembleType(nestedType); - output.WriteLine(); - } - output.WriteLine(); - } - if (type.HasFields) { - output.WriteLine("// Fields"); - foreach (var field in type.Fields) { - cancellationToken.ThrowIfCancellationRequested(); - DisassembleField(field); - } - output.WriteLine(); - } - if (type.HasMethods) { - output.WriteLine("// Methods"); - foreach (var m in type.Methods) { - cancellationToken.ThrowIfCancellationRequested(); - DisassembleMethod(m); - output.WriteLine(); - } - } - if (type.HasEvents) { - output.WriteLine("// Events"); - foreach (var ev in type.Events) { - cancellationToken.ThrowIfCancellationRequested(); - DisassembleEvent(ev); - output.WriteLine(); - } - output.WriteLine(); - } - if (type.HasProperties) { - output.WriteLine("// Properties"); - foreach (var prop in type.Properties) { - cancellationToken.ThrowIfCancellationRequested(); - DisassembleProperty(prop); - } - output.WriteLine(); - } - CloseBlock("end of class " + (type.DeclaringType != null ? type.Name : type.FullName)); - isInType = oldIsInType; - } - - void WriteTypeParameters(ITextOutput output, IGenericParameterProvider p) - { - if (p.HasGenericParameters) { - output.Write('<'); - for (int i = 0; i < p.GenericParameters.Count; i++) { - if (i > 0) - output.Write(", "); - GenericParameter gp = p.GenericParameters[i]; - if (gp.HasReferenceTypeConstraint) { - output.Write("class "); - } else if (gp.HasNotNullableValueTypeConstraint) { - output.Write("valuetype "); - } - if (gp.HasDefaultConstructorConstraint) { - output.Write(".ctor "); - } - if (gp.HasConstraints) { - output.Write('('); - for (int j = 0; j < gp.Constraints.Count; j++) { - if (j > 0) - output.Write(", "); - gp.Constraints[j].WriteTo(output, ILNameSyntax.TypeName); - } - output.Write(") "); - } - if (gp.IsContravariant) { - output.Write('-'); - } else if (gp.IsCovariant) { - output.Write('+'); - } - output.Write(DisassemblerHelpers.Escape(gp.Name)); - } - output.Write('>'); - } - } - #endregion - - #region Helper methods - void WriteAttributes(Collection<CustomAttribute> attributes) - { - foreach (CustomAttribute a in attributes) { - output.Write(".custom "); - a.Constructor.WriteTo(output); - byte[] blob = a.GetBlob(); - if (blob != null) { - output.Write(" = "); - WriteBlob(blob); - } - output.WriteLine(); - } - } - - void WriteBlob(byte[] blob) - { - output.Write("("); - output.Indent(); - - for (int i = 0; i < blob.Length; i++) { - if (i % 16 == 0 && i < blob.Length - 1) { - output.WriteLine(); - } else { - output.Write(' '); - } - output.Write(blob[i].ToString("x2")); - } - - output.WriteLine(); - output.Unindent(); - output.Write(")"); - } - - void OpenBlock(bool defaultCollapsed) - { - output.MarkFoldStart(defaultCollapsed: defaultCollapsed); - output.WriteLine(); - output.WriteLine("{"); - output.Indent(); - } - - void CloseBlock(string comment = null) - { - output.Unindent(); - output.Write("}"); - if (comment != null) - output.Write(" // " + comment); - output.MarkFoldEnd(); - output.WriteLine(); - } - - void WriteFlags<T>(T flags, EnumNameCollection<T> flagNames) where T : struct - { - long val = Convert.ToInt64(flags); - long tested = 0; - foreach (var pair in flagNames) { - tested |= pair.Key; - if ((val & pair.Key) != 0 && pair.Value != null) { - output.Write(pair.Value); - output.Write(' '); - } - } - if ((val & ~tested) != 0) - output.Write("flag({0:x4}) ", val & ~tested); - } - - void WriteEnum<T>(T enumValue, EnumNameCollection<T> enumNames) where T : struct - { - long val = Convert.ToInt64(enumValue); - foreach (var pair in enumNames) { - if (pair.Key == val) { - if (pair.Value != null) { - output.Write(pair.Value); - output.Write(' '); - } - return; - } - } - if (val != 0) { - output.Write("flag({0:x4})", val); - output.Write(' '); - } - - } - - sealed class EnumNameCollection<T> : IEnumerable<KeyValuePair<long, string>> where T : struct - { - List<KeyValuePair<long, string>> names = new List<KeyValuePair<long, string>>(); - - public void Add(T flag, string name) - { - this.names.Add(new KeyValuePair<long, string>(Convert.ToInt64(flag), name)); - } - - public IEnumerator<KeyValuePair<long, string>> GetEnumerator() - { - return names.GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return names.GetEnumerator(); - } - } - #endregion - - public void DisassembleNamespace(string nameSpace, IEnumerable<TypeDefinition> types) - { - if (!string.IsNullOrEmpty(nameSpace)) { - output.Write(".namespace " + DisassemblerHelpers.Escape(nameSpace)); - OpenBlock(false); - } - bool oldIsInType = isInType; - isInType = true; - foreach (TypeDefinition td in types) { - cancellationToken.ThrowIfCancellationRequested(); - DisassembleType(td); - output.WriteLine(); - } - if (!string.IsNullOrEmpty(nameSpace)) { - CloseBlock(); - isInType = oldIsInType; - } - } - - public void WriteAssemblyHeader(AssemblyDefinition asm) - { - output.Write(".assembly "); - if (asm.Name.IsWindowsRuntime) - output.Write("windowsruntime "); - output.Write(DisassemblerHelpers.Escape(asm.Name.Name)); - OpenBlock(false); - WriteAttributes(asm.CustomAttributes); - WriteSecurityDeclarations(asm); - if (asm.Name.PublicKey != null && asm.Name.PublicKey.Length > 0) { - output.Write(".publickey = "); - WriteBlob(asm.Name.PublicKey); - output.WriteLine(); - } - if (asm.Name.HashAlgorithm != AssemblyHashAlgorithm.None) { - output.Write(".hash algorithm 0x{0:x8}", (int)asm.Name.HashAlgorithm); - if (asm.Name.HashAlgorithm == AssemblyHashAlgorithm.SHA1) - output.Write(" // SHA1"); - output.WriteLine(); - } - Version v = asm.Name.Version; - if (v != null) { - output.WriteLine(".ver {0}:{1}:{2}:{3}", v.Major, v.Minor, v.Build, v.Revision); - } - CloseBlock(); - } - - public void WriteAssemblyReferences(ModuleDefinition module) - { - foreach (var mref in module.ModuleReferences) { - output.WriteLine(".module extern {0}", DisassemblerHelpers.Escape(mref.Name)); - } - foreach (var aref in module.AssemblyReferences) { - output.Write(".assembly extern "); - if (aref.IsWindowsRuntime) - output.Write("windowsruntime "); - output.Write(DisassemblerHelpers.Escape(aref.Name)); - OpenBlock(false); - if (aref.PublicKeyToken != null) { - output.Write(".publickeytoken = "); - WriteBlob(aref.PublicKeyToken); - output.WriteLine(); - } - if (aref.Version != null) { - output.WriteLine(".ver {0}:{1}:{2}:{3}", aref.Version.Major, aref.Version.Minor, aref.Version.Build, aref.Version.Revision); - } - CloseBlock(); - } - } - - public void WriteModuleHeader(ModuleDefinition module) - { - if (module.HasExportedTypes) { - foreach (ExportedType exportedType in module.ExportedTypes) { - output.Write(".class extern "); - if (exportedType.IsForwarder) - output.Write("forwarder "); - output.Write(exportedType.DeclaringType != null ? exportedType.Name : exportedType.FullName); - OpenBlock(false); - if (exportedType.DeclaringType != null) - output.WriteLine(".class extern {0}", DisassemblerHelpers.Escape(exportedType.DeclaringType.FullName)); - else - output.WriteLine(".assembly extern {0}", DisassemblerHelpers.Escape(exportedType.Scope.Name)); - CloseBlock(); - } - } - - output.WriteLine(".module {0}", module.Name); - output.WriteLine("// MVID: {0}", module.Mvid.ToString("B").ToUpperInvariant()); - // TODO: imagebase, file alignment, stackreserve, subsystem - output.WriteLine(".corflags 0x{0:x} // {1}", module.Attributes, module.Attributes.ToString()); - - WriteAttributes(module.CustomAttributes); - } - - public void WriteModuleContents(ModuleDefinition module) - { - foreach (TypeDefinition td in module.Types) { - DisassembleType(td); - output.WriteLine(); - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs b/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs deleted file mode 100644 index bfab810cfc..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs +++ /dev/null @@ -1,78 +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; - -namespace ICSharpCode.Decompiler.FlowAnalysis -{ - /// <summary> - /// Describes the type of a control flow egde. - /// </summary> - public enum JumpType - { - /// <summary> - /// A regular control flow edge. - /// </summary> - Normal, - /// <summary> - /// Jump to exception handler (an exception occurred) - /// </summary> - JumpToExceptionHandler, - /// <summary> - /// Jump from try block to leave target: - /// This is not a real jump, as the finally handler is executed first! - /// </summary> - LeaveTry, - /// <summary> - /// Jump at endfinally (to any of the potential leave targets). - /// For any leave-instruction, control flow enters the finally block - the edge to the leave target (LeaveTry) is not a real control flow edge. - /// EndFinally edges are inserted at the end of the finally block, jumping to any of the targets of the leave instruction. - /// This edge type is only used when copying of finally blocks is disabled (with copying, a normal deterministic edge is used at each copy of the endfinally node). - /// </summary> - EndFinally - } - - /// <summary> - /// Represents an edge in the control flow graph, pointing from Source to Target. - /// </summary> - public sealed class ControlFlowEdge - { - public readonly ControlFlowNode Source; - public readonly ControlFlowNode Target; - public readonly JumpType Type; - - public ControlFlowEdge(ControlFlowNode source, ControlFlowNode target, JumpType type) - { - this.Source = source; - this.Target = target; - this.Type = type; - } - - public override string ToString() - { - switch (Type) { - case JumpType.Normal: - return "#" + Target.BlockIndex; - case JumpType.JumpToExceptionHandler: - return "e:#" + Target.BlockIndex; - default: - return Type.ToString() + ":#" + Target.BlockIndex; - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs b/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs deleted file mode 100644 index 7cc815a60f..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs +++ /dev/null @@ -1,191 +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.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Linq; -using System.Threading; - -using ICSharpCode.NRefactory.Utils; - -namespace ICSharpCode.Decompiler.FlowAnalysis -{ - /// <summary> - /// Contains the control flow graph. - /// </summary> - /// <remarks>Use ControlFlowGraph builder to create instances of the ControlFlowGraph.</remarks> - public sealed class ControlFlowGraph - { - readonly ReadOnlyCollection<ControlFlowNode> nodes; - - public ControlFlowNode EntryPoint { - get { return nodes[0]; } - } - - public ControlFlowNode RegularExit { - get { return nodes[1]; } - } - - public ControlFlowNode ExceptionalExit { - get { return nodes[2]; } - } - - public ReadOnlyCollection<ControlFlowNode> Nodes { - get { return nodes; } - } - - internal ControlFlowGraph(ControlFlowNode[] nodes) - { - this.nodes = new ReadOnlyCollection<ControlFlowNode>(nodes); - Debug.Assert(EntryPoint.NodeType == ControlFlowNodeType.EntryPoint); - Debug.Assert(RegularExit.NodeType == ControlFlowNodeType.RegularExit); - Debug.Assert(ExceptionalExit.NodeType == ControlFlowNodeType.ExceptionalExit); - } - - public GraphVizGraph ExportGraph() - { - GraphVizGraph graph = new GraphVizGraph(); - foreach (ControlFlowNode node in nodes) { - graph.AddNode(new GraphVizNode(node.BlockIndex) { label = node.ToString(), shape = "box" }); - } - foreach (ControlFlowNode node in nodes) { - foreach (ControlFlowEdge edge in node.Outgoing) { - GraphVizEdge e = new GraphVizEdge(edge.Source.BlockIndex, edge.Target.BlockIndex); - switch (edge.Type) { - case JumpType.Normal: - break; - case JumpType.LeaveTry: - case JumpType.EndFinally: - e.color = "red"; - break; - default: - e.color = "gray"; - //e.constraint = false; - break; - } - graph.AddEdge(e); - } - if (node.ImmediateDominator != null) { - graph.AddEdge(new GraphVizEdge(node.ImmediateDominator.BlockIndex, node.BlockIndex) { color = "green", constraint = false }); - } - } - return graph; - } - - /// <summary> - /// Resets "Visited" to false for all nodes in this graph. - /// </summary> - public void ResetVisited() - { - foreach (ControlFlowNode node in nodes) { - node.Visited = false; - } - } - - /// <summary> - /// Computes the dominator tree. - /// </summary> - public void ComputeDominance(CancellationToken cancellationToken = default(CancellationToken)) - { - // A Simple, Fast Dominance Algorithm - // Keith D. Cooper, Timothy J. Harvey and Ken Kennedy - - EntryPoint.ImmediateDominator = EntryPoint; - bool changed = true; - while (changed) { - changed = false; - ResetVisited(); - - cancellationToken.ThrowIfCancellationRequested(); - - // for all nodes b except the entry point - EntryPoint.TraversePreOrder( - b => b.Successors, - b => { - if (b != EntryPoint) { - ControlFlowNode newIdom = b.Predecessors.First(block => block.Visited && block != b); - // for all other predecessors p of b - foreach (ControlFlowNode p in b.Predecessors) { - if (p != b && p.ImmediateDominator != null) { - newIdom = FindCommonDominator(p, newIdom); - } - } - if (b.ImmediateDominator != newIdom) { - b.ImmediateDominator = newIdom; - changed = true; - } - } - }); - } - EntryPoint.ImmediateDominator = null; - foreach (ControlFlowNode node in nodes) { - if (node.ImmediateDominator != null) - node.ImmediateDominator.DominatorTreeChildren.Add(node); - } - } - - static ControlFlowNode FindCommonDominator(ControlFlowNode b1, ControlFlowNode b2) - { - // Here we could use the postorder numbers to get rid of the hashset, see "A Simple, Fast Dominance Algorithm" - HashSet<ControlFlowNode> path1 = new HashSet<ControlFlowNode>(); - while (b1 != null && path1.Add(b1)) - b1 = b1.ImmediateDominator; - while (b2 != null) { - if (path1.Contains(b2)) - return b2; - else - b2 = b2.ImmediateDominator; - } - throw new Exception("No common dominator found!"); - } - - /// <summary> - /// Computes dominance frontiers. - /// This method requires that the dominator tree is already computed! - /// </summary> - public void ComputeDominanceFrontier() - { - ResetVisited(); - - EntryPoint.TraversePostOrder( - b => b.DominatorTreeChildren, - n => { - //logger.WriteLine("Calculating dominance frontier for " + n.Name); - n.DominanceFrontier = new HashSet<ControlFlowNode>(); - // DF_local computation - foreach (ControlFlowNode succ in n.Successors) { - if (succ.ImmediateDominator != n) { - //logger.WriteLine(" local: " + succ.Name); - n.DominanceFrontier.Add(succ); - } - } - // DF_up computation - foreach (ControlFlowNode child in n.DominatorTreeChildren) { - foreach (ControlFlowNode p in child.DominanceFrontier) { - if (p.ImmediateDominator != n) { - //logger.WriteLine(" DF_up: " + p.Name + " (child=" + child.Name); - n.DominanceFrontier.Add(p); - } - } - } - }); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs b/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs deleted file mode 100644 index 4b0c434d48..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs +++ /dev/null @@ -1,439 +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.Collections.Generic; -using System.Diagnostics; -using System.Linq; - -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.FlowAnalysis -{ - /// <summary> - /// Constructs the Control Flow Graph from a Cecil method body. - /// </summary> - public sealed class ControlFlowGraphBuilder - { - public static ControlFlowGraph Build(MethodBody methodBody) - { - return new ControlFlowGraphBuilder(methodBody).Build(); - } - - // This option controls how finally blocks are handled: - // false means that the endfinally instruction will jump to any of the leave targets (EndFinally edge type). - // true means that a copy of the whole finally block is created for each leave target. In this case, each endfinally node will be connected with the leave - // target using a normal edge. - bool copyFinallyBlocks = false; - - MethodBody methodBody; - int[] offsets; // array index = instruction index; value = IL offset - bool[] hasIncomingJumps; // array index = instruction index - List<ControlFlowNode> nodes = new List<ControlFlowNode>(); - ControlFlowNode entryPoint; - ControlFlowNode regularExit; - ControlFlowNode exceptionalExit; - - private ControlFlowGraphBuilder(MethodBody methodBody) - { - this.methodBody = methodBody; - offsets = methodBody.Instructions.Select(i => i.Offset).ToArray(); - hasIncomingJumps = new bool[methodBody.Instructions.Count]; - - entryPoint = new ControlFlowNode(0, 0, ControlFlowNodeType.EntryPoint); - nodes.Add(entryPoint); - regularExit = new ControlFlowNode(1, -1, ControlFlowNodeType.RegularExit); - nodes.Add(regularExit); - exceptionalExit = new ControlFlowNode(2, -1, ControlFlowNodeType.ExceptionalExit); - nodes.Add(exceptionalExit); - Debug.Assert(nodes.Count == 3); - } - - /// <summary> - /// Determines the index of the instruction (for use with the hasIncomingJumps array) - /// </summary> - int GetInstructionIndex(Instruction inst) - { - int index = Array.BinarySearch(offsets, inst.Offset); - Debug.Assert(index >= 0); - return index; - } - - /// <summary> - /// Builds the ControlFlowGraph. - /// </summary> - public ControlFlowGraph Build() - { - CalculateHasIncomingJumps(); - CreateNodes(); - CreateRegularControlFlow(); - CreateExceptionalControlFlow(); - if (copyFinallyBlocks) - CopyFinallyBlocksIntoLeaveEdges(); - else - TransformLeaveEdges(); - return new ControlFlowGraph(nodes.ToArray()); - } - - #region Step 1: calculate which instructions are the targets of jump instructions. - void CalculateHasIncomingJumps() - { - foreach (Instruction inst in methodBody.Instructions) { - if (inst.OpCode.OperandType == OperandType.InlineBrTarget || inst.OpCode.OperandType == OperandType.ShortInlineBrTarget) { - hasIncomingJumps[GetInstructionIndex((Instruction)inst.Operand)] = true; - } else if (inst.OpCode.OperandType == OperandType.InlineSwitch) { - foreach (Instruction i in (Instruction[])inst.Operand) - hasIncomingJumps[GetInstructionIndex(i)] = true; - } - } - foreach (ExceptionHandler eh in methodBody.ExceptionHandlers) { - if (eh.FilterStart != null) { - hasIncomingJumps[GetInstructionIndex(eh.FilterStart)] = true; - } - hasIncomingJumps[GetInstructionIndex(eh.HandlerStart)] = true; - } - } - #endregion - - #region Step 2: create nodes - void CreateNodes() - { - // Step 2a: find basic blocks and create nodes for them - for (int i = 0; i < methodBody.Instructions.Count; i++) { - Instruction blockStart = methodBody.Instructions[i]; - ExceptionHandler blockStartEH = FindInnermostExceptionHandler(blockStart.Offset); - // try and see how big we can make that block: - for (; i + 1 < methodBody.Instructions.Count; i++) { - Instruction inst = methodBody.Instructions[i]; - if (IsBranch(inst.OpCode) || CanThrowException(inst.OpCode)) - break; - if (hasIncomingJumps[i + 1]) - break; - if (inst.Next != null) { - // ensure that blocks never contain instructions from different try blocks - ExceptionHandler instEH = FindInnermostExceptionHandler(inst.Next.Offset); - if (instEH != blockStartEH) - break; - } - } - - nodes.Add(new ControlFlowNode(nodes.Count, blockStart, methodBody.Instructions[i])); - } - // Step 2b: Create special nodes for the exception handling constructs - foreach (ExceptionHandler handler in methodBody.ExceptionHandlers) { - if (handler.HandlerType == ExceptionHandlerType.Filter) - throw new NotSupportedException(); - ControlFlowNode endFinallyOrFaultNode = null; - if (handler.HandlerType == ExceptionHandlerType.Finally || handler.HandlerType == ExceptionHandlerType.Fault) { - endFinallyOrFaultNode = new ControlFlowNode(nodes.Count, handler.HandlerEnd.Offset, ControlFlowNodeType.EndFinallyOrFault); - nodes.Add(endFinallyOrFaultNode); - } - nodes.Add(new ControlFlowNode(nodes.Count, handler, endFinallyOrFaultNode)); - } - } - #endregion - - #region Step 3: create edges for the normal flow of control (assuming no exceptions thrown) - void CreateRegularControlFlow() - { - CreateEdge(entryPoint, methodBody.Instructions[0], JumpType.Normal); - foreach (ControlFlowNode node in nodes) { - if (node.End != null) { - // create normal edges from one instruction to the next - if (!OpCodeInfo.IsUnconditionalBranch(node.End.OpCode)) - CreateEdge(node, node.End.Next, JumpType.Normal); - - // create edges for branch instructions - if (node.End.OpCode.OperandType == OperandType.InlineBrTarget || node.End.OpCode.OperandType == OperandType.ShortInlineBrTarget) { - if (node.End.OpCode == OpCodes.Leave || node.End.OpCode == OpCodes.Leave_S) { - var handlerBlock = FindInnermostHandlerBlock(node.End.Offset); - if (handlerBlock.NodeType == ControlFlowNodeType.FinallyOrFaultHandler) - CreateEdge(node, (Instruction)node.End.Operand, JumpType.LeaveTry); - else - CreateEdge(node, (Instruction)node.End.Operand, JumpType.Normal); - } else { - CreateEdge(node, (Instruction)node.End.Operand, JumpType.Normal); - } - } else if (node.End.OpCode.OperandType == OperandType.InlineSwitch) { - foreach (Instruction i in (Instruction[])node.End.Operand) - CreateEdge(node, i, JumpType.Normal); - } - - // create edges for return instructions - if (node.End.OpCode.FlowControl == FlowControl.Return) { - switch (node.End.OpCode.Code) { - case Code.Ret: - CreateEdge(node, regularExit, JumpType.Normal); - break; - case Code.Endfinally: - ControlFlowNode handlerBlock = FindInnermostHandlerBlock(node.End.Offset); - if (handlerBlock.EndFinallyOrFaultNode == null) - throw new InvalidProgramException("Found endfinally in block " + handlerBlock); - CreateEdge(node, handlerBlock.EndFinallyOrFaultNode, JumpType.Normal); - break; - default: - throw new NotSupportedException(node.End.OpCode.ToString()); - } - } - } - } - } - #endregion - - #region Step 4: create edges for the exceptional control flow (from instructions that might throw, to the innermost containing exception handler) - void CreateExceptionalControlFlow() - { - foreach (ControlFlowNode node in nodes) { - if (node.End != null && CanThrowException(node.End.OpCode)) { - CreateEdge(node, FindInnermostExceptionHandlerNode(node.End.Offset), JumpType.JumpToExceptionHandler); - } - if (node.ExceptionHandler != null) { - if (node.EndFinallyOrFaultNode != null) { - // For Fault and Finally blocks, create edge from "EndFinally" to next exception handler. - // This represents the exception bubbling up after finally block was executed. - CreateEdge(node.EndFinallyOrFaultNode, FindParentExceptionHandlerNode(node), JumpType.JumpToExceptionHandler); - } else { - // For Catch blocks, create edge from "CatchHandler" block (at beginning) to next exception handler. - // This represents the exception bubbling up because it did not match the type of the catch block. - CreateEdge(node, FindParentExceptionHandlerNode(node), JumpType.JumpToExceptionHandler); - } - CreateEdge(node, node.ExceptionHandler.HandlerStart, JumpType.Normal); - } - } - } - - ExceptionHandler FindInnermostExceptionHandler(int instructionOffsetInTryBlock) - { - foreach (ExceptionHandler h in methodBody.ExceptionHandlers) { - if (h.TryStart.Offset <= instructionOffsetInTryBlock && instructionOffsetInTryBlock < h.TryEnd.Offset) { - return h; - } - } - return null; - } - - ControlFlowNode FindInnermostExceptionHandlerNode(int instructionOffsetInTryBlock) - { - ExceptionHandler h = FindInnermostExceptionHandler(instructionOffsetInTryBlock); - if (h != null) - return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null); - else - return exceptionalExit; - } - - ControlFlowNode FindInnermostHandlerBlock(int instructionOffset) - { - foreach (ExceptionHandler h in methodBody.ExceptionHandlers) { - if (h.TryStart.Offset <= instructionOffset && instructionOffset < h.TryEnd.Offset - || h.HandlerStart.Offset <= instructionOffset && instructionOffset < h.HandlerEnd.Offset) - { - return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null); - } - } - return exceptionalExit; - } - - ControlFlowNode FindParentExceptionHandlerNode(ControlFlowNode exceptionHandler) - { - Debug.Assert(exceptionHandler.NodeType == ControlFlowNodeType.CatchHandler - || exceptionHandler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler); - int offset = exceptionHandler.ExceptionHandler.TryStart.Offset; - for (int i = exceptionHandler.BlockIndex + 1; i < nodes.Count; i++) { - ExceptionHandler h = nodes[i].ExceptionHandler; - if (h != null && h.TryStart.Offset <= offset && offset < h.TryEnd.Offset) - return nodes[i]; - } - return exceptionalExit; - } - #endregion - - #region Step 5a: replace LeaveTry edges with EndFinally edges - // this is used only for copyFinallyBlocks==false; see Step 5b otherwise - void TransformLeaveEdges() - { - for (int i = nodes.Count - 1; i >= 0; i--) { - ControlFlowNode node = nodes[i]; - if (node.End != null && node.Outgoing.Count == 1 && node.Outgoing[0].Type == JumpType.LeaveTry) { - Debug.Assert(node.End.OpCode == OpCodes.Leave || node.End.OpCode == OpCodes.Leave_S); - - ControlFlowNode target = node.Outgoing[0].Target; - // remove the edge - target.Incoming.Remove(node.Outgoing[0]); - node.Outgoing.Clear(); - - ControlFlowNode handler = FindInnermostExceptionHandlerNode(node.End.Offset); - Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler); - - CreateEdge(node, handler, JumpType.Normal); - CreateEdge(handler.EndFinallyOrFaultNode, target, JumpType.EndFinally); - } - } - } - #endregion - - #region Step 5b: copy finally blocks into the LeaveTry edges - void CopyFinallyBlocksIntoLeaveEdges() - { - // We need to process try-finally blocks inside-out. - // We'll do that by going through all instructions in reverse order - for (int i = nodes.Count - 1; i >= 0; i--) { - ControlFlowNode node = nodes[i]; - if (node.End != null && node.Outgoing.Count == 1 && node.Outgoing[0].Type == JumpType.LeaveTry) { - Debug.Assert(node.End.OpCode == OpCodes.Leave || node.End.OpCode == OpCodes.Leave_S); - - ControlFlowNode target = node.Outgoing[0].Target; - // remove the edge - target.Incoming.Remove(node.Outgoing[0]); - node.Outgoing.Clear(); - - ControlFlowNode handler = FindInnermostExceptionHandlerNode(node.End.Offset); - Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler); - - ControlFlowNode copy = CopyFinallySubGraph(handler, handler.EndFinallyOrFaultNode, target); - CreateEdge(node, copy, JumpType.Normal); - } - } - } - - /// <summary> - /// Creates a copy of all nodes pointing to 'end' and replaces those references with references to 'newEnd'. - /// Nodes pointing to the copied node are copied recursively to update those references, too. - /// This recursion stops at 'start'. The modified version of start is returned. - /// </summary> - ControlFlowNode CopyFinallySubGraph(ControlFlowNode start, ControlFlowNode end, ControlFlowNode newEnd) - { - return new CopyFinallySubGraphLogic(this, start, end, newEnd).CopyFinallySubGraph(); - } - - class CopyFinallySubGraphLogic - { - readonly ControlFlowGraphBuilder builder; - readonly Dictionary<ControlFlowNode, ControlFlowNode> oldToNew = new Dictionary<ControlFlowNode, ControlFlowNode>(); - readonly ControlFlowNode start; - readonly ControlFlowNode end; - readonly ControlFlowNode newEnd; - - public CopyFinallySubGraphLogic(ControlFlowGraphBuilder builder, ControlFlowNode start, ControlFlowNode end, ControlFlowNode newEnd) - { - this.builder = builder; - this.start = start; - this.end = end; - this.newEnd = newEnd; - } - - internal ControlFlowNode CopyFinallySubGraph() - { - foreach (ControlFlowNode n in end.Predecessors) { - CollectNodes(n); - } - foreach (var pair in oldToNew) - ReconstructEdges(pair.Key, pair.Value); - return GetNew(start); - } - - void CollectNodes(ControlFlowNode node) - { - if (node == end || node == newEnd) - throw new InvalidOperationException("unexpected cycle involving finally construct"); - if (!oldToNew.ContainsKey(node)) { - int newBlockIndex = builder.nodes.Count; - ControlFlowNode copy; - switch (node.NodeType) { - case ControlFlowNodeType.Normal: - copy = new ControlFlowNode(newBlockIndex, node.Start, node.End); - break; - case ControlFlowNodeType.FinallyOrFaultHandler: - copy = new ControlFlowNode(newBlockIndex, node.ExceptionHandler, node.EndFinallyOrFaultNode); - break; - default: - // other nodes shouldn't occur when copying finally blocks - throw new NotSupportedException(node.NodeType.ToString()); - } - copy.CopyFrom = node; - builder.nodes.Add(copy); - oldToNew.Add(node, copy); - - if (node != start) { - foreach (ControlFlowNode n in node.Predecessors) { - CollectNodes(n); - } - } - } - } - - void ReconstructEdges(ControlFlowNode oldNode, ControlFlowNode newNode) - { - foreach (ControlFlowEdge oldEdge in oldNode.Outgoing) { - builder.CreateEdge(newNode, GetNew(oldEdge.Target), oldEdge.Type); - } - } - - ControlFlowNode GetNew(ControlFlowNode oldNode) - { - if (oldNode == end) - return newEnd; - ControlFlowNode newNode; - if (oldToNew.TryGetValue(oldNode, out newNode)) - return newNode; - return oldNode; - } - } - #endregion - - #region CreateEdge methods - void CreateEdge(ControlFlowNode fromNode, Instruction toInstruction, JumpType type) - { - CreateEdge(fromNode, nodes.Single(n => n.Start == toInstruction), type); - } - - void CreateEdge(ControlFlowNode fromNode, ControlFlowNode toNode, JumpType type) - { - ControlFlowEdge edge = new ControlFlowEdge(fromNode, toNode, type); - fromNode.Outgoing.Add(edge); - toNode.Incoming.Add(edge); - } - #endregion - - #region OpCode info - static bool CanThrowException(OpCode opcode) - { - if (opcode.OpCodeType == OpCodeType.Prefix) - return false; - return OpCodeInfo.Get(opcode).CanThrow; - } - - static bool IsBranch(OpCode opcode) - { - if (opcode.OpCodeType == OpCodeType.Prefix) - return false; - switch (opcode.FlowControl) { - case FlowControl.Cond_Branch: - case FlowControl.Branch: - case FlowControl.Throw: - case FlowControl.Return: - return true; - case FlowControl.Next: - case FlowControl.Call: - return false; - default: - throw new NotSupportedException(opcode.FlowControl.ToString()); - } - } - #endregion - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs b/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs deleted file mode 100644 index cab507bc95..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs +++ /dev/null @@ -1,305 +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.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; - -using ICSharpCode.Decompiler.Disassembler; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.FlowAnalysis -{ - /// <summary> - /// Type of the control flow node - /// </summary> - public enum ControlFlowNodeType - { - /// <summary> - /// A normal node represents a basic block. - /// </summary> - Normal, - /// <summary> - /// The entry point of the method. - /// </summary> - EntryPoint, - /// <summary> - /// The exit point of the method (every ret instruction branches to this node) - /// </summary> - RegularExit, - /// <summary> - /// This node represents leaving a method irregularly by throwing an exception. - /// </summary> - ExceptionalExit, - /// <summary> - /// This node is used as a header for exception handler blocks. - /// </summary> - CatchHandler, - /// <summary> - /// This node is used as a header for finally blocks and fault blocks. - /// Every leave instruction in the try block leads to the handler of the containing finally block; - /// and exceptional control flow also leads to this handler. - /// </summary> - FinallyOrFaultHandler, - /// <summary> - /// This node is used as footer for finally blocks and fault blocks. - /// Depending on the "copyFinallyBlocks" option used when creating the graph, it is connected with all leave targets using - /// EndFinally edges (when not copying); or with a specific leave target using a normal edge (when copying). - /// For fault blocks, an exception edge is used to represent the "re-throwing" of the exception. - /// </summary> - EndFinallyOrFault - } - - /// <summary> - /// Represents a block in the control flow graph. - /// </summary> - public sealed class ControlFlowNode - { - /// <summary> - /// Index of this node in the ControlFlowGraph.Nodes collection. - /// </summary> - public readonly int BlockIndex; - - /// <summary> - /// Gets the IL offset of this node. - /// </summary> - public readonly int Offset; - - /// <summary> - /// Type of the node. - /// </summary> - public readonly ControlFlowNodeType NodeType; - - /// <summary> - /// If this node is a FinallyOrFaultHandler node, this field points to the corresponding EndFinallyOrFault node. - /// Otherwise, this field is null. - /// </summary> - public readonly ControlFlowNode EndFinallyOrFaultNode; - - /// <summary> - /// Visited flag, used in various algorithms. - /// Before using it in your algorithm, reset it to false by calling ControlFlowGraph.ResetVisited(); - /// </summary> - public bool Visited; - - /// <summary> - /// Gets whether this node is reachable. Requires that dominance is computed! - /// </summary> - public bool IsReachable { - get { return ImmediateDominator != null || NodeType == ControlFlowNodeType.EntryPoint; } - } - - /// <summary> - /// Signalizes that this node is a copy of another node. - /// </summary> - public ControlFlowNode CopyFrom { get; internal set; } - - /// <summary> - /// Gets the immediate dominator (the parent in the dominator tree). - /// Null if dominance has not been calculated; or if the node is unreachable. - /// </summary> - public ControlFlowNode ImmediateDominator { get; internal set; } - - /// <summary> - /// List of children in the dominator tree. - /// </summary> - public readonly List<ControlFlowNode> DominatorTreeChildren = new List<ControlFlowNode>(); - - /// <summary> - /// The dominance frontier of this node. - /// This is the set of nodes for which this node dominates a predecessor, but which are not strictly dominated by this node. - /// </summary> - /// <remarks> - /// b.DominanceFrontier = { y in CFG; (exists p in predecessors(y): b dominates p) and not (b strictly dominates y)} - /// </remarks> - public HashSet<ControlFlowNode> DominanceFrontier; - - /// <summary> - /// Start of code block represented by this node. Only set for nodetype == Normal. - /// </summary> - public readonly Instruction Start; - - /// <summary> - /// End of the code block represented by this node. Only set for nodetype == Normal. - /// The end is exclusive, the end instruction itself does not belong to this block. - /// </summary> - public readonly Instruction End; - - /// <summary> - /// Gets the exception handler associated with this node. - /// Only set for nodetype == CatchHandler or nodetype == FinallyOrFaultHandler. - /// </summary> - public readonly ExceptionHandler ExceptionHandler; - - /// <summary> - /// List of incoming control flow edges. - /// </summary> - public readonly List<ControlFlowEdge> Incoming = new List<ControlFlowEdge>(); - - /// <summary> - /// List of outgoing control flow edges. - /// </summary> - public readonly List<ControlFlowEdge> Outgoing = new List<ControlFlowEdge>(); - - /// <summary> - /// Any user data - /// </summary> - public object UserData; - - internal ControlFlowNode(int blockIndex, int offset, ControlFlowNodeType nodeType) - { - this.BlockIndex = blockIndex; - this.Offset = offset; - this.NodeType = nodeType; - } - - internal ControlFlowNode(int blockIndex, Instruction start, Instruction end) - { - if (start == null) - throw new ArgumentNullException("start"); - if (end == null) - throw new ArgumentNullException("end"); - this.BlockIndex = blockIndex; - this.NodeType = ControlFlowNodeType.Normal; - this.Start = start; - this.End = end; - this.Offset = start.Offset; - } - - internal ControlFlowNode(int blockIndex, ExceptionHandler exceptionHandler, ControlFlowNode endFinallyOrFaultNode) - { - this.BlockIndex = blockIndex; - this.NodeType = endFinallyOrFaultNode != null ? ControlFlowNodeType.FinallyOrFaultHandler : ControlFlowNodeType.CatchHandler; - this.ExceptionHandler = exceptionHandler; - this.EndFinallyOrFaultNode = endFinallyOrFaultNode; - Debug.Assert((exceptionHandler.HandlerType == ExceptionHandlerType.Finally || exceptionHandler.HandlerType == ExceptionHandlerType.Fault) == (endFinallyOrFaultNode != null)); - this.Offset = exceptionHandler.HandlerStart.Offset; - } - - /// <summary> - /// Gets all predecessors (=sources of incoming edges) - /// </summary> - public IEnumerable<ControlFlowNode> Predecessors { - get { - return Incoming.Select(e => e.Source); - } - } - - /// <summary> - /// Gets all successors (=targets of outgoing edges) - /// </summary> - public IEnumerable<ControlFlowNode> Successors { - get { - return Outgoing.Select(e => e.Target); - } - } - - /// <summary> - /// Gets all instructions in this node. - /// Returns an empty list for special nodes that don't have any instructions. - /// </summary> - public IEnumerable<Instruction> Instructions { - get { - Instruction inst = Start; - if (inst != null) { - yield return inst; - while (inst != End) { - inst = inst.Next; - yield return inst; - } - } - } - } - - public void TraversePreOrder(Func<ControlFlowNode, IEnumerable<ControlFlowNode>> children, Action<ControlFlowNode> visitAction) - { - if (Visited) - return; - Visited = true; - visitAction(this); - foreach (ControlFlowNode t in children(this)) - t.TraversePreOrder(children, visitAction); - } - - public void TraversePostOrder(Func<ControlFlowNode, IEnumerable<ControlFlowNode>> children, Action<ControlFlowNode> visitAction) - { - if (Visited) - return; - Visited = true; - foreach (ControlFlowNode t in children(this)) - t.TraversePostOrder(children, visitAction); - visitAction(this); - } - - public override string ToString() - { - StringWriter writer = new StringWriter(); - switch (NodeType) { - case ControlFlowNodeType.Normal: - writer.Write("Block #{0}", BlockIndex); - if (Start != null) - writer.Write(": IL_{0:x4}", Start.Offset); - if (End != null) - writer.Write(" to IL_{0:x4}", End.GetEndOffset()); - break; - case ControlFlowNodeType.CatchHandler: - case ControlFlowNodeType.FinallyOrFaultHandler: - writer.Write("Block #{0}: {1}: ", BlockIndex, NodeType); - Disassembler.DisassemblerHelpers.WriteTo(ExceptionHandler, new PlainTextOutput(writer)); - break; - default: - writer.Write("Block #{0}: {1}", BlockIndex, NodeType); - break; - } -// if (ImmediateDominator != null) { -// writer.WriteLine(); -// writer.Write("ImmediateDominator: #{0}", ImmediateDominator.BlockIndex); -// } - if (DominanceFrontier != null && DominanceFrontier.Any()) { - writer.WriteLine(); - writer.Write("DominanceFrontier: " + string.Join(",", DominanceFrontier.OrderBy(d => d.BlockIndex).Select(d => d.BlockIndex.ToString()))); - } - foreach (Instruction inst in this.Instructions) { - writer.WriteLine(); - Disassembler.DisassemblerHelpers.WriteTo(inst, new PlainTextOutput(writer)); - } - if (UserData != null) { - writer.WriteLine(); - writer.Write(UserData.ToString()); - } - return writer.ToString(); - } - - /// <summary> - /// Gets whether <c>this</c> dominates <paramref name="node"/>. - /// </summary> - public bool Dominates(ControlFlowNode node) - { - // TODO: this can be made O(1) by numbering the dominator tree - ControlFlowNode tmp = node; - while (tmp != null) { - if (tmp == this) - return true; - tmp = tmp.ImmediateDominator; - } - return false; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs b/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs deleted file mode 100644 index d00f82a8e6..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs +++ /dev/null @@ -1,241 +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.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.FlowAnalysis -{ - /// <summary> - /// Detects the structure of the control flow (exception blocks and loops). - /// </summary> - public class ControlStructureDetector - { - public static ControlStructure DetectStructure(ControlFlowGraph g, IEnumerable<ExceptionHandler> exceptionHandlers, CancellationToken cancellationToken) - { - ControlStructure root = new ControlStructure(new HashSet<ControlFlowNode>(g.Nodes), g.EntryPoint, ControlStructureType.Root); - // First build a structure tree out of the exception table - DetectExceptionHandling(root, g, exceptionHandlers); - // Then run the loop detection. - DetectLoops(g, root, cancellationToken); - return root; - } - - #region Exception Handling - static void DetectExceptionHandling(ControlStructure current, ControlFlowGraph g, IEnumerable<ExceptionHandler> exceptionHandlers) - { - // We rely on the fact that the exception handlers are sorted so that the innermost come first. - // For each exception handler, we determine the nodes and substructures inside that handler, and move them into a new substructure. - // This is always possible because exception handlers are guaranteed (by the CLR spec) to be properly nested and non-overlapping; - // so they directly form the tree that we need. - foreach (ExceptionHandler eh in exceptionHandlers) { - var tryNodes = FindNodes(current, eh.TryStart, eh.TryEnd); - current.Nodes.ExceptWith(tryNodes); - ControlStructure tryBlock = new ControlStructure( - tryNodes, - g.Nodes.Single(n => n.Start == eh.TryStart), - ControlStructureType.Try); - tryBlock.ExceptionHandler = eh; - MoveControlStructures(current, tryBlock, eh.TryStart, eh.TryEnd); - current.Children.Add(tryBlock); - - if (eh.FilterStart != null) { - throw new NotSupportedException(); - } - - var handlerNodes = FindNodes(current, eh.HandlerStart, eh.HandlerEnd); - var handlerNode = current.Nodes.Single(n => n.ExceptionHandler == eh); - handlerNodes.Add(handlerNode); - if (handlerNode.EndFinallyOrFaultNode != null) - handlerNodes.Add(handlerNode.EndFinallyOrFaultNode); - current.Nodes.ExceptWith(handlerNodes); - ControlStructure handlerBlock = new ControlStructure( - handlerNodes, handlerNode, ControlStructureType.Handler); - handlerBlock.ExceptionHandler = eh; - MoveControlStructures(current, handlerBlock, eh.HandlerStart, eh.HandlerEnd); - current.Children.Add(handlerBlock); - } - } - - /// <summary> - /// Removes all nodes from start to end (exclusive) from this ControlStructure and moves them to the target structure. - /// </summary> - static HashSet<ControlFlowNode> FindNodes(ControlStructure current, Instruction startInst, Instruction endInst) - { - HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>(); - int start = startInst.Offset; - int end = endInst.Offset; - foreach (var node in current.Nodes.ToArray()) { - if (node.Start != null && start <= node.Start.Offset && node.Start.Offset < end) { - result.Add(node); - } - } - return result; - } - - static void MoveControlStructures(ControlStructure current, ControlStructure target, Instruction startInst, Instruction endInst) - { - for (int i = 0; i < current.Children.Count; i++) { - var child = current.Children[i]; - if (startInst.Offset <= child.EntryPoint.Offset && child.EntryPoint.Offset < endInst.Offset) { - current.Children.RemoveAt(i--); - target.Children.Add(child); - target.AllNodes.UnionWith(child.AllNodes); - } - } - } - #endregion - - #region Loop Detection - // Loop detection works like this: - // We find a top-level loop by looking for its entry point, which is characterized by a node dominating its own predecessor. - // Then we determine all other nodes that belong to such a loop (all nodes which lead to the entry point, and are dominated by it). - // Finally, we check whether our result conforms with potential existing exception structures, and create the substructure for the loop if successful. - - // This algorithm is applied recursively for any substructures (both detected loops and exception blocks) - - // But maybe we should get rid of this complex stuff and instead treat every backward jump as a loop? - // That should still work with the IL produced by compilers, and has the advantage that the detected loop bodies are consecutive IL regions. - - static void DetectLoops(ControlFlowGraph g, ControlStructure current, CancellationToken cancellationToken) - { - if (!current.EntryPoint.IsReachable) - return; - g.ResetVisited(); - cancellationToken.ThrowIfCancellationRequested(); - FindLoops(current, current.EntryPoint); - foreach (ControlStructure loop in current.Children) - DetectLoops(g, loop, cancellationToken); - } - - static void FindLoops(ControlStructure current, ControlFlowNode node) - { - if (node.Visited) - return; - node.Visited = true; - if (current.Nodes.Contains(node) - && node.DominanceFrontier.Contains(node) - && !(node == current.EntryPoint && current.Type == ControlStructureType.Loop)) - { - HashSet<ControlFlowNode> loopContents = new HashSet<ControlFlowNode>(); - FindLoopContents(current, loopContents, node, node); - List<ControlStructure> containedChildStructures = new List<ControlStructure>(); - bool invalidNesting = false; - foreach (ControlStructure childStructure in current.Children) { - if (childStructure.AllNodes.IsSubsetOf(loopContents)) { - containedChildStructures.Add(childStructure); - } else if (childStructure.AllNodes.Intersect(loopContents).Any()) { - invalidNesting = true; - } - } - if (!invalidNesting) { - current.Nodes.ExceptWith(loopContents); - ControlStructure ctl = new ControlStructure(loopContents, node, ControlStructureType.Loop); - foreach (ControlStructure childStructure in containedChildStructures) { - ctl.Children.Add(childStructure); - current.Children.Remove(childStructure); - ctl.Nodes.ExceptWith(childStructure.AllNodes); - } - current.Children.Add(ctl); - } - } - foreach (var edge in node.Outgoing) { - FindLoops(current, edge.Target); - } - } - - static void FindLoopContents(ControlStructure current, HashSet<ControlFlowNode> loopContents, ControlFlowNode loopHead, ControlFlowNode node) - { - if (current.AllNodes.Contains(node) && loopHead.Dominates(node) && loopContents.Add(node)) { - foreach (var edge in node.Incoming) { - FindLoopContents(current, loopContents, loopHead, edge.Source); - } - } - } - #endregion - } - - public enum ControlStructureType - { - /// <summary> - /// The root block of the method - /// </summary> - Root, - /// <summary> - /// A nested control structure representing a loop. - /// </summary> - Loop, - /// <summary> - /// A nested control structure representing a try block. - /// </summary> - Try, - /// <summary> - /// A nested control structure representing a catch, finally, or fault block. - /// </summary> - Handler, - /// <summary> - /// A nested control structure representing an exception filter block. - /// </summary> - Filter - } - - /// <summary> - /// Represents the structure detected by the <see cref="ControlStructureDetector"/>. - /// - /// This is a tree of ControlStructure nodes. Each node contains a set of CFG nodes, and every CFG node is contained in exactly one ControlStructure node. - /// </summary> - public class ControlStructure - { - public readonly ControlStructureType Type; - public readonly List<ControlStructure> Children = new List<ControlStructure>(); - - /// <summary> - /// The nodes in this control structure. - /// </summary> - public readonly HashSet<ControlFlowNode> Nodes; - - /// <summary> - /// The nodes in this control structure and in all child control structures. - /// </summary> - public readonly HashSet<ControlFlowNode> AllNodes; - - /// <summary> - /// The entry point of this control structure. - /// </summary> - public readonly ControlFlowNode EntryPoint; - - /// <summary> - /// The exception handler associated with this Try,Handler or Finally structure. - /// </summary> - public ExceptionHandler ExceptionHandler; - - public ControlStructure(HashSet<ControlFlowNode> nodes, ControlFlowNode entryPoint, ControlStructureType type) - { - if (nodes == null) - throw new ArgumentNullException("nodes"); - this.Nodes = nodes; - this.EntryPoint = entryPoint; - this.Type = type; - this.AllNodes = new HashSet<ControlFlowNode>(nodes); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/OpCodeInfo.cs b/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/OpCodeInfo.cs deleted file mode 100644 index e26447ebc3..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/OpCodeInfo.cs +++ /dev/null @@ -1,312 +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.Collections.Generic; -using System.Linq; - -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.FlowAnalysis -{ - /// <summary> - /// Additional info about opcodes. - /// </summary> - sealed class OpCodeInfo - { - public static bool IsUnconditionalBranch(OpCode opcode) - { - if (opcode.OpCodeType == OpCodeType.Prefix) - return false; - switch (opcode.FlowControl) { - case FlowControl.Branch: - case FlowControl.Throw: - case FlowControl.Return: - return true; - case FlowControl.Next: - case FlowControl.Call: - case FlowControl.Cond_Branch: - return false; - default: - throw new NotSupportedException(opcode.FlowControl.ToString()); - } - } - - static readonly OpCodeInfo[] knownOpCodes = { - #region Base Instructions - new OpCodeInfo(OpCodes.Add) { CanThrow = false }, - new OpCodeInfo(OpCodes.Add_Ovf) { CanThrow = true }, - new OpCodeInfo(OpCodes.Add_Ovf_Un) { CanThrow = true }, - new OpCodeInfo(OpCodes.And) { CanThrow = false }, - new OpCodeInfo(OpCodes.Arglist) { CanThrow = false }, - new OpCodeInfo(OpCodes.Beq) { CanThrow = false }, - new OpCodeInfo(OpCodes.Beq_S) { CanThrow = false }, - new OpCodeInfo(OpCodes.Bge) { CanThrow = false }, - new OpCodeInfo(OpCodes.Bge_S) { CanThrow = false }, - new OpCodeInfo(OpCodes.Bge_Un) { CanThrow = false }, - new OpCodeInfo(OpCodes.Bge_Un_S) { CanThrow = false }, - new OpCodeInfo(OpCodes.Bgt) { CanThrow = false }, - new OpCodeInfo(OpCodes.Bgt_S) { CanThrow = false }, - new OpCodeInfo(OpCodes.Bgt_Un) { CanThrow = false }, - new OpCodeInfo(OpCodes.Bgt_Un_S) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ble) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ble_S) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ble_Un) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ble_Un_S) { CanThrow = false }, - new OpCodeInfo(OpCodes.Blt) { CanThrow = false }, - new OpCodeInfo(OpCodes.Blt_S) { CanThrow = false }, - new OpCodeInfo(OpCodes.Blt_Un) { CanThrow = false }, - new OpCodeInfo(OpCodes.Blt_Un_S) { CanThrow = false }, - new OpCodeInfo(OpCodes.Bne_Un) { CanThrow = false }, - new OpCodeInfo(OpCodes.Bne_Un_S) { CanThrow = false }, - new OpCodeInfo(OpCodes.Br) { CanThrow = false }, - new OpCodeInfo(OpCodes.Br_S) { CanThrow = false }, - new OpCodeInfo(OpCodes.Break) { CanThrow = true }, - new OpCodeInfo(OpCodes.Brfalse) { CanThrow = false }, - new OpCodeInfo(OpCodes.Brfalse_S) { CanThrow = false }, - new OpCodeInfo(OpCodes.Brtrue) { CanThrow = false }, - new OpCodeInfo(OpCodes.Brtrue_S) { CanThrow = false }, - new OpCodeInfo(OpCodes.Call) { CanThrow = true }, - new OpCodeInfo(OpCodes.Calli) { CanThrow = true }, - new OpCodeInfo(OpCodes.Ceq) { CanThrow = false }, - new OpCodeInfo(OpCodes.Cgt) { CanThrow = false }, - new OpCodeInfo(OpCodes.Cgt_Un) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ckfinite) { CanThrow = true }, - new OpCodeInfo(OpCodes.Clt) { CanThrow = false }, - new OpCodeInfo(OpCodes.Clt_Un) { CanThrow = false }, - // conv.<to type> - new OpCodeInfo(OpCodes.Conv_I1) { CanThrow = false }, - new OpCodeInfo(OpCodes.Conv_I2) { CanThrow = false }, - new OpCodeInfo(OpCodes.Conv_I4) { CanThrow = false }, - new OpCodeInfo(OpCodes.Conv_I8) { CanThrow = false }, - new OpCodeInfo(OpCodes.Conv_R4) { CanThrow = false }, - new OpCodeInfo(OpCodes.Conv_R8) { CanThrow = false }, - new OpCodeInfo(OpCodes.Conv_U1) { CanThrow = false }, - new OpCodeInfo(OpCodes.Conv_U2) { CanThrow = false }, - new OpCodeInfo(OpCodes.Conv_U4) { CanThrow = false }, - new OpCodeInfo(OpCodes.Conv_U8) { CanThrow = false }, - new OpCodeInfo(OpCodes.Conv_I) { CanThrow = false }, - new OpCodeInfo(OpCodes.Conv_U) { CanThrow = false }, - new OpCodeInfo(OpCodes.Conv_R_Un) { CanThrow = false }, - // conv.ovf.<to type> - new OpCodeInfo(OpCodes.Conv_Ovf_I1) { CanThrow = true}, - new OpCodeInfo(OpCodes.Conv_Ovf_I2) { CanThrow = true}, - new OpCodeInfo(OpCodes.Conv_Ovf_I4) { CanThrow = true}, - new OpCodeInfo(OpCodes.Conv_Ovf_I8) { CanThrow = true}, - new OpCodeInfo(OpCodes.Conv_Ovf_U1) { CanThrow = true}, - new OpCodeInfo(OpCodes.Conv_Ovf_U2) { CanThrow = true}, - new OpCodeInfo(OpCodes.Conv_Ovf_U4) { CanThrow = true}, - new OpCodeInfo(OpCodes.Conv_Ovf_U8) { CanThrow = true}, - new OpCodeInfo(OpCodes.Conv_Ovf_I) { CanThrow = true}, - new OpCodeInfo(OpCodes.Conv_Ovf_U) { CanThrow = true}, - // conv.ovf.<to type>.un - new OpCodeInfo(OpCodes.Conv_Ovf_I1_Un) { CanThrow = true }, - new OpCodeInfo(OpCodes.Conv_Ovf_I2_Un) { CanThrow = true }, - new OpCodeInfo(OpCodes.Conv_Ovf_I4_Un) { CanThrow = true }, - new OpCodeInfo(OpCodes.Conv_Ovf_I8_Un) { CanThrow = true }, - new OpCodeInfo(OpCodes.Conv_Ovf_U1_Un) { CanThrow = true }, - new OpCodeInfo(OpCodes.Conv_Ovf_U2_Un) { CanThrow = true }, - new OpCodeInfo(OpCodes.Conv_Ovf_U4_Un) { CanThrow = true }, - new OpCodeInfo(OpCodes.Conv_Ovf_U8_Un) { CanThrow = true }, - new OpCodeInfo(OpCodes.Conv_Ovf_I_Un) { CanThrow = true }, - new OpCodeInfo(OpCodes.Conv_Ovf_U_Un) { CanThrow = true }, - - //new OpCodeInfo(OpCodes.Cpblk) { CanThrow = true }, - no idea whether this might cause trouble for the type system, C# shouldn't use it so I'll disable it - new OpCodeInfo(OpCodes.Div) { CanThrow = true }, - new OpCodeInfo(OpCodes.Div_Un) { CanThrow = true }, - new OpCodeInfo(OpCodes.Dup) { CanThrow = true, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Endfilter) { CanThrow = false }, - new OpCodeInfo(OpCodes.Endfinally) { CanThrow = false }, - //new OpCodeInfo(OpCodes.Initblk) { CanThrow = true }, - no idea whether this might cause trouble for the type system, C# shouldn't use it so I'll disable it - //new OpCodeInfo(OpCodes.Jmp) { CanThrow = true } - We don't support non-local control transfers. - new OpCodeInfo(OpCodes.Ldarg) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Ldarg_0) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Ldarg_1) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Ldarg_2) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Ldarg_3) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Ldarg_S) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Ldarga) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldarga_S) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldc_I4) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldc_I4_M1) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldc_I4_0) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldc_I4_1) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldc_I4_2) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldc_I4_3) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldc_I4_4) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldc_I4_5) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldc_I4_6) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldc_I4_7) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldc_I4_8) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldc_I4_S) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldc_I8) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldc_R4) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldc_R8) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldftn) { CanThrow = false }, - // ldind.<type> - new OpCodeInfo(OpCodes.Ldind_I1) { CanThrow = true }, - new OpCodeInfo(OpCodes.Ldind_I2) { CanThrow = true }, - new OpCodeInfo(OpCodes.Ldind_I4) { CanThrow = true }, - new OpCodeInfo(OpCodes.Ldind_I8) { CanThrow = true }, - new OpCodeInfo(OpCodes.Ldind_U1) { CanThrow = true }, - new OpCodeInfo(OpCodes.Ldind_U2) { CanThrow = true }, - new OpCodeInfo(OpCodes.Ldind_U4) { CanThrow = true }, - new OpCodeInfo(OpCodes.Ldind_R4) { CanThrow = true }, - new OpCodeInfo(OpCodes.Ldind_R8) { CanThrow = true }, - new OpCodeInfo(OpCodes.Ldind_I) { CanThrow = true }, - new OpCodeInfo(OpCodes.Ldind_Ref) { CanThrow = true }, - // the ldloc exceptions described in the spec can only occur on methods without .localsinit - but csc always sets that flag - new OpCodeInfo(OpCodes.Ldloc) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Ldloc_0) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Ldloc_1) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Ldloc_2) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Ldloc_3) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Ldloc_S) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Ldloca) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldloca_S) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldnull) { CanThrow = false }, - new OpCodeInfo(OpCodes.Leave) { CanThrow = false }, - new OpCodeInfo(OpCodes.Leave_S) { CanThrow = false }, - new OpCodeInfo(OpCodes.Localloc) { CanThrow = true }, - new OpCodeInfo(OpCodes.Mul) { CanThrow = false }, - new OpCodeInfo(OpCodes.Mul_Ovf) { CanThrow = true }, - new OpCodeInfo(OpCodes.Mul_Ovf_Un) { CanThrow = true }, - new OpCodeInfo(OpCodes.Neg) { CanThrow = false }, - new OpCodeInfo(OpCodes.Nop) { CanThrow = false }, - new OpCodeInfo(OpCodes.Not) { CanThrow = false }, - new OpCodeInfo(OpCodes.Or) { CanThrow = false }, - new OpCodeInfo(OpCodes.Pop) { CanThrow = false }, - new OpCodeInfo(OpCodes.Rem) { CanThrow = true }, - new OpCodeInfo(OpCodes.Rem_Un) { CanThrow = true }, - new OpCodeInfo(OpCodes.Ret) { CanThrow = false }, - new OpCodeInfo(OpCodes.Shl) { CanThrow = false }, - new OpCodeInfo(OpCodes.Shr) { CanThrow = false }, - new OpCodeInfo(OpCodes.Shr_Un) { CanThrow = false }, - new OpCodeInfo(OpCodes.Starg) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Starg_S) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Stind_I1) { CanThrow = true }, - new OpCodeInfo(OpCodes.Stind_I2) { CanThrow = true }, - new OpCodeInfo(OpCodes.Stind_I4) { CanThrow = true }, - new OpCodeInfo(OpCodes.Stind_I8) { CanThrow = true }, - new OpCodeInfo(OpCodes.Stind_R4) { CanThrow = true }, - new OpCodeInfo(OpCodes.Stind_R8) { CanThrow = true }, - new OpCodeInfo(OpCodes.Stind_I) { CanThrow = true }, - new OpCodeInfo(OpCodes.Stind_Ref) { CanThrow = true }, - new OpCodeInfo(OpCodes.Stloc) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Stloc_0) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Stloc_1) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Stloc_2) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Stloc_3) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Stloc_S) { CanThrow = false, IsMoveInstruction = true }, - new OpCodeInfo(OpCodes.Sub) { CanThrow = false }, - new OpCodeInfo(OpCodes.Sub_Ovf) { CanThrow = true }, - new OpCodeInfo(OpCodes.Sub_Ovf_Un) { CanThrow = true }, - new OpCodeInfo(OpCodes.Switch) { CanThrow = false }, - new OpCodeInfo(OpCodes.Xor) { CanThrow = false }, - #endregion - #region Object model instructions - // CanThrow is true by default - most OO instructions can throw, so we don't specify CanThrow all of the time - new OpCodeInfo(OpCodes.Box), - new OpCodeInfo(OpCodes.Callvirt), - new OpCodeInfo(OpCodes.Castclass), - new OpCodeInfo(OpCodes.Cpobj), - new OpCodeInfo(OpCodes.Initobj) { CanThrow = false }, - new OpCodeInfo(OpCodes.Isinst) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldelem_Any), - // ldelem.<type> - new OpCodeInfo(OpCodes.Ldelem_I) , - new OpCodeInfo(OpCodes.Ldelem_I1), - new OpCodeInfo(OpCodes.Ldelem_I2), - new OpCodeInfo(OpCodes.Ldelem_I4), - new OpCodeInfo(OpCodes.Ldelem_I8), - new OpCodeInfo(OpCodes.Ldelem_R4), - new OpCodeInfo(OpCodes.Ldelem_R8), - new OpCodeInfo(OpCodes.Ldelem_Ref), - new OpCodeInfo(OpCodes.Ldelem_U1), - new OpCodeInfo(OpCodes.Ldelem_U2), - new OpCodeInfo(OpCodes.Ldelem_U4), - new OpCodeInfo(OpCodes.Ldelema) , - new OpCodeInfo(OpCodes.Ldfld) , - new OpCodeInfo(OpCodes.Ldflda), - new OpCodeInfo(OpCodes.Ldlen) , - new OpCodeInfo(OpCodes.Ldobj) , - new OpCodeInfo(OpCodes.Ldsfld), - new OpCodeInfo(OpCodes.Ldsflda), - new OpCodeInfo(OpCodes.Ldstr) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldtoken) { CanThrow = false }, - new OpCodeInfo(OpCodes.Ldvirtftn), - new OpCodeInfo(OpCodes.Mkrefany), - new OpCodeInfo(OpCodes.Newarr), - new OpCodeInfo(OpCodes.Newobj), - new OpCodeInfo(OpCodes.Refanytype) { CanThrow = false }, - new OpCodeInfo(OpCodes.Refanyval), - new OpCodeInfo(OpCodes.Rethrow), - new OpCodeInfo(OpCodes.Sizeof) { CanThrow = false }, - new OpCodeInfo(OpCodes.Stelem_Any), - new OpCodeInfo(OpCodes.Stelem_I1), - new OpCodeInfo(OpCodes.Stelem_I2), - new OpCodeInfo(OpCodes.Stelem_I4), - new OpCodeInfo(OpCodes.Stelem_I8), - new OpCodeInfo(OpCodes.Stelem_R4), - new OpCodeInfo(OpCodes.Stelem_R8), - new OpCodeInfo(OpCodes.Stelem_Ref), - new OpCodeInfo(OpCodes.Stfld), - new OpCodeInfo(OpCodes.Stobj), - new OpCodeInfo(OpCodes.Stsfld), - new OpCodeInfo(OpCodes.Throw), - new OpCodeInfo(OpCodes.Unbox), - new OpCodeInfo(OpCodes.Unbox_Any), - #endregion - }; - static readonly Dictionary<Code, OpCodeInfo> knownOpCodeDict = knownOpCodes.ToDictionary(info => info.OpCode.Code); - - public static OpCodeInfo Get(OpCode opCode) - { - return Get(opCode.Code); - } - - public static OpCodeInfo Get(Code code) - { - OpCodeInfo info; - if (knownOpCodeDict.TryGetValue(code, out info)) - return info; - else - throw new NotSupportedException(code.ToString()); - } - - OpCode opcode; - - private OpCodeInfo(OpCode opcode) - { - this.opcode = opcode; - this.CanThrow = true; - } - - public OpCode OpCode { get { return opcode; } } - - /// <summary> - /// 'Move' kind of instructions have one input (may be stack or local variable) and copy that value to all outputs (again stack or local variable). - /// </summary> - public bool IsMoveInstruction { get; private set; } - - /// <summary> - /// Specifies whether this opcode is capable of throwing exceptions. - /// </summary> - public bool CanThrow { get; private set; } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SimplifyByRefCalls.cs b/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SimplifyByRefCalls.cs deleted file mode 100644 index 6b0e03950a..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SimplifyByRefCalls.cs +++ /dev/null @@ -1,174 +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.Collections.Generic; -using System.Diagnostics; - -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.FlowAnalysis -{ - /// <summary> - /// This is a transformation working on SSA form. - /// It removes ldloca instructions and replaces them with SpecialOpCode.PrepareByOutCall or SpecialOpCode.PrepareByRefCall. - /// This then allows the variable that had its address taken to also be transformed into SSA. - /// </summary> - sealed class SimplifyByRefCalls - { - public static bool MakeByRefCallsSimple(SsaForm ssaForm) - { - SimplifyByRefCalls instance = new SimplifyByRefCalls(ssaForm); - foreach (SsaBlock block in ssaForm.Blocks) { - for (int i = 0; i < block.Instructions.Count; i++) { - SsaInstruction inst = block.Instructions[i]; - if (inst.Instruction != null) { - switch (inst.Instruction.OpCode.Code) { - case Code.Call: - case Code.Callvirt: - instance.MakeByRefCallSimple(block, ref i, (IMethodSignature)inst.Instruction.Operand); - break; - case Code.Initobj: - instance.MakeInitObjCallSimple(block, ref i); - break; - case Code.Ldfld: - instance.MakeLoadFieldCallSimple(block, ref i); - break; - } - } - } - } - instance.RemoveRedundantInstructions(); - if (instance.couldSimplifySomething) - ssaForm.ComputeVariableUsage(); - return instance.couldSimplifySomething; - } - - readonly SsaForm ssaForm; - - bool couldSimplifySomething; - - // the list of ldloca instructions we will remove - readonly List<SsaInstruction> redundantLoadAddressInstructions = new List<SsaInstruction>(); - - private SimplifyByRefCalls(SsaForm ssaForm) - { - this.ssaForm = ssaForm; - } - - void MakeByRefCallSimple(SsaBlock block, ref int instructionIndexInBlock, IMethodSignature targetMethod) - { - SsaInstruction inst = block.Instructions[instructionIndexInBlock]; - for (int i = 0; i < inst.Operands.Length; i++) { - SsaVariable operand = inst.Operands[i]; - if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) { - // address is used for this method call only - - Instruction loadAddressInstruction = operand.Definition.Instruction; - - // find target parameter type: - bool isOut; - if (i == 0 && targetMethod.HasThis) { - isOut = false; - } else { - ParameterDefinition parameter = targetMethod.Parameters[i - (targetMethod.HasThis ? 1 : 0)]; - isOut = parameter.IsOut; - } - - SsaVariable addressTakenOf = GetVariableFromLoadAddressInstruction(loadAddressInstruction); - - // insert "Prepare" instruction on front - SpecialOpCode loadOpCode = isOut ? SpecialOpCode.PrepareByOutCall : SpecialOpCode.PrepareByRefCall; - block.Instructions.Insert(instructionIndexInBlock++, new SsaInstruction( - block, null, operand, new SsaVariable[] { addressTakenOf }, specialOpCode: loadOpCode)); - - // insert "WriteAfterByRefOrOutCall" instruction after call - block.Instructions.Insert(instructionIndexInBlock + 1, new SsaInstruction( - block, null, addressTakenOf, new SsaVariable[] { operand }, specialOpCode: SpecialOpCode.WriteAfterByRefOrOutCall)); - - couldSimplifySomething = true; - - // remove the loadAddressInstruction later - // (later because it might be defined in the current block and we don't want instructionIndex to become invalid) - redundantLoadAddressInstructions.Add(operand.Definition); - } - } - } - - SsaVariable GetVariableFromLoadAddressInstruction(Instruction loadAddressInstruction) - { - if (loadAddressInstruction.OpCode == OpCodes.Ldloca) { - return ssaForm.GetOriginalVariable((VariableReference)loadAddressInstruction.Operand); - } else { - Debug.Assert(loadAddressInstruction.OpCode == OpCodes.Ldarga); - return ssaForm.GetOriginalVariable((ParameterReference)loadAddressInstruction.Operand); - } - } - - static bool IsLoadAddress(SsaInstruction inst) - { - return inst.Instruction != null && (inst.Instruction.OpCode == OpCodes.Ldloca || inst.Instruction.OpCode == OpCodes.Ldarga); - } - - void MakeInitObjCallSimple(SsaBlock block, ref int instructionIndexInBlock) - { - SsaInstruction inst = block.Instructions[instructionIndexInBlock]; - Debug.Assert(inst.Operands.Length == 1); - SsaVariable operand = inst.Operands[0]; - if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) { - // replace instruction with special "InitObj" instruction - block.Instructions[instructionIndexInBlock] = new SsaInstruction( - inst.ParentBlock, null, GetVariableFromLoadAddressInstruction(operand.Definition.Instruction), null, - specialOpCode: SpecialOpCode.InitObj, - typeOperand: (TypeReference)inst.Instruction.Operand); - - couldSimplifySomething = true; - - // remove the loadAddressInstruction later - redundantLoadAddressInstructions.Add(operand.Definition); - } - } - - void MakeLoadFieldCallSimple(SsaBlock block, ref int instructionIndexInBlock) - { - SsaInstruction inst = block.Instructions[instructionIndexInBlock]; - Debug.Assert(inst.Operands.Length == 1); - SsaVariable operand = inst.Operands[0]; - if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) { - // insert special "PrepareForFieldAccess" instruction in front - block.Instructions.Insert(instructionIndexInBlock++, new SsaInstruction( - inst.ParentBlock, null, operand, - new SsaVariable[] { GetVariableFromLoadAddressInstruction(operand.Definition.Instruction) }, - specialOpCode: SpecialOpCode.PrepareForFieldAccess)); - - couldSimplifySomething = true; - - // remove the loadAddressInstruction later - redundantLoadAddressInstructions.Add(operand.Definition); - } - } - - void RemoveRedundantInstructions() - { - foreach (SsaInstruction inst in redundantLoadAddressInstructions) { - inst.ParentBlock.Instructions.Remove(inst); - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SsaBlock.cs b/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SsaBlock.cs deleted file mode 100644 index 44022e30e8..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SsaBlock.cs +++ /dev/null @@ -1,60 +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.Collections.Generic; -using System.IO; - -namespace ICSharpCode.Decompiler.FlowAnalysis -{ - /// <summary> - /// A block in a control flow graph; with instructions represented by "SsaInstructions" (instructions use variables, no evaluation stack). - /// Usually these variables are in SSA form to make analysis easier. - /// </summary> - public sealed class SsaBlock - { - public readonly List<SsaBlock> Successors = new List<SsaBlock>(); - public readonly List<SsaBlock> Predecessors = new List<SsaBlock>(); - public readonly ControlFlowNodeType NodeType; - public readonly List<SsaInstruction> Instructions = new List<SsaInstruction>(); - - /// <summary> - /// The block index in the control flow graph. - /// This correspons to the node index in ControlFlowGraph.Nodes, so it can be used to retrieve the original CFG node and look - /// up additional information (e.g. dominance). - /// </summary> - public readonly int BlockIndex; - - internal SsaBlock(ControlFlowNode node) - { - this.NodeType = node.NodeType; - this.BlockIndex = node.BlockIndex; - } - - public override string ToString() - { - StringWriter writer = new StringWriter(); - writer.Write("Block #{0} ({1})", BlockIndex, NodeType); - foreach (SsaInstruction inst in Instructions) { - writer.WriteLine(); - inst.WriteTo(writer); - } - return writer.ToString(); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SsaForm.cs b/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SsaForm.cs deleted file mode 100644 index 1a85e76a74..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SsaForm.cs +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (c) 2010 Daniel Grunwald -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Linq; - -using ICSharpCode.NRefactory.Utils; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.FlowAnalysis -{ - /// <summary> - /// Represents a graph of SsaBlocks. - /// </summary> - public sealed class SsaForm - { - readonly SsaVariable[] parameters; - readonly SsaVariable[] locals; - public readonly ReadOnlyCollection<SsaVariable> OriginalVariables; - public readonly ReadOnlyCollection<SsaBlock> Blocks; - readonly bool methodHasThis; - - public SsaBlock EntryPoint { - get { return this.Blocks[0]; } - } - - public SsaBlock RegularExit { - get { return this.Blocks[1]; } - } - - public SsaBlock ExceptionalExit { - get { return this.Blocks[2]; } - } - - internal SsaForm(SsaBlock[] blocks, SsaVariable[] parameters, SsaVariable[] locals, SsaVariable[] stackLocations, bool methodHasThis) - { - this.parameters = parameters; - this.locals = locals; - this.Blocks = new ReadOnlyCollection<SsaBlock>(blocks); - this.OriginalVariables = new ReadOnlyCollection<SsaVariable>(parameters.Concat(locals).Concat(stackLocations).ToList()); - this.methodHasThis = methodHasThis; - - Debug.Assert(EntryPoint.NodeType == ControlFlowNodeType.EntryPoint); - Debug.Assert(RegularExit.NodeType == ControlFlowNodeType.RegularExit); - Debug.Assert(ExceptionalExit.NodeType == ControlFlowNodeType.ExceptionalExit); - for (int i = 0; i < this.OriginalVariables.Count; i++) { - this.OriginalVariables[i].OriginalVariableIndex = i; - } - } - - public GraphVizGraph ExportBlockGraph(Func<SsaBlock, string> labelProvider = null) - { - if (labelProvider == null) - labelProvider = b => b.ToString(); - GraphVizGraph graph = new GraphVizGraph(); - foreach (SsaBlock block in this.Blocks) { - graph.AddNode(new GraphVizNode(block.BlockIndex) { label = labelProvider(block), shape = "box" }); - } - foreach (SsaBlock block in this.Blocks) { - foreach (SsaBlock s in block.Successors) { - graph.AddEdge(new GraphVizEdge(block.BlockIndex, s.BlockIndex)); - } - } - return graph; - } - - public GraphVizGraph ExportVariableGraph(Func<SsaVariable, string> labelProvider = null) - { - if (labelProvider == null) - labelProvider = v => v.ToString(); - GraphVizGraph graph = new GraphVizGraph(); - foreach (SsaVariable v in this.AllVariables) { - graph.AddNode(new GraphVizNode(v.Name) { label = labelProvider(v) }); - } - int instructionIndex = 0; - foreach (SsaBlock block in this.Blocks) { - foreach (SsaInstruction inst in block.Instructions) { - if (inst.Operands.Length == 0 && inst.Target == null) - continue; - string id = "instruction" + (++instructionIndex); - graph.AddNode(new GraphVizNode(id) { label = inst.ToString(), shape = "box" }); - foreach (SsaVariable op in inst.Operands) - graph.AddEdge(new GraphVizEdge(op.Name, id)); - if (inst.Target != null) - graph.AddEdge(new GraphVizEdge(id, inst.Target.Name)); - } - } - return graph; - } - - public SsaVariable GetOriginalVariable(ParameterReference parameter) - { - if (methodHasThis) - return parameters[parameter.Index + 1]; - else - return parameters[parameter.Index]; - } - - public SsaVariable GetOriginalVariable(VariableReference variable) - { - return locals[variable.Index]; - } - - #region ComputeVariableUsage - public void ComputeVariableUsage() - { - // clear data from previous runs - foreach (SsaBlock block in this.Blocks) { - foreach (SsaInstruction inst in block.Instructions) { - foreach (SsaVariable v in inst.Operands) { - if (v.Usage != null) - v.Usage.Clear(); - } - if (inst.Target != null && inst.Target.Usage != null) - inst.Target.Usage.Clear(); - } - } - foreach (SsaBlock block in this.Blocks) { - foreach (SsaInstruction inst in block.Instructions) { - foreach (SsaVariable v in inst.Operands) { - if (v.Usage == null) - v.Usage = new List<SsaInstruction>(); - v.Usage.Add(inst); - } - if (inst.Target != null && inst.Target.Usage == null) - inst.Target.Usage = new List<SsaInstruction>(); - } - } - } - #endregion - - public IEnumerable<SsaVariable> AllVariables { - get { - return ( - from block in this.Blocks - from instruction in block.Instructions - where instruction.Target != null - select instruction.Target - ).Distinct(); - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SsaFormBuilder.cs b/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SsaFormBuilder.cs deleted file mode 100644 index 6f23001843..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SsaFormBuilder.cs +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright (c) 2010 Daniel Grunwald -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Diagnostics; - -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.FlowAnalysis -{ - /// <summary> - /// Constructs "SsaForm" graph for a CFG. - /// This class transforms the method from stack-based IL to a register-based IL language. - /// Then it calls into TransformToSsa to convert the resulting graph to static single assignment form. - /// </summary> - public sealed class SsaFormBuilder - { - public static SsaForm Build(MethodDefinition method) - { - if (method == null) - throw new ArgumentNullException("method"); - var cfg = ControlFlowGraphBuilder.Build(method.Body); - cfg.ComputeDominance(); - cfg.ComputeDominanceFrontier(); - var ssa = BuildRegisterIL(method, cfg); - TransformToSsa.Transform(cfg, ssa); - return ssa; - } - - public static SsaForm BuildRegisterIL(MethodDefinition method, ControlFlowGraph cfg) - { - if (method == null) - throw new ArgumentNullException("method"); - if (cfg == null) - throw new ArgumentNullException("cfg"); - return new SsaFormBuilder(method, cfg).Build(); - } - - readonly MethodDefinition method; - readonly ControlFlowGraph cfg; - - readonly SsaBlock[] blocks; // array index = block index - readonly int[] stackSizeAtBlockStart; // array index = block index - - readonly SsaVariable[] parameters; // array index = parameter number - readonly SsaVariable[] locals; // array index = local number - readonly SsaVariable[] stackLocations; // array index = position on the IL evaluation stack - SsaForm ssaForm; - - private SsaFormBuilder(MethodDefinition method, ControlFlowGraph cfg) - { - this.method = method; - this.cfg = cfg; - - this.blocks = new SsaBlock[cfg.Nodes.Count]; - this.stackSizeAtBlockStart = new int[cfg.Nodes.Count]; - for (int i = 0; i < stackSizeAtBlockStart.Length; i++) { - stackSizeAtBlockStart[i] = -1; - } - stackSizeAtBlockStart[cfg.EntryPoint.BlockIndex] = 0; - - this.parameters = new SsaVariable[method.Parameters.Count + (method.HasThis ? 1 : 0)]; - if (method.HasThis) - parameters[0] = new SsaVariable(method.Body.ThisParameter); - for (int i = 0; i < method.Parameters.Count; i++) - parameters[i + (method.HasThis ? 1 : 0)] = new SsaVariable(method.Parameters[i]); - - this.locals = new SsaVariable[method.Body.Variables.Count]; - for (int i = 0; i < locals.Length; i++) - locals[i] = new SsaVariable(method, method.Body.Variables[i]); - - this.stackLocations = new SsaVariable[method.Body.MaxStackSize]; - for (int i = 0; i < stackLocations.Length; i++) { - stackLocations[i] = new SsaVariable(i); - } - } - - internal SsaForm Build() - { - CreateGraphStructure(); - this.ssaForm = new SsaForm(blocks, parameters, locals, stackLocations, method.HasThis); - CreateInstructions(cfg.EntryPoint.BlockIndex); - CreateSpecialInstructions(); - return ssaForm; - } - - void CreateGraphStructure() - { - for (int i = 0; i < blocks.Length; i++) { - blocks[i] = new SsaBlock(cfg.Nodes[i]); - } - for (int i = 0; i < blocks.Length; i++) { - foreach (ControlFlowNode node in cfg.Nodes[i].Successors) { - blocks[i].Successors.Add(blocks[node.BlockIndex]); - blocks[node.BlockIndex].Predecessors.Add(blocks[i]); - } - } - } - - void CreateInstructions(int blockIndex) - { - ControlFlowNode cfgNode = cfg.Nodes[blockIndex]; - SsaBlock block = blocks[blockIndex]; - - int stackSize = stackSizeAtBlockStart[blockIndex]; - Debug.Assert(stackSize >= 0); - - List<Instruction> prefixes = new List<Instruction>(); - foreach (Instruction inst in cfgNode.Instructions) { - if (inst.OpCode.OpCodeType == OpCodeType.Prefix) { - prefixes.Add(inst); - continue; - } - - int popCount = inst.GetPopDelta(method) ?? stackSize; - stackSize -= popCount; - if (stackSize < 0) - throw new InvalidProgramException("IL stack underflow"); - - int pushCount = inst.GetPushDelta(); - if (stackSize + pushCount > stackLocations.Length) - throw new InvalidProgramException("IL stack overflow"); - - SsaVariable target; - SsaVariable[] operands; - DetermineOperands(stackSize, inst, popCount, pushCount, out target, out operands); - - Instruction[] prefixArray = prefixes.Count > 0 ? prefixes.ToArray() : null; - prefixes.Clear(); - - // ignore NOP instructions - if (!(inst.OpCode == OpCodes.Nop || inst.OpCode == OpCodes.Pop)) { - block.Instructions.Add(new SsaInstruction(block, inst, target, operands, prefixArray)); - } - stackSize += pushCount; - } - - foreach (ControlFlowEdge edge in cfgNode.Outgoing) { - int newStackSize; - switch (edge.Type) { - case JumpType.Normal: - newStackSize = stackSize; - break; - case JumpType.EndFinally: - if (stackSize != 0) - throw new NotSupportedException("stacksize must be 0 in endfinally edge"); - newStackSize = 0; - break; - case JumpType.JumpToExceptionHandler: - switch (edge.Target.NodeType) { - case ControlFlowNodeType.FinallyOrFaultHandler: - newStackSize = 0; - break; - case ControlFlowNodeType.ExceptionalExit: - case ControlFlowNodeType.CatchHandler: - newStackSize = 1; - break; - default: - throw new NotSupportedException("unsupported target node type: " + edge.Target.NodeType); - } - break; - default: - throw new NotSupportedException("unsupported jump type: " + edge.Type); - } - - int nextStackSize = stackSizeAtBlockStart[edge.Target.BlockIndex]; - if (nextStackSize == -1) { - stackSizeAtBlockStart[edge.Target.BlockIndex] = newStackSize; - CreateInstructions(edge.Target.BlockIndex); - } else if (nextStackSize != newStackSize) { - throw new InvalidProgramException("Stack size doesn't match"); - } - } - } - - void DetermineOperands(int stackSize, Instruction inst, int popCount, int pushCount, out SsaVariable target, out SsaVariable[] operands) - { - switch (inst.OpCode.Code) { - case Code.Ldarg: - operands = new SsaVariable[] { ssaForm.GetOriginalVariable((ParameterReference)inst.Operand) }; - target = stackLocations[stackSize]; - break; - case Code.Starg: - operands = new SsaVariable[] { stackLocations[stackSize] }; - target = ssaForm.GetOriginalVariable((ParameterReference)inst.Operand); - break; - case Code.Ldloc: - operands = new SsaVariable[] { ssaForm.GetOriginalVariable((VariableReference)inst.Operand) }; - target = stackLocations[stackSize]; - break; - case Code.Stloc: - operands = new SsaVariable[] { stackLocations[stackSize] }; - target = ssaForm.GetOriginalVariable((VariableReference)inst.Operand); - break; - case Code.Dup: - operands = new SsaVariable[] { stackLocations[stackSize] }; - target = stackLocations[stackSize + 1]; - break; - default: - operands = new SsaVariable[popCount]; - for (int i = 0; i < popCount; i++) { - operands[i] = stackLocations[stackSize + i]; - } - - switch (pushCount) { - case 0: - target = null; - break; - case 1: - target = stackLocations[stackSize]; - break; - default: - throw new NotSupportedException("unsupported pushCount=" + pushCount); - } - break; - } - } - - void CreateSpecialInstructions() - { - // Everything needs an initial write for the SSA transformation to work correctly. - foreach (SsaVariable v in parameters) { - ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Parameter)); - } - foreach (SsaVariable v in locals) { - ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Uninitialized)); - } - foreach (SsaVariable v in stackLocations) { - ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Uninitialized)); - } - foreach (SsaBlock b in blocks) { - if (b.NodeType == ControlFlowNodeType.CatchHandler) { - b.Instructions.Add(new SsaInstruction(b, null, stackLocations[0], null, - specialOpCode: SpecialOpCode.Exception, - typeOperand: cfg.Nodes[b.BlockIndex].ExceptionHandler.CatchType)); - } - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SsaInstruction.cs b/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SsaInstruction.cs deleted file mode 100644 index 077a329ce1..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SsaInstruction.cs +++ /dev/null @@ -1,191 +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.Diagnostics; -using System.IO; - -using ICSharpCode.Decompiler.Disassembler; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.FlowAnalysis -{ - public enum SpecialOpCode - { - /// <summary> - /// No special op code: SsaInstruction has a normal IL instruction - /// </summary> - None, - /// <summary> - /// Φ function: chooses the appropriate variable based on which CFG edge was used to enter this block - /// </summary> - Phi, - /// <summary> - /// Variable is read from before passing it by ref. - /// This instruction constructs a managed reference to the variable. - /// </summary> - PrepareByRefCall, - /// <summary> - /// This instruction constructs a managed reference to the variable. - /// The variable is not really read from. - /// </summary> - PrepareByOutCall, - /// <summary> - /// This instruction constructs a managed reference to the variable. - /// The reference is used for a field access on a value type. - /// </summary> - PrepareForFieldAccess, - /// <summary> - /// Variable is written to after passing it by ref or out. - /// </summary> - WriteAfterByRefOrOutCall, - /// <summary> - /// Variable is not initialized. - /// </summary> - Uninitialized, - /// <summary> - /// Value is passed in as parameter - /// </summary> - Parameter, - /// <summary> - /// Value is a caught exception. - /// TypeOperand is set to the exception type. - /// </summary> - Exception, - /// <summary> - /// Initialize a value type. Unlike the real initobj instruction, this one does not take an address - /// but assigns to the target variable. - /// TypeOperand is set to the type being created. - /// </summary> - InitObj - } - - public sealed class SsaInstruction - { - public readonly SsaBlock ParentBlock; - public readonly SpecialOpCode SpecialOpCode; - - /// <summary> - /// The original IL instruction. - /// May be null for "invented" instructions (SpecialOpCode != None). - /// </summary> - public readonly Instruction Instruction; - - /// <summary> - /// Prefixes in front of the IL instruction. - /// </summary> - public readonly Instruction[] Prefixes; - - /// <summary> - /// Gets the type operand. This is used only in combination with some special opcodes. - /// </summary> - public readonly TypeReference TypeOperand; - - public SsaVariable Target; - public SsaVariable[] Operands; - - static readonly SsaVariable[] emptyVariableArray = {}; - static readonly Instruction[] emptyInstructionArray = {}; - - public SsaInstruction(SsaBlock parentBlock, Instruction instruction, SsaVariable target, SsaVariable[] operands, - Instruction[] prefixes = null, SpecialOpCode specialOpCode = SpecialOpCode.None, - TypeReference typeOperand = null) - { - this.ParentBlock = parentBlock; - this.Instruction = instruction; - this.Prefixes = prefixes ?? emptyInstructionArray; - this.Target = target; - this.Operands = operands ?? emptyVariableArray; - this.SpecialOpCode = specialOpCode; - this.TypeOperand = typeOperand; - Debug.Assert((typeOperand != null) == (specialOpCode == SpecialOpCode.Exception || specialOpCode == SpecialOpCode.InitObj)); - } - - /// <summary> - /// Gets whether this instruction is a simple assignment from one variable to another. - /// </summary> - public bool IsMoveInstruction { - get { - return Target != null && Operands.Length == 1 && Instruction != null && OpCodeInfo.Get(Instruction.OpCode).IsMoveInstruction; - } - } - - public void ReplaceVariableInOperands(SsaVariable oldVar, SsaVariable newVar) - { - for (int i = 0; i < this.Operands.Length; i++) { - if (this.Operands[i] == oldVar) - this.Operands[i] = newVar; - } - } - - public override string ToString() - { - StringWriter w = new StringWriter(); - WriteTo(w); - return w.ToString(); - } - - public void WriteTo(TextWriter writer) - { - foreach (Instruction prefix in this.Prefixes) { - Disassembler.DisassemblerHelpers.WriteTo(prefix, new PlainTextOutput(writer)); - writer.WriteLine(); - } - if (Instruction != null && Instruction.Offset >= 0) { - writer.Write(CecilExtensions.OffsetToString(Instruction.Offset)); - writer.Write(": "); - } - if (Target != null) { - writer.Write(Target.ToString()); - writer.Write(" = "); - } - if (IsMoveInstruction) { - writer.Write(Operands[0].ToString()); - if (Instruction != null) { - writer.Write(" (" + Instruction.OpCode.Name + ")"); - } - } else { - if (Instruction == null) { - writer.Write(SpecialOpCode.ToString()); - } else { - writer.Write(Instruction.OpCode.Name); - if(null != Instruction.Operand) { - writer.Write(' '); - Disassembler.DisassemblerHelpers.WriteOperand(new PlainTextOutput(writer), Instruction.Operand); - writer.Write(' '); - } - } - if (TypeOperand != null) { - writer.Write(' '); - writer.Write(TypeOperand.ToString()); - writer.Write(' '); - } - if (Operands.Length > 0) { - writer.Write('('); - for (int i = 0; i < Operands.Length; i++) { - if (i > 0) - writer.Write(", "); - writer.Write(Operands[i].ToString()); - } - writer.Write(')'); - } - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SsaOptimization.cs b/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SsaOptimization.cs deleted file mode 100644 index 5466bf4620..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SsaOptimization.cs +++ /dev/null @@ -1,138 +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.Collections.Generic; -using System.Diagnostics; - -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.FlowAnalysis -{ - /// <summary> - /// Contains some very simple optimizations that work on the SSA form. - /// </summary> - static class SsaOptimization - { - public static void Optimize(SsaForm ssaForm) - { - DirectlyStoreToVariables(ssaForm); - SimpleCopyPropagation(ssaForm); - RemoveDeadAssignments(ssaForm); - } - - /// <summary> - /// When any instructions stores its result in a stack location that's used only once in a 'stloc' or 'starg' instruction, - /// we optimize this to directly store in the target location. - /// As optimization this is redundant (does the same as copy propagation), but it'll make us keep the variables named - /// after locals instead of keeping the temps as using only the simple copy propagation would do. - /// </summary> - public static void DirectlyStoreToVariables(SsaForm ssaForm) - { - foreach (SsaBlock block in ssaForm.Blocks) { - block.Instructions.RemoveAll( - inst => { - if (inst.Instruction != null && (inst.Instruction.OpCode == OpCodes.Stloc || inst.Instruction.OpCode == OpCodes.Starg)) { - SsaVariable target = inst.Target; - SsaVariable temp = inst.Operands[0]; - if (target.IsSingleAssignment && temp.IsSingleAssignment && temp.Usage.Count == 1 && temp.IsStackLocation) { - temp.Definition.Target = target; - return true; - } - } - return false; - }); - } - ssaForm.ComputeVariableUsage(); // update usage after we modified stuff - } - - public static void SimpleCopyPropagation(SsaForm ssaForm, bool onlyForStackLocations = true) - { - foreach (SsaBlock block in ssaForm.Blocks) { - foreach (SsaInstruction inst in block.Instructions) { - if (inst.IsMoveInstruction && inst.Target.IsSingleAssignment && inst.Operands[0].IsSingleAssignment) { - if (inst.Target.IsStackLocation || !onlyForStackLocations) { - // replace all uses of 'target' with 'operands[0]'. - foreach (SsaInstruction useInstruction in inst.Target.Usage) { - useInstruction.ReplaceVariableInOperands(inst.Target, inst.Operands[0]); - } - } - } - } - } - ssaForm.ComputeVariableUsage(); // update usage after we modified stuff - } - - public static void RemoveDeadAssignments(SsaForm ssaForm) - { - HashSet<SsaVariable> liveVariables = new HashSet<SsaVariable>(); - // find variables that are used directly - foreach (SsaBlock block in ssaForm.Blocks) { - foreach (SsaInstruction inst in block.Instructions) { - if (!CanRemoveAsDeadCode(inst)) { - if (inst.Target != null) - liveVariables.Add(inst.Target); - foreach (SsaVariable op in inst.Operands) { - liveVariables.Add(op); - } - } - } - } - Queue<SsaVariable> queue = new Queue<SsaVariable>(liveVariables); - // find variables that are used indirectly - while (queue.Count > 0) { - SsaVariable v = queue.Dequeue(); - if (v.IsSingleAssignment) { - foreach (SsaVariable op in v.Definition.Operands) { - if (liveVariables.Add(op)) - queue.Enqueue(op); - } - } - } - // remove assignments to all unused variables - foreach (SsaBlock block in ssaForm.Blocks) { - block.Instructions.RemoveAll( - inst => { - if (inst.Target != null && !liveVariables.Contains(inst.Target)) { - Debug.Assert(inst.Target.IsSingleAssignment); - return true; - } - return false; - }); - } - ssaForm.ComputeVariableUsage(); // update usage after we modified stuff - } - - static bool CanRemoveAsDeadCode(SsaInstruction inst) - { - if (inst.Target != null && !inst.Target.IsSingleAssignment) - return false; - switch (inst.SpecialOpCode) { - case SpecialOpCode.Phi: - case SpecialOpCode.Exception: - case SpecialOpCode.Parameter: - case SpecialOpCode.Uninitialized: - return true; - case SpecialOpCode.None: - return inst.IsMoveInstruction; - default: - return false; - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SsaVariable.cs b/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SsaVariable.cs deleted file mode 100644 index 650da76e77..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/SsaVariable.cs +++ /dev/null @@ -1,93 +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.Collections.Generic; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.FlowAnalysis -{ - /// <summary> - /// Represents a variable used with the SsaInstruction register-based instructions. - /// Despite what the name suggests, the variable is not necessarily in single-assignment form - take a look at "bool IsSingleAssignment". - /// </summary> - public sealed class SsaVariable - { - public int OriginalVariableIndex; - public readonly string Name; - public readonly bool IsStackLocation; - - public readonly ParameterDefinition Parameter; - public readonly VariableDefinition Variable; - - public SsaVariable(ParameterDefinition p) - { - this.Name = string.IsNullOrEmpty(p.Name) ? "param" + p.Index : p.Name; - this.Parameter = p; - } - - public SsaVariable(MethodDefinition m, VariableDefinition v) - { - if (!m.DebugInformation.TryGetName (v, out Name)) { - this.Name = "V_" + v.Index; - } - this.Variable = v; - } - - public SsaVariable(int stackLocation) - { - this.Name = "stack" + stackLocation; - this.IsStackLocation = true; - } - - public SsaVariable(SsaVariable original, string newName) - { - this.Name = newName; - this.IsStackLocation = original.IsStackLocation; - this.OriginalVariableIndex = original.OriginalVariableIndex; - this.Parameter = original.Parameter; - this.Variable = original.Variable; - } - - public override string ToString() - { - return Name; - } - - /// <summary> - /// Gets whether this variable has only a single assignment. - /// This field is initialized in TransformToSsa step. - /// </summary> - /// <remarks>Not all variables can be transformed to single assignment form: variables that have their address taken - /// cannot be represented in SSA (although SimplifyByRefCalls will get rid of the address-taking instruction in almost all cases)</remarks> - public bool IsSingleAssignment; - - /// <summary> - /// Gets the instruction defining the variable. - /// This field is initialized in TransformToSsa step. It is only set for variables with a single assignment. - /// </summary> - public SsaInstruction Definition; - - /// <summary> - /// Gets the places where a variable is used. - /// If a single instruction reads a variable 2 times (e.g. adding to itself), then it must be included 2 times in this list! - /// </summary> - public List<SsaInstruction> Usage; - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs b/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs deleted file mode 100644 index 9fbe366e02..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs +++ /dev/null @@ -1,254 +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.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Linq; - -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.FlowAnalysis -{ - /// <summary> - /// Convers a method to static single assignment form. - /// </summary> - sealed class TransformToSsa - { - public static void Transform(ControlFlowGraph cfg, SsaForm ssa, bool optimize = true) - { - TransformToSsa transform = new TransformToSsa(cfg, ssa); - transform.ConvertVariablesToSsa(); - SsaOptimization.RemoveDeadAssignments(ssa); // required so that 'MakeByRefCallsSimple' can detect more cases - if (SimplifyByRefCalls.MakeByRefCallsSimple(ssa)) { - transform.ConvertVariablesToSsa(); - } - if (optimize) - SsaOptimization.Optimize(ssa); - } - - readonly ControlFlowGraph cfg; - readonly SsaForm ssaForm; - readonly List<SsaInstruction>[] writeToOriginalVariables; // array index -> SsaVariable OriginalVariableIndex - readonly bool[] addressTaken; // array index -> SsaVariable OriginalVariableIndex; value = whether ldloca instruction was used with variable - - private TransformToSsa(ControlFlowGraph cfg, SsaForm ssaForm) - { - this.cfg = cfg; - this.ssaForm = ssaForm; - this.writeToOriginalVariables = new List<SsaInstruction>[ssaForm.OriginalVariables.Count]; - this.addressTaken = new bool[ssaForm.OriginalVariables.Count]; - } - - #region CollectInformationAboutOriginalVariableUse - void CollectInformationAboutOriginalVariableUse() - { - Debug.Assert(addressTaken.Length == writeToOriginalVariables.Length); - for (int i = 0; i < writeToOriginalVariables.Length; i++) { - Debug.Assert(ssaForm.OriginalVariables[i].OriginalVariableIndex == i); - - addressTaken[i] = false; - // writeToOriginalVariables is only used when placing phi functions - // we don't need to do that anymore for variables that are already in SSA form - if (ssaForm.OriginalVariables[i].IsSingleAssignment) - writeToOriginalVariables[i] = null; - else - writeToOriginalVariables[i] = new List<SsaInstruction>(); - } - foreach (SsaBlock block in ssaForm.Blocks) { - foreach (SsaInstruction inst in block.Instructions) { - if (inst.Target != null ) { - var list = writeToOriginalVariables[inst.Target.OriginalVariableIndex]; - if (list != null) - list.Add(inst); - } - if (inst.Instruction != null) { - if (inst.Instruction.OpCode == OpCodes.Ldloca) { - addressTaken[ssaForm.GetOriginalVariable((VariableDefinition)inst.Instruction.Operand).OriginalVariableIndex] = true; - } else if (inst.Instruction.OpCode == OpCodes.Ldarga) { - addressTaken[ssaForm.GetOriginalVariable((ParameterDefinition)inst.Instruction.Operand).OriginalVariableIndex] = true; - } - } - } - } - } - #endregion - - #region ConvertToSsa - void ConvertVariablesToSsa() - { - CollectInformationAboutOriginalVariableUse(); - bool[] processVariable = new bool[ssaForm.OriginalVariables.Count]; - foreach (SsaVariable variable in ssaForm.OriginalVariables) { - if (!variable.IsSingleAssignment && !addressTaken[variable.OriginalVariableIndex]) { - PlacePhiFunctions(variable); - processVariable[variable.OriginalVariableIndex] = true; - } - } - RenameVariables(processVariable); - foreach (SsaVariable variable in ssaForm.OriginalVariables) { - if (!addressTaken[variable.OriginalVariableIndex]) { - Debug.Assert(variable.IsSingleAssignment && variable.Definition != null); - } - } - ssaForm.ComputeVariableUsage(); - } - #endregion - - #region PlacePhiFunctions - void PlacePhiFunctions(SsaVariable variable) - { - cfg.ResetVisited(); - HashSet<SsaBlock> blocksWithPhi = new HashSet<SsaBlock>(); - Queue<ControlFlowNode> worklist = new Queue<ControlFlowNode>(); - foreach (SsaInstruction writeInstruction in writeToOriginalVariables[variable.OriginalVariableIndex]) { - ControlFlowNode cfgNode = cfg.Nodes[writeInstruction.ParentBlock.BlockIndex]; - if (!cfgNode.Visited) { - cfgNode.Visited = true; - worklist.Enqueue(cfgNode); - } - } - while (worklist.Count > 0) { - ControlFlowNode cfgNode = worklist.Dequeue(); - foreach (ControlFlowNode dfNode in cfgNode.DominanceFrontier) { - // we don't need phi functions in the exit node - if (dfNode.NodeType == ControlFlowNodeType.RegularExit || dfNode.NodeType == ControlFlowNodeType.ExceptionalExit) - continue; - SsaBlock y = ssaForm.Blocks[dfNode.BlockIndex]; - if (blocksWithPhi.Add(y)) { - // add a phi instruction in y - SsaVariable[] operands = Enumerable.Repeat(variable, dfNode.Incoming.Count).ToArray(); - y.Instructions.Insert(0, new SsaInstruction(y, null, variable, operands, specialOpCode: SpecialOpCode.Phi)); - if (!dfNode.Visited) { - dfNode.Visited = true; - worklist.Enqueue(dfNode); - } - } - } - } - } - #endregion - - #region RenameVariable - int tempVariableCounter = 1; - - void RenameVariables(bool[] processVariable) - { - VariableRenamer r = new VariableRenamer(this, processVariable); - r.Visit(ssaForm.EntryPoint); - } - - sealed class VariableRenamer - { - readonly TransformToSsa transform; - readonly ReadOnlyCollection<SsaVariable> inputVariables; - internal readonly Stack<SsaVariable>[] versionStacks; - int[] versionCounters; // specifies for each input variable the next version number - - // processVariable = specifies for each input variable whether we should rename it - public VariableRenamer(TransformToSsa transform, bool[] processVariable) - { - this.transform = transform; - this.inputVariables = transform.ssaForm.OriginalVariables; - Debug.Assert(inputVariables.Count == processVariable.Length); - this.versionCounters = new int[inputVariables.Count]; - this.versionStacks = new Stack<SsaVariable>[inputVariables.Count]; - for (int i = 0; i < versionStacks.Length; i++) { - if (processVariable[i]) { - Debug.Assert(inputVariables[i].IsSingleAssignment == false); - // only create version stacks for the variables that we need to process and that weren't already processed earlier - versionStacks[i] = new Stack<SsaVariable>(); - versionStacks[i].Push(inputVariables[i]); - } - } - } - - SsaVariable MakeNewVersion(int variableIndex) - { - int versionCounter = ++versionCounters[variableIndex]; - SsaVariable x = inputVariables[variableIndex]; - if (versionCounter == 1) { - return x; - } else { - if (x.IsStackLocation) { - return new SsaVariable(x, "temp" + (transform.tempVariableCounter++)); - } else { - return new SsaVariable(x, x.Name + "_" + versionCounter); - } - } - } - - internal void Visit(SsaBlock block) - { - // duplicate top of all stacks - foreach (var stack in versionStacks) { - if (stack != null) - stack.Push(stack.Peek()); - } - - foreach (SsaInstruction s in block.Instructions) { - // replace all uses of variables being processed with their current version. - if (s.SpecialOpCode != SpecialOpCode.Phi) { - for (int i = 0; i < s.Operands.Length; i++) { - var stack = versionStacks[s.Operands[i].OriginalVariableIndex]; - if (stack != null) - s.Operands[i] = stack.Peek(); - } - } - // if we're writing to a variable we should process: - if (s.Target != null) { - int targetIndex = s.Target.OriginalVariableIndex; - if (versionStacks[targetIndex] != null) { - s.Target = MakeNewVersion(targetIndex); - s.Target.IsSingleAssignment = true; - s.Target.Definition = s; - - // we already pushed our entry for this SsaBlock at the beginning (where we duplicated all stacks), - // so now replace the top element - versionStacks[targetIndex].Pop(); - versionStacks[targetIndex].Push(s.Target); - } - } - } - - foreach (SsaBlock succ in block.Successors) { - int j = succ.Predecessors.IndexOf(block); - Debug.Assert(j >= 0); - foreach (SsaInstruction f in succ.Instructions) { - if (f.SpecialOpCode == SpecialOpCode.Phi) { - var stack = versionStacks[f.Target.OriginalVariableIndex]; - if (stack != null) { - f.Operands[j] = stack.Peek(); - } - } - } - } - foreach (ControlFlowNode child in transform.cfg.Nodes[block.BlockIndex].DominatorTreeChildren) - Visit(transform.ssaForm.Blocks[child.BlockIndex]); - // restore stacks: - foreach (var stack in versionStacks) { - if (stack != null) - stack.Pop(); - } - } - } - #endregion - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/main/contrib/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj deleted file mode 100644 index c81cafd171..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ /dev/null @@ -1,161 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build"> - <PropertyGroup> - <ProjectGuid>{984CC812-9470-4A13-AFF9-CC44068D666C}</ProjectGuid> - <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> - <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> - <OutputType>Library</OutputType> - <RootNamespace>ICSharpCode.Decompiler</RootNamespace> - <AssemblyName>ICSharpCode.Decompiler</AssemblyName> - <AppDesignerFolder>Properties</AppDesignerFolder> - <AllowUnsafeBlocks>False</AllowUnsafeBlocks> - <NoStdLib>False</NoStdLib> - <WarningLevel>4</WarningLevel> - <TreatWarningsAsErrors>false</TreatWarningsAsErrors> - <SignAssembly>True</SignAssembly> - <AssemblyOriginatorKeyFile>..\..\external\nrefactory\ICSharpCode.NRefactory.snk</AssemblyOriginatorKeyFile> - <AssemblyOriginatorKeyMode>File</AssemblyOriginatorKeyMode> - <ProductVersion>8.0.30703</ProductVersion> - <SchemaVersion>2.0</SchemaVersion> - </PropertyGroup> - <PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' "> - <PlatformTarget>AnyCPU</PlatformTarget> - <RegisterForComInterop>False</RegisterForComInterop> - <GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies> - <BaseAddress>448462848</BaseAddress> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> - <OutputPath>bin\Debug\</OutputPath> - <DebugSymbols>true</DebugSymbols> - <DebugType>full</DebugType> - <Optimize>False</Optimize> - <CheckForOverflowUnderflow>True</CheckForOverflowUnderflow> - <DefineConstants>DEBUG;TRACE</DefineConstants> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)' == 'Release' "> - <OutputPath>bin\Release\</OutputPath> - <DebugSymbols>true</DebugSymbols> - <DebugType>PdbOnly</DebugType> - <Optimize>True</Optimize> - <CheckForOverflowUnderflow>False</CheckForOverflowUnderflow> - <DefineConstants>TRACE</DefineConstants> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> - <DebugType>full</DebugType> - <OutputPath>..\..\build\bin</OutputPath> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> - <OutputPath>..\..\build\bin</OutputPath> - </PropertyGroup> - <ItemGroup> - <Compile Include="Ast\Annotations.cs" /> - <Compile Include="Ast\AstBuilder.cs" /> - <Compile Include="Ast\AstMethodBodyBuilder.cs" /> - <Compile Include="Ast\CommentStatement.cs" /> - <Compile Include="Ast\DecompilerContext.cs" /> - <Compile Include="Ast\NameVariables.cs" /> - <Compile Include="Ast\NRefactoryExtensions.cs" /> - <Compile Include="Ast\TextTokenWriter.cs" /> - <Compile Include="Ast\Transforms\AddCheckedBlocks.cs" /> - <Compile Include="Ast\Transforms\CombineQueryExpressions.cs" /> - <Compile Include="Ast\Transforms\ContextTrackingVisitor.cs" /> - <Compile Include="Ast\Transforms\ConvertConstructorCallIntoInitializer.cs" /> - <Compile Include="Ast\Transforms\CustomPatterns.cs" /> - <Compile Include="Ast\Transforms\DecimalConstantTransform.cs" /> - <Compile Include="Ast\Transforms\DeclareVariables.cs" /> - <Compile Include="Ast\Transforms\DelegateConstruction.cs" /> - <Compile Include="Ast\Transforms\ExpressionTreeConverter.cs" /> - <Compile Include="Ast\Transforms\FlattenSwitchBlocks.cs" /> - <Compile Include="Ast\Transforms\IntroduceExtensionMethods.cs" /> - <Compile Include="Ast\Transforms\IntroduceQueryExpressions.cs" /> - <Compile Include="Ast\Transforms\IntroduceUnsafeModifier.cs" /> - <Compile Include="Ast\Transforms\IntroduceUsingDeclarations.cs" /> - <Compile Include="Ast\Transforms\ReplaceMethodCallsWithOperators.cs" /> - <Compile Include="Ast\Transforms\PushNegation.cs" /> - <Compile Include="Ast\Transforms\TransformationPipeline.cs" /> - <Compile Include="Ast\Transforms\PatternStatementTransform.cs" /> - <Compile Include="Ast\TypesHierarchyHelpers.cs" /> - <Compile Include="CecilExtensions.cs" /> - <Compile Include="CodeMappings.cs" /> - <Compile Include="DecompilerException.cs" /> - <Compile Include="DecompilerSettings.cs" /> - <Compile Include="Disassembler\DisassemblerHelpers.cs" /> - <Compile Include="Disassembler\ILStructure.cs" /> - <Compile Include="Disassembler\MethodBodyDisassembler.cs" /> - <Compile Include="Disassembler\ReflectionDisassembler.cs" /> - <Compile Include="FlowAnalysis\ControlFlowEdge.cs" /> - <Compile Include="FlowAnalysis\ControlFlowGraph.cs" /> - <Compile Include="FlowAnalysis\ControlFlowGraphBuilder.cs" /> - <Compile Include="FlowAnalysis\ControlFlowNode.cs" /> - <Compile Include="FlowAnalysis\ControlStructureDetector.cs" /> - <Compile Include="FlowAnalysis\OpCodeInfo.cs" /> - <Compile Include="FlowAnalysis\SimplifyByRefCalls.cs" /> - <Compile Include="FlowAnalysis\SsaBlock.cs" /> - <Compile Include="FlowAnalysis\SsaForm.cs" /> - <Compile Include="FlowAnalysis\SsaFormBuilder.cs" /> - <Compile Include="FlowAnalysis\SsaInstruction.cs" /> - <Compile Include="FlowAnalysis\SsaOptimization.cs" /> - <Compile Include="FlowAnalysis\SsaVariable.cs" /> - <Compile Include="FlowAnalysis\TransformToSsa.cs" /> - <Compile Include="ILAst\AsyncDecompiler.cs" /> - <Compile Include="ILAst\LiftedOperators.cs" /> - <Compile Include="ILAst\InitializerPeepholeTransforms.cs" /> - <Compile Include="ILAst\DefaultDictionary.cs" /> - <Compile Include="ILAst\GotoRemoval.cs" /> - <Compile Include="ILAst\ILAstBuilder.cs" /> - <Compile Include="ILAst\ILAstOptimizer.cs" /> - <Compile Include="ILAst\ILAstTypes.cs" /> - <Compile Include="ILAst\ILCodes.cs" /> - <Compile Include="ILAst\ILInlining.cs" /> - <Compile Include="ILAst\LoopsAndConditions.cs" /> - <Compile Include="ILAst\PatternMatching.cs" /> - <Compile Include="ILAst\PeepholeTransform.cs" /> - <Compile Include="ILAst\SimpleControlFlow.cs" /> - <Compile Include="ILAst\StateRange.cs" /> - <Compile Include="ILAst\SymbolicExecution.cs" /> - <Compile Include="ILAst\TypeAnalysis.cs" /> - <Compile Include="ILAst\YieldReturnDecompiler.cs" /> - <Compile Include="ITextOutput.cs" /> - <Compile Include="PlainTextOutput.cs" /> - <Compile Include="ReferenceResolvingException.cs" /> - <Compile Include="TextOutputWriter.cs" /> - <None Include="Properties\AssemblyInfo.template.cs" /> - <None Include="packages.config" /> - </ItemGroup> - <ItemGroup> - <ProjectReference Include="..\..\external\nrefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj"> - <Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project> - <Name>ICSharpCode.NRefactory</Name> - <Private>False</Private> - </ProjectReference> - <ProjectReference Include="..\..\external\nrefactory\ICSharpCode.NRefactory.CSharp\ICSharpCode.NRefactory.CSharp.csproj"> - <Project>{53DCA265-3C3C-42F9-B647-F72BA678122B}</Project> - <Name>ICSharpCode.NRefactory.CSharp</Name> - <Private>False</Private> - </ProjectReference> - </ItemGroup> - <ItemGroup /> - <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> - <ItemGroup> - <Reference Include="System" /> - <Reference Include="System.Core" /> - <Reference Include="System.Xml" /> - <Reference Include="System.Xml.Linq" /> - <Reference Include="Mono.Cecil"> - <HintPath>..\..\packages\Mono.Cecil.0.10.0-beta6\lib\net40\Mono.Cecil.dll</HintPath> - <Private>False</Private> - </Reference> - <Reference Include="Mono.Cecil.Mdb"> - <HintPath>..\..\packages\Mono.Cecil.0.10.0-beta6\lib\net40\Mono.Cecil.Mdb.dll</HintPath> - <Private>False</Private> - </Reference> - <Reference Include="Mono.Cecil.Pdb"> - <HintPath>..\..\packages\Mono.Cecil.0.10.0-beta6\lib\net40\Mono.Cecil.Pdb.dll</HintPath> - <Private>False</Private> - </Reference> - <Reference Include="Mono.Cecil.Rocks"> - <HintPath>..\..\packages\Mono.Cecil.0.10.0-beta6\lib\net40\Mono.Cecil.Rocks.dll</HintPath> - <Private>False</Private> - </Reference> - </ItemGroup> -</Project> diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs deleted file mode 100644 index 85dca01023..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs +++ /dev/null @@ -1,705 +0,0 @@ -// Copyright (c) 2012 AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.ILAst -{ - /// <summary> - /// Decompiler step for C# 5 async/await. - /// </summary> - class AsyncDecompiler - { - public static bool IsCompilerGeneratedStateMachine(TypeDefinition type) - { - if (!(type.DeclaringType != null && type.IsCompilerGenerated())) - return false; - foreach (InterfaceImplementation ii in type.Interfaces) { - var i = ii.InterfaceType; - if (i.Namespace == "System.Runtime.CompilerServices" && i.Name == "IAsyncStateMachine") - return true; - } - return false; - } - - enum AsyncMethodType - { - Void, - Task, - TaskOfT - } - - DecompilerContext context; - - // These fields are set by MatchTaskCreationPattern() - AsyncMethodType methodType; - int initialState; - TypeDefinition stateMachineStruct; - MethodDefinition moveNextMethod; - FieldDefinition builderField; - FieldDefinition stateField; - Dictionary<FieldDefinition, ILVariable> fieldToParameterMap = new Dictionary<FieldDefinition, ILVariable>(); - ILVariable cachedStateVar; - - // These fields are set by AnalyzeMoveNext() - int finalState = -2; - ILTryCatchBlock mainTryCatch; - ILLabel setResultAndExitLabel; - ILLabel exitLabel; - ILExpression resultExpr; - - #region RunStep1() method - public static void RunStep1(DecompilerContext context, ILBlock method) - { - if (!context.Settings.AsyncAwait) - return; // abort if async decompilation is disabled - var yrd = new AsyncDecompiler(); - yrd.context = context; - if (!yrd.MatchTaskCreationPattern(method)) - return; - #if DEBUG - if (Debugger.IsAttached) { - yrd.Run(); - } else { - #endif - try { - yrd.Run(); - } catch (SymbolicAnalysisFailedException) { - return; - } - #if DEBUG - } - #endif - context.CurrentMethodIsAsync = true; - - method.Body.Clear(); - method.EntryGoto = null; - method.Body.AddRange(yrd.newTopLevelBody); - ILAstOptimizer.RemoveRedundantCode(method); - } - - void Run() - { - AnalyzeMoveNext(); - ValidateCatchBlock(mainTryCatch.CatchBlocks[0]); - AnalyzeStateMachine(mainTryCatch.TryBlock); - // AnalyzeStateMachine invokes ConvertBody - MarkGeneratedVariables(); - YieldReturnDecompiler.TranslateFieldsToLocalAccess(newTopLevelBody, fieldToParameterMap); - } - #endregion - - #region MatchTaskCreationPattern - bool MatchTaskCreationPattern(ILBlock method) - { - if (method.Body.Count < 5) - return false; - // Check the second-to-last instruction (the start call) first, as we can get the most information from that - MethodReference startMethod; - ILExpression loadStartTarget, loadStartArgument; - // call(AsyncTaskMethodBuilder::Start, ldloca(builder), ldloca(stateMachine)) - if (!method.Body[method.Body.Count - 2].Match(ILCode.Call, out startMethod, out loadStartTarget, out loadStartArgument)) - return false; - if (startMethod.Name != "Start" || startMethod.DeclaringType == null || startMethod.DeclaringType.Namespace != "System.Runtime.CompilerServices") - return false; - switch (startMethod.DeclaringType.Name) { - case "AsyncTaskMethodBuilder`1": - methodType = AsyncMethodType.TaskOfT; - break; - case "AsyncTaskMethodBuilder": - methodType = AsyncMethodType.Task; - break; - case "AsyncVoidMethodBuilder": - methodType = AsyncMethodType.Void; - break; - default: - return false; - } - ILVariable stateMachineVar, builderVar; - if (!loadStartTarget.Match(ILCode.Ldloca, out builderVar)) - return false; - if (!loadStartArgument.Match(ILCode.Ldloca, out stateMachineVar)) - return false; - - stateMachineStruct = stateMachineVar.Type.ResolveWithinSameModule(); - if (stateMachineStruct == null || !stateMachineStruct.IsValueType) - return false; - moveNextMethod = stateMachineStruct.Methods.FirstOrDefault(f => f.Name == "MoveNext"); - if (moveNextMethod == null) - return false; - - // Check third-to-last instruction (copy of builder): - // stloc(builder, ldfld(StateMachine::<>t__builder, ldloca(stateMachine))) - ILExpression loadBuilderExpr; - if (!method.Body[method.Body.Count - 3].MatchStloc(builderVar, out loadBuilderExpr)) - return false; - FieldReference builderFieldRef; - ILExpression loadStateMachineForBuilderExpr; - if (!loadBuilderExpr.Match(ILCode.Ldfld, out builderFieldRef, out loadStateMachineForBuilderExpr)) - return false; - if (!(loadStateMachineForBuilderExpr.MatchLdloca(stateMachineVar) || loadStateMachineForBuilderExpr.MatchLdloc(stateMachineVar))) - return false; - builderField = builderFieldRef.ResolveWithinSameModule(); - if (builderField == null) - return false; - - // Check the last instruction (ret) - if (methodType == AsyncMethodType.Void) { - if (!method.Body[method.Body.Count - 1].Match(ILCode.Ret)) - return false; - } else { - // ret(call(AsyncTaskMethodBuilder::get_Task, ldflda(StateMachine::<>t__builder, ldloca(stateMachine)))) - ILExpression returnValue; - if (!method.Body[method.Body.Count - 1].Match(ILCode.Ret, out returnValue)) - return false; - MethodReference getTaskMethod; - ILExpression builderExpr; - if (!returnValue.Match(ILCode.Call, out getTaskMethod, out builderExpr)) - return false; - ILExpression loadStateMachineForBuilderExpr2; - FieldReference builderField2; - if (!builderExpr.Match(ILCode.Ldflda, out builderField2, out loadStateMachineForBuilderExpr2)) - return false; - if (builderField2.ResolveWithinSameModule() != builderField || !loadStateMachineForBuilderExpr2.MatchLdloca(stateMachineVar)) - return false; - } - - // Check the last field assignment - this should be the state field - ILExpression initialStateExpr; - if (!MatchStFld(method.Body[method.Body.Count - 4], stateMachineVar, out stateField, out initialStateExpr)) - return false; - if (!initialStateExpr.Match(ILCode.Ldc_I4, out initialState)) - return false; - if (initialState != -1) - return false; - - // Check the second-to-last field assignment - this should be the builder field - FieldDefinition builderField3; - ILExpression builderInitialization; - if (!MatchStFld(method.Body[method.Body.Count - 5], stateMachineVar, out builderField3, out builderInitialization)) - return false; - MethodReference createMethodRef; - if (builderField3 != builderField || !builderInitialization.Match(ILCode.Call, out createMethodRef)) - return false; - if (createMethodRef.Name != "Create") - return false; - - for (int i = 0; i < method.Body.Count - 5; i++) { - FieldDefinition field; - ILExpression fieldInit; - if (!MatchStFld(method.Body[i], stateMachineVar, out field, out fieldInit)) - return false; - ILVariable v; - if (!fieldInit.Match(ILCode.Ldloc, out v)) - return false; - if (!v.IsParameter) - return false; - fieldToParameterMap[field] = v; - } - - return true; - } - - static bool MatchStFld(ILNode stfld, ILVariable stateMachineVar, out FieldDefinition field, out ILExpression expr) - { - field = null; - FieldReference fieldRef; - ILExpression ldloca; - if (!stfld.Match(ILCode.Stfld, out fieldRef, out ldloca, out expr)) - return false; - field = fieldRef.ResolveWithinSameModule(); - return field != null && ldloca.MatchLdloca(stateMachineVar); - } - #endregion - - #region Analyze MoveNext - void AnalyzeMoveNext() - { - ILBlock ilMethod = CreateILAst(moveNextMethod); - - int startIndex; - if (ilMethod.Body.Count == 6) { - startIndex = 0; - } else if (ilMethod.Body.Count == 7) { - // stloc(cachedState, ldfld(valuetype StateMachineStruct::<>1__state, ldloc(this))) - ILExpression cachedStateInit; - if (!ilMethod.Body[0].Match(ILCode.Stloc, out cachedStateVar, out cachedStateInit)) - throw new SymbolicAnalysisFailedException(); - ILExpression instanceExpr; - FieldReference loadedField; - if (!cachedStateInit.Match(ILCode.Ldfld, out loadedField, out instanceExpr) || loadedField.ResolveWithinSameModule() != stateField || !instanceExpr.MatchThis()) - throw new SymbolicAnalysisFailedException(); - startIndex = 1; - } else { - throw new SymbolicAnalysisFailedException(); - } - - mainTryCatch = ilMethod.Body[startIndex + 0] as ILTryCatchBlock; - if (mainTryCatch == null || mainTryCatch.CatchBlocks.Count != 1) - throw new SymbolicAnalysisFailedException(); - if (mainTryCatch.FaultBlock != null || mainTryCatch.FinallyBlock != null) - throw new SymbolicAnalysisFailedException(); - - setResultAndExitLabel = ilMethod.Body[startIndex + 1] as ILLabel; - if (setResultAndExitLabel == null) - throw new SymbolicAnalysisFailedException(); - - if (!MatchStateAssignment(ilMethod.Body[startIndex + 2], out finalState)) - throw new SymbolicAnalysisFailedException(); - - // call(AsyncTaskMethodBuilder`1::SetResult, ldflda(StateMachine::<>t__builder, ldloc(this)), ldloc(<>t__result)) - MethodReference setResultMethod; - ILExpression builderExpr; - if (methodType == AsyncMethodType.TaskOfT) { - if (!ilMethod.Body[startIndex + 3].Match(ILCode.Call, out setResultMethod, out builderExpr, out resultExpr)) - throw new SymbolicAnalysisFailedException(); - } else { - if (!ilMethod.Body[startIndex + 3].Match(ILCode.Call, out setResultMethod, out builderExpr)) - throw new SymbolicAnalysisFailedException(); - } - if (!(setResultMethod.Name == "SetResult" && IsBuilderFieldOnThis(builderExpr))) - throw new SymbolicAnalysisFailedException(); - - exitLabel = ilMethod.Body[startIndex + 4] as ILLabel; - if (exitLabel == null) - throw new SymbolicAnalysisFailedException(); - } - - /// <summary> - /// Creates ILAst for the specified method, optimized up to before the 'YieldReturn' step. - /// </summary> - ILBlock CreateILAst(MethodDefinition method) - { - if (method == null || !method.HasBody) - throw new SymbolicAnalysisFailedException(); - - ILBlock ilMethod = new ILBlock(); - ILAstBuilder astBuilder = new ILAstBuilder(); - ilMethod.Body = astBuilder.Build(method, true, context); - ILAstOptimizer optimizer = new ILAstOptimizer(); - optimizer.Optimize(context, ilMethod, ILAstOptimizationStep.YieldReturn); - return ilMethod; - } - - void ValidateCatchBlock(ILTryCatchBlock.CatchBlock catchBlock) - { - if (catchBlock.ExceptionType == null || catchBlock.ExceptionType.Name != "Exception") - throw new SymbolicAnalysisFailedException(); - if (catchBlock.Body.Count != 3) - throw new SymbolicAnalysisFailedException(); - int stateID; - if (!(MatchStateAssignment(catchBlock.Body[0], out stateID) && stateID == finalState)) - throw new SymbolicAnalysisFailedException(); - MethodReference setExceptionMethod; - ILExpression builderExpr, exceptionExpr; - if (!catchBlock.Body[1].Match(ILCode.Call, out setExceptionMethod, out builderExpr, out exceptionExpr)) - throw new SymbolicAnalysisFailedException(); - if (!(setExceptionMethod.Name == "SetException" && IsBuilderFieldOnThis(builderExpr) && exceptionExpr.MatchLdloc(catchBlock.ExceptionVariable))) - throw new SymbolicAnalysisFailedException(); - - ILLabel label; - if (!(catchBlock.Body[2].Match(ILCode.Leave, out label) && label == exitLabel)) - throw new SymbolicAnalysisFailedException(); - } - - bool IsBuilderFieldOnThis(ILExpression builderExpr) - { - // ldflda(StateMachine::<>t__builder, ldloc(this)) - FieldReference fieldRef; - ILExpression target; - return builderExpr.Match(ILCode.Ldflda, out fieldRef, out target) - && fieldRef.ResolveWithinSameModule() == builderField - && target.MatchThis(); - } - - bool MatchStateAssignment(ILNode stfld, out int stateID) - { - // stfld(StateMachine::<>1__state, ldloc(this), ldc.i4(stateId)) - stateID = 0; - FieldReference fieldRef; - ILExpression target, val; - if (stfld.Match(ILCode.Stfld, out fieldRef, out target, out val)) { - return fieldRef.ResolveWithinSameModule() == stateField - && target.MatchThis() - && val.Match(ILCode.Ldc_I4, out stateID); - } - return false; - } - - bool MatchRoslynStateAssignment(List<ILNode> block, int index, out int stateID) - { - // v = ldc.i4(stateId) - // stloc(cachedState, v) - // stfld(StateMachine::<>1__state, ldloc(this), v) - stateID = 0; - if (index < 0) - return false; - ILVariable v; - ILExpression val; - if (!block[index].Match(ILCode.Stloc, out v, out val) || !val.Match(ILCode.Ldc_I4, out stateID)) - return false; - ILExpression loadV; - if (!block[index + 1].MatchStloc(cachedStateVar, out loadV) || !loadV.MatchLdloc(v)) - return false; - ILExpression target; - FieldReference fieldRef; - if (block[index + 2].Match(ILCode.Stfld, out fieldRef, out target, out loadV)) { - return fieldRef.ResolveWithinSameModule() == stateField - && target.MatchThis() - && loadV.MatchLdloc(v); - } - return false; - } - #endregion - - #region AnalyzeStateMachine - ILVariable doFinallyBodies; - List<ILNode> newTopLevelBody; - - void AnalyzeStateMachine(ILBlock block) - { - var body = block.Body; - if (body.Count == 0) - throw new SymbolicAnalysisFailedException(); - if (DetectDoFinallyBodies(body)) { - body.RemoveAt(0); - if (body.Count == 0) - throw new SymbolicAnalysisFailedException(); - } - StateRangeAnalysis rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.AsyncMoveNext, stateField, cachedStateVar); - int bodyLength = block.Body.Count; - int pos = rangeAnalysis.AssignStateRanges(body, bodyLength); - rangeAnalysis.EnsureLabelAtPos(body, ref pos, ref bodyLength); - - var labelStateRangeMapping = rangeAnalysis.CreateLabelRangeMapping(body, pos, bodyLength); - newTopLevelBody = ConvertBody(body, pos, bodyLength, labelStateRangeMapping); - newTopLevelBody.Insert(0, MakeGoTo(labelStateRangeMapping, initialState)); - newTopLevelBody.Add(setResultAndExitLabel); - if (methodType == AsyncMethodType.TaskOfT) { - newTopLevelBody.Add(new ILExpression(ILCode.Ret, null, resultExpr)); - } else { - newTopLevelBody.Add(new ILExpression(ILCode.Ret, null)); - } - } - - bool DetectDoFinallyBodies(List<ILNode> body) - { - ILVariable v; - ILExpression initExpr; - if (!body[0].Match(ILCode.Stloc, out v, out initExpr)) - return false; - int initialValue; - if (!(initExpr.Match(ILCode.Ldc_I4, out initialValue) && initialValue == 1)) - return false; - doFinallyBodies = v; - return true; - } - #endregion - - #region ConvertBody - ILExpression MakeGoTo(LabelRangeMapping mapping, int state) - { - foreach (var pair in mapping) { - if (pair.Value.Contains(state)) - return new ILExpression(ILCode.Br, pair.Key); - } - throw new SymbolicAnalysisFailedException(); - } - - List<ILNode> ConvertBody(List<ILNode> body, int startPos, int bodyLength, LabelRangeMapping mapping) - { - List<ILNode> newBody = new List<ILNode>(); - // Copy all instructions from the old body to newBody. - for (int pos = startPos; pos < bodyLength; pos++) { - ILTryCatchBlock tryCatchBlock = body[pos] as ILTryCatchBlock; - ILExpression expr = body[pos] as ILExpression; - if (expr != null && expr.Code == ILCode.Leave && expr.Operand == exitLabel) { - ILVariable awaiterVar; - FieldDefinition awaiterField; - int targetStateID; - HandleAwait(newBody, out awaiterVar, out awaiterField, out targetStateID); - MarkAsGeneratedVariable(awaiterVar); - newBody.Add(new ILExpression(ILCode.Await, null, new ILExpression(ILCode.Ldloca, awaiterVar))); - newBody.Add(MakeGoTo(mapping, targetStateID)); - } else if (tryCatchBlock != null) { - ILTryCatchBlock newTryCatchBlock = new ILTryCatchBlock(); - var tryBody = tryCatchBlock.TryBlock.Body; - if (tryBody.Count == 0) - throw new SymbolicAnalysisFailedException(); - StateRangeAnalysis rangeAnalysis = new StateRangeAnalysis(tryBody[0], StateRangeAnalysisMode.AsyncMoveNext, stateField, cachedStateVar); - int tryBodyLength = tryBody.Count; - int posInTryBody = rangeAnalysis.AssignStateRanges(tryBody, tryBodyLength); - rangeAnalysis.EnsureLabelAtPos(tryBody, ref posInTryBody, ref tryBodyLength); - - var mappingInTryBlock = rangeAnalysis.CreateLabelRangeMapping(tryBody, posInTryBody, tryBodyLength); - var newTryBody = ConvertBody(tryBody, posInTryBody, tryBodyLength, mappingInTryBlock); - newTryBody.Insert(0, MakeGoTo(mappingInTryBlock, initialState)); - - // If there's a label at the beginning of the state dispatcher, copy that - if (posInTryBody > 0 && tryBody.FirstOrDefault() is ILLabel) - newTryBody.Insert(0, tryBody.First()); - - newTryCatchBlock.TryBlock = new ILBlock(newTryBody); - newTryCatchBlock.CatchBlocks = new List<ILTryCatchBlock.CatchBlock>(tryCatchBlock.CatchBlocks); - newTryCatchBlock.FaultBlock = tryCatchBlock.FaultBlock; - if (tryCatchBlock.FinallyBlock != null) - newTryCatchBlock.FinallyBlock = new ILBlock(ConvertFinally(tryCatchBlock.FinallyBlock.Body)); - - newBody.Add(newTryCatchBlock); - } else { - newBody.Add(body[pos]); - } - } - return newBody; - } - - List<ILNode> ConvertFinally(List<ILNode> body) - { - List<ILNode> newBody = new List<ILNode>(body); - if (newBody.Count == 0) - return newBody; - ILLabel endFinallyLabel; - ILExpression ceqExpr; - if (newBody[0].Match(ILCode.Brtrue, out endFinallyLabel, out ceqExpr)) { - ILExpression condition; - if (MatchLogicNot(ceqExpr, out condition)) { - if (condition.MatchLdloc(doFinallyBodies)) { - newBody.RemoveAt(0); - } else if (condition.Code == ILCode.Clt && condition.Arguments[0].MatchLdloc(cachedStateVar) && condition.Arguments[1].MatchLdcI4(0)) { - newBody.RemoveAt(0); - } - } - } - return newBody; - } - - bool MatchLogicNot(ILExpression expr, out ILExpression arg) - { - ILExpression loadZero; - object unused; - if (expr.Match(ILCode.Ceq, out unused, out arg, out loadZero)) { - int num; - return loadZero.Match(ILCode.Ldc_I4, out num) && num == 0; - } - return expr.Match(ILCode.LogicNot, out arg); - } - - void HandleAwait(List<ILNode> newBody, out ILVariable awaiterVar, out FieldDefinition awaiterField, out int targetStateID) - { - // Handle the instructions prior to the exit out of the method to detect what is being awaited. - // (analyses the last instructions in newBody and removes the analyzed instructions from newBody) - - if (doFinallyBodies != null) { - // stloc(<>t__doFinallyBodies, ldc.i4(0)) - ILExpression dfbInitExpr; - if (!newBody.LastOrDefault().MatchStloc(doFinallyBodies, out dfbInitExpr)) - throw new SymbolicAnalysisFailedException(); - int val; - if (!(dfbInitExpr.Match(ILCode.Ldc_I4, out val) && val == 0)) - throw new SymbolicAnalysisFailedException(); - newBody.RemoveAt(newBody.Count - 1); // remove doFinallyBodies assignment - } - - // call(AsyncTaskMethodBuilder::AwaitUnsafeOnCompleted, ldflda(StateMachine::<>t__builder, ldloc(this)), ldloca(CS$0$0001), ldloc(this)) - ILExpression callAwaitUnsafeOnCompleted = newBody.LastOrDefault() as ILExpression; - newBody.RemoveAt(newBody.Count - 1); // remove AwaitUnsafeOnCompleted call - if (callAwaitUnsafeOnCompleted == null || callAwaitUnsafeOnCompleted.Code != ILCode.Call) - throw new SymbolicAnalysisFailedException(); - string methodName = ((MethodReference)callAwaitUnsafeOnCompleted.Operand).Name; - if (methodName != "AwaitUnsafeOnCompleted" && methodName != "AwaitOnCompleted") - throw new SymbolicAnalysisFailedException(); - if (callAwaitUnsafeOnCompleted.Arguments.Count != 3) - throw new SymbolicAnalysisFailedException(); - if (!callAwaitUnsafeOnCompleted.Arguments[1].Match(ILCode.Ldloca, out awaiterVar)) - throw new SymbolicAnalysisFailedException(); - - // stfld(StateMachine::<>u__$awaiter6, ldloc(this), ldloc(CS$0$0001)) - FieldReference awaiterFieldRef; - ILExpression loadThis, loadAwaiterVar; - if (!newBody.LastOrDefault().Match(ILCode.Stfld, out awaiterFieldRef, out loadThis, out loadAwaiterVar)) - throw new SymbolicAnalysisFailedException(); - newBody.RemoveAt(newBody.Count - 1); // remove awaiter field assignment - awaiterField = awaiterFieldRef.ResolveWithinSameModule(); - if (!(awaiterField != null && loadThis.MatchThis() && loadAwaiterVar.MatchLdloc(awaiterVar))) - throw new SymbolicAnalysisFailedException(); - - // stfld(StateMachine::<>1__state, ldloc(this), ldc.i4(0)) - if (MatchStateAssignment(newBody.LastOrDefault(), out targetStateID)) - newBody.RemoveAt(newBody.Count - 1); // remove awaiter field assignment - else if (MatchRoslynStateAssignment(newBody, newBody.Count - 3, out targetStateID)) - newBody.RemoveRange(newBody.Count - 3, 3); // remove awaiter field assignment - } - #endregion - - #region MarkGeneratedVariables - int smallestGeneratedVariableIndex = int.MaxValue; - - void MarkAsGeneratedVariable(ILVariable v) - { - if (v.OriginalVariable != null && v.OriginalVariable.Index >= 0) { - smallestGeneratedVariableIndex = Math.Min(smallestGeneratedVariableIndex, v.OriginalVariable.Index); - } - } - - void MarkGeneratedVariables() - { - var expressions = new ILBlock(newTopLevelBody).GetSelfAndChildrenRecursive<ILExpression>(); - foreach (var v in expressions.Select(e => e.Operand).OfType<ILVariable>()) { - if (v.OriginalVariable != null && v.OriginalVariable.Index >= smallestGeneratedVariableIndex) - v.IsGenerated = true; - } - } - #endregion - - #region RunStep2() method - public static void RunStep2(DecompilerContext context, ILBlock method) - { - if (context.CurrentMethodIsAsync) { - Step2(method.Body); - ILAstOptimizer.RemoveRedundantCode(method); - // Repeat the inlining/copy propagation optimization because the conversion of field access - // to local variables can open up additional inlining possibilities. - ILInlining inlining = new ILInlining(method); - inlining.InlineAllVariables(); - inlining.CopyPropagation(); - } - } - - static void Step2(List<ILNode> body) - { - for (int pos = 0; pos < body.Count; pos++) { - ILTryCatchBlock tc = body[pos] as ILTryCatchBlock; - if (tc != null) { - Step2(tc.TryBlock.Body); - } else { - Step2(body, ref pos); - } - } - } - - static bool Step2(List<ILNode> body, ref int pos) - { - // stloc(CS$0$0001, callvirt(class System.Threading.Tasks.Task`1<bool>::GetAwaiter, awaiterExpr) - // brtrue(IL_7C, call(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::get_IsCompleted, ldloca(CS$0$0001))) - // await(ldloca(CS$0$0001)) - // ... - // IL_7C: - // arg_8B_0 = call(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::GetResult, ldloca(CS$0$0001)) - // initobj(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>, ldloca(CS$0$0001)) - - ILExpression loadAwaiter; - ILVariable awaiterVar; - if (!body[pos].Match(ILCode.Await, out loadAwaiter)) - return false; - if (!loadAwaiter.Match(ILCode.Ldloca, out awaiterVar)) - return false; - - ILVariable stackVar; - ILExpression stackExpr; - while (pos >= 1 && body[pos - 1].Match(ILCode.Stloc, out stackVar, out stackExpr)) - pos--; - - // stloc(CS$0$0001, callvirt(class System.Threading.Tasks.Task`1<bool>::GetAwaiter, awaiterExpr) - ILExpression getAwaiterCall; - if (!(pos >= 2 && body[pos - 2].MatchStloc(awaiterVar, out getAwaiterCall))) - return false; - MethodReference getAwaiterMethod; - ILExpression awaitedExpr; - if (!(getAwaiterCall.Match(ILCode.Call, out getAwaiterMethod, out awaitedExpr) || getAwaiterCall.Match(ILCode.Callvirt, out getAwaiterMethod, out awaitedExpr))) - return false; - - if (awaitedExpr.Code == ILCode.AddressOf) { - // remove 'AddressOf()' when calling GetAwaiter() on a value type - awaitedExpr = awaitedExpr.Arguments[0]; - } - - // brtrue(IL_7C, call(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::get_IsCompleted, ldloca(CS$0$0001))) - ILLabel label; - ILExpression getIsCompletedCall; - if (!(pos >= 1 && body[pos - 1].Match(ILCode.Brtrue, out label, out getIsCompletedCall))) - return false; - - int labelPos = body.IndexOf(label); - if (labelPos < pos) - return false; - for (int i = pos + 1; i < labelPos; i++) { - // validate that we aren't deleting any unexpected instructions - - // between the await and the label, there should only be the stack, awaiter and state logic - ILExpression expr = body[i] as ILExpression; - if (expr == null) - return false; - switch (expr.Code) { - case ILCode.Stloc: - case ILCode.Initobj: - case ILCode.Stfld: - case ILCode.Await: - // e.g. - // stloc(CS$0$0001, ldfld(StateMachine::<>u__$awaitere, ldloc(this))) - // initobj(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>, ldloca(CS$0$0002_66)) - // stfld('<AwaitInLoopCondition>d__d'::<>u__$awaitere, ldloc(this), ldloc(CS$0$0002_66)) - // stfld('<AwaitInLoopCondition>d__d'::<>1__state, ldloc(this), ldc.i4(-1)) - break; - default: - return false; - } - } - if (labelPos + 1 >= body.Count) - return false; - ILExpression resultAssignment = body[labelPos + 1] as ILExpression; - ILVariable resultVar; - ILExpression getResultCall; - bool isResultAssignment = resultAssignment.Match(ILCode.Stloc, out resultVar, out getResultCall); - if (!isResultAssignment) - getResultCall = resultAssignment; - if (!(getResultCall.Operand is MethodReference && ((MethodReference)getResultCall.Operand).Name == "GetResult")) - return false; - - pos -= 2; // also delete 'stloc', 'brtrue' and 'await' - body.RemoveRange(pos, labelPos - pos); - Debug.Assert(body[pos] == label); - - pos++; - if (isResultAssignment) { - Debug.Assert(body[pos] == resultAssignment); - resultAssignment.Arguments[0] = new ILExpression(ILCode.Await, null, awaitedExpr); - } else { - body[pos] = new ILExpression(ILCode.Await, null, awaitedExpr); - } - - // if the awaiter variable is cleared out in the next instruction, remove that instruction - if (IsVariableReset(body.ElementAtOrDefault(pos + 1), awaiterVar)) { - body.RemoveAt(pos + 1); - } - - return true; - } - - static bool IsVariableReset(ILNode expr, ILVariable variable) - { - object unused; - ILExpression ldloca; - return expr.Match(ILCode.Initobj, out unused, out ldloca) && ldloca.MatchLdloca(variable); - } - #endregion - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/DefaultDictionary.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/DefaultDictionary.cs deleted file mode 100644 index 6c4f824745..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/DefaultDictionary.cs +++ /dev/null @@ -1,128 +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.Collections; -using System.Collections.Generic; - -namespace ICSharpCode.Decompiler.ILAst -{ - /// <summary> - /// Dictionary with default values. - /// </summary> - sealed class DefaultDictionary<TKey, TValue> : IDictionary<TKey, TValue> - { - readonly IDictionary<TKey, TValue> dict; - readonly Func<TKey, TValue> defaultProvider; - - public DefaultDictionary(TValue defaultValue, IDictionary<TKey, TValue> dictionary = null) - : this(key => defaultValue, dictionary) - { - } - - public DefaultDictionary(Func<TKey, TValue> defaultProvider = null, IDictionary<TKey, TValue> dictionary = null) - { - this.dict = dictionary ?? new Dictionary<TKey, TValue>(); - this.defaultProvider = defaultProvider ?? (key => default(TValue)); - } - - public TValue this[TKey key] { - get { - TValue val; - if (dict.TryGetValue(key, out val)) - return val; - else - return dict[key] = defaultProvider(key); - } - set { - dict[key] = value; - } - } - - public ICollection<TKey> Keys { - get { return dict.Keys; } - } - - public ICollection<TValue> Values { - get { return dict.Values; } - } - - public int Count { - get { return dict.Count; } - } - - bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly { - get { return false; } - } - - public bool ContainsKey(TKey key) - { - return dict.ContainsKey(key); - } - - public void Add(TKey key, TValue value) - { - dict.Add(key, value); - } - - public bool Remove(TKey key) - { - return dict.Remove(key); - } - - public bool TryGetValue(TKey key, out TValue value) - { - return dict.TryGetValue(key, out value); - } - - void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) - { - dict.Add(item); - } - - public void Clear() - { - dict.Clear(); - } - - bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) - { - return dict.Contains(item); - } - - void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) - { - dict.CopyTo(array, arrayIndex); - } - - bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) - { - return dict.Remove(item); - } - - IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() - { - return dict.GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return dict.GetEnumerator(); - } - } -}
\ No newline at end of file diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs deleted file mode 100644 index fafe13e1ae..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs +++ /dev/null @@ -1,319 +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.Diagnostics; -using System.IO; -using System.Collections.Generic; -using System.Linq; - -namespace ICSharpCode.Decompiler.ILAst -{ - public class GotoRemoval - { - Dictionary<ILNode, ILNode> parent = new Dictionary<ILNode, ILNode>(); - Dictionary<ILNode, ILNode> nextSibling = new Dictionary<ILNode, ILNode>(); - - public void RemoveGotos(ILBlock method) - { - // Build the navigation data - parent[method] = null; - foreach (ILNode node in method.GetSelfAndChildrenRecursive<ILNode>()) { - ILNode previousChild = null; - foreach (ILNode child in node.GetChildren()) { - if (parent.ContainsKey(child)) - throw new Exception("The following expression is linked from several locations: " + child.ToString()); - parent[child] = node; - if (previousChild != null) - nextSibling[previousChild] = child; - previousChild = child; - } - if (previousChild != null) - nextSibling[previousChild] = null; - } - - // Simplify gotos - bool modified; - do { - modified = false; - foreach (ILExpression gotoExpr in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.Code == ILCode.Br || e.Code == ILCode.Leave)) { - modified |= TrySimplifyGoto(gotoExpr); - } - } while(modified); - - RemoveRedundantCode(method); - } - - public static void RemoveRedundantCode(ILBlock method) - { - // Remove dead lables and nops - HashSet<ILLabel> liveLabels = new HashSet<ILLabel>(method.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())); - foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - block.Body = block.Body.Where(n => !n.Match(ILCode.Nop) && !(n is ILLabel && !liveLabels.Contains((ILLabel)n))).ToList(); - } - - // Remove redundant continue - foreach(ILWhileLoop loop in method.GetSelfAndChildrenRecursive<ILWhileLoop>()) { - var body = loop.BodyBlock.Body; - if (body.Count > 0 && body.Last().Match(ILCode.LoopContinue)) { - body.RemoveAt(body.Count - 1); - } - } - - // Remove redundant break at the end of case - // Remove redundant case blocks altogether - foreach(ILSwitch ilSwitch in method.GetSelfAndChildrenRecursive<ILSwitch>()) { - foreach(ILBlock ilCase in ilSwitch.CaseBlocks) { - Debug.Assert(ilCase.EntryGoto == null); - - int count = ilCase.Body.Count; - if (count >= 2) { - if (ilCase.Body[count - 2].IsUnconditionalControlFlow() && - ilCase.Body[count - 1].Match(ILCode.LoopOrSwitchBreak)) - { - ilCase.Body.RemoveAt(count - 1); - } - } - } - - var defaultCase = ilSwitch.CaseBlocks.SingleOrDefault(cb => cb.Values == null); - // If there is no default block, remove empty case blocks - if (defaultCase == null || (defaultCase.Body.Count == 1 && defaultCase.Body.Single().Match(ILCode.LoopOrSwitchBreak))) { - ilSwitch.CaseBlocks.RemoveAll(b => b.Body.Count == 1 && b.Body.Single().Match(ILCode.LoopOrSwitchBreak)); - } - } - - // Remove redundant return at the end of method - if (method.Body.Count > 0 && method.Body.Last().Match(ILCode.Ret) && ((ILExpression)method.Body.Last()).Arguments.Count == 0) { - method.Body.RemoveAt(method.Body.Count - 1); - } - - // Remove unreachable return statements - bool modified = false; - foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - for (int i = 0; i < block.Body.Count - 1;) { - if (block.Body[i].IsUnconditionalControlFlow() && block.Body[i+1].Match(ILCode.Ret)) { - modified = true; - block.Body.RemoveAt(i+1); - } else { - i++; - } - } - } - if (modified) { - // More removals might be possible - new GotoRemoval().RemoveGotos(method); - } - } - - IEnumerable<ILNode> GetParents(ILNode node) - { - ILNode current = node; - while(true) { - current = parent[current]; - if (current == null) - yield break; - yield return current; - } - } - - bool TrySimplifyGoto(ILExpression gotoExpr) - { - Debug.Assert(gotoExpr.Code == ILCode.Br || gotoExpr.Code == ILCode.Leave); - Debug.Assert(gotoExpr.Prefixes == null); - Debug.Assert(gotoExpr.Operand != null); - - ILNode target = Enter(gotoExpr, new HashSet<ILNode>()); - if (target == null) - return false; - - // The gotoExper is marked as visited because we do not want to - // walk over node which we plan to modify - - // The simulated path always has to start in the same try-block - // in other for the same finally blocks to be executed. - - if (target == Exit(gotoExpr, new HashSet<ILNode>() { gotoExpr })) { - gotoExpr.Code = ILCode.Nop; - gotoExpr.Operand = null; - if (target is ILExpression) - ((ILExpression)target).ILRanges.AddRange(gotoExpr.ILRanges); - gotoExpr.ILRanges.Clear(); - return true; - } - - ILNode breakBlock = GetParents(gotoExpr).FirstOrDefault(n => n is ILWhileLoop || n is ILSwitch); - if (breakBlock != null && target == Exit(breakBlock, new HashSet<ILNode>() { gotoExpr })) { - gotoExpr.Code = ILCode.LoopOrSwitchBreak; - gotoExpr.Operand = null; - return true; - } - - ILNode continueBlock = GetParents(gotoExpr).FirstOrDefault(n => n is ILWhileLoop); - if (continueBlock != null && target == Enter(continueBlock, new HashSet<ILNode>() { gotoExpr })) { - gotoExpr.Code = ILCode.LoopContinue; - gotoExpr.Operand = null; - return true; - } - - return false; - } - - /// <summary> - /// Get the first expression to be excecuted if the instruction pointer is at the start of the given node. - /// Try blocks may not be entered in any way. If possible, the try block is returned as the node to be executed. - /// </summary> - ILNode Enter(ILNode node, HashSet<ILNode> visitedNodes) - { - if (node == null) - throw new ArgumentNullException(); - - if (!visitedNodes.Add(node)) - return null; // Infinite loop - - ILLabel label = node as ILLabel; - if (label != null) { - return Exit(label, visitedNodes); - } - - ILExpression expr = node as ILExpression; - if (expr != null) { - if (expr.Code == ILCode.Br || expr.Code == ILCode.Leave) { - ILLabel target = (ILLabel)expr.Operand; - // Early exit - same try-block - if (GetParents(expr).OfType<ILTryCatchBlock>().FirstOrDefault() == GetParents(target).OfType<ILTryCatchBlock>().FirstOrDefault()) - return Enter(target, visitedNodes); - // Make sure we are not entering any try-block - var srcTryBlocks = GetParents(expr).OfType<ILTryCatchBlock>().Reverse().ToList(); - var dstTryBlocks = GetParents(target).OfType<ILTryCatchBlock>().Reverse().ToList(); - // Skip blocks that we are already in - int i = 0; - while(i < srcTryBlocks.Count && i < dstTryBlocks.Count && srcTryBlocks[i] == dstTryBlocks[i]) i++; - if (i == dstTryBlocks.Count) { - return Enter(target, visitedNodes); - } else { - ILTryCatchBlock dstTryBlock = dstTryBlocks[i]; - // Check that the goto points to the start - ILTryCatchBlock current = dstTryBlock; - while(current != null) { - foreach(ILNode n in current.TryBlock.Body) { - if (n is ILLabel) { - if (n == target) - return dstTryBlock; - } else if (!n.Match(ILCode.Nop)) { - current = n as ILTryCatchBlock; - break; - } - } - } - return null; - } - } else if (expr.Code == ILCode.Nop) { - return Exit(expr, visitedNodes); - } else if (expr.Code == ILCode.LoopOrSwitchBreak) { - ILNode breakBlock = GetParents(expr).First(n => n is ILWhileLoop || n is ILSwitch); - return Exit(breakBlock, new HashSet<ILNode>() { expr }); - } else if (expr.Code == ILCode.LoopContinue) { - ILNode continueBlock = GetParents(expr).First(n => n is ILWhileLoop); - return Enter(continueBlock, new HashSet<ILNode>() { expr }); - } else { - return expr; - } - } - - ILBlock block = node as ILBlock; - if (block != null) { - if (block.EntryGoto != null) { - return Enter(block.EntryGoto, visitedNodes); - } else if (block.Body.Count > 0) { - return Enter(block.Body[0], visitedNodes); - } else { - return Exit(block, visitedNodes); - } - } - - ILCondition cond = node as ILCondition; - if (cond != null) { - return cond.Condition; - } - - ILWhileLoop loop = node as ILWhileLoop; - if (loop != null) { - if (loop.Condition != null) { - return loop.Condition; - } else { - return Enter(loop.BodyBlock, visitedNodes); - } - } - - ILTryCatchBlock tryCatch = node as ILTryCatchBlock; - if (tryCatch != null) { - return tryCatch; - } - - ILSwitch ilSwitch = node as ILSwitch; - if (ilSwitch != null) { - return ilSwitch.Condition; - } - - throw new NotSupportedException(node.GetType().ToString()); - } - - /// <summary> - /// Get the first expression to be excecuted if the instruction pointer is at the end of the given node - /// </summary> - ILNode Exit(ILNode node, HashSet<ILNode> visitedNodes) - { - if (node == null) - throw new ArgumentNullException(); - - ILNode nodeParent = parent[node]; - if (nodeParent == null) - return null; // Exited main body - - if (nodeParent is ILBlock) { - ILNode nextNode = nextSibling[node]; - if (nextNode != null) { - return Enter(nextNode, visitedNodes); - } else { - return Exit(nodeParent, visitedNodes); - } - } - - if (nodeParent is ILCondition) { - return Exit(nodeParent, visitedNodes); - } - - if (nodeParent is ILTryCatchBlock) { - // Finally blocks are completely ignored. - // We rely on the fact that try blocks can not be entered. - return Exit(nodeParent, visitedNodes); - } - - if (nodeParent is ILSwitch) { - return null; // Implicit exit from switch is not allowed - } - - if (nodeParent is ILWhileLoop) { - return Enter(nodeParent, visitedNodes); - } - - throw new NotSupportedException(nodeParent.GetType().ToString()); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs deleted file mode 100644 index 88cd2dabc7..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ /dev/null @@ -1,841 +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.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using Mono.Cecil; -using Mono.Cecil.Cil; -using Cecil = Mono.Cecil; - -namespace ICSharpCode.Decompiler.ILAst -{ - /// <summary> - /// Converts stack-based bytecode to variable-based bytecode by calculating use-define chains - /// </summary> - public class ILAstBuilder - { - /// <summary> Immutable </summary> - struct StackSlot - { - public readonly ByteCode[] Definitions; // Reaching definitions of this stack slot - public readonly ILVariable LoadFrom; // Variable used for storage of the value - - public StackSlot(ByteCode[] definitions, ILVariable loadFrom) - { - this.Definitions = definitions; - this.LoadFrom = loadFrom; - } - - public static StackSlot[] ModifyStack(StackSlot[] stack, int popCount, int pushCount, ByteCode pushDefinition) - { - StackSlot[] newStack = new StackSlot[stack.Length - popCount + pushCount]; - Array.Copy(stack, newStack, stack.Length - popCount); - for (int i = stack.Length - popCount; i < newStack.Length; i++) { - newStack[i] = new StackSlot(new [] { pushDefinition }, null); - } - return newStack; - } - } - - /// <summary> Immutable </summary> - struct VariableSlot - { - public readonly ByteCode[] Definitions; // Reaching deinitions of this variable - public readonly bool UnknownDefinition; // Used for initial state and exceptional control flow - - static readonly VariableSlot UnknownInstance = new VariableSlot(new ByteCode[0], true); - - public VariableSlot(ByteCode[] definitions, bool unknownDefinition) - { - this.Definitions = definitions; - this.UnknownDefinition = unknownDefinition; - } - - public static VariableSlot[] CloneVariableState(VariableSlot[] state) - { - VariableSlot[] clone = new VariableSlot[state.Length]; - Array.Copy(state, clone, state.Length); - return clone; - } - - public static VariableSlot[] MakeUknownState(int varCount) - { - VariableSlot[] unknownVariableState = new VariableSlot[varCount]; - for (int i = 0; i < unknownVariableState.Length; i++) { - unknownVariableState[i] = UnknownInstance; - } - return unknownVariableState; - } - } - - sealed class ByteCode - { - public ILLabel Label; // Non-null only if needed - public int Offset; - public int EndOffset; - public ILCode Code; - public object Operand; - public int? PopCount; // Null means pop all - public int PushCount; - public string Name { get { return "IL_" + this.Offset.ToString("X2"); } } - public ByteCode Next; - public Instruction[] Prefixes; // Non-null only if needed - public StackSlot[] StackBefore; // Unique per bytecode; not shared - public VariableSlot[] VariablesBefore; // Unique per bytecode; not shared - public List<ILVariable> StoreTo; // Store result of instruction to those AST variables - - public bool IsVariableDefinition { - get { - return (this.Code == ILCode.Stloc) || (this.Code == ILCode.Ldloca && this.Next != null && this.Next.Code == ILCode.Initobj); - } - } - - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - - // Label - sb.Append(this.Name); - sb.Append(':'); - if (this.Label != null) - sb.Append('*'); - - // Name - sb.Append(' '); - if (this.Prefixes != null) { - foreach (var prefix in this.Prefixes) { - sb.Append(prefix.OpCode.Name); - sb.Append(' '); - } - } - sb.Append(this.Code.GetName()); - - if (this.Operand != null) { - sb.Append(' '); - if (this.Operand is Instruction) { - sb.Append("IL_" + ((Instruction)this.Operand).Offset.ToString("X2")); - } else if (this.Operand is Instruction[]) { - foreach(Instruction inst in (Instruction[])this.Operand) { - sb.Append("IL_" + inst.Offset.ToString("X2")); - sb.Append(" "); - } - } else if (this.Operand is ILLabel) { - sb.Append(((ILLabel)this.Operand).Name); - } else if (this.Operand is ILLabel[]) { - foreach(ILLabel label in (ILLabel[])this.Operand) { - sb.Append(label.Name); - sb.Append(" "); - } - } else { - sb.Append(this.Operand.ToString()); - } - } - - if (this.StackBefore != null) { - sb.Append(" StackBefore={"); - bool first = true; - foreach (StackSlot slot in this.StackBefore) { - if (!first) sb.Append(","); - bool first2 = true; - foreach(ByteCode defs in slot.Definitions) { - if (!first2) sb.Append("|"); - sb.AppendFormat("IL_{0:X2}", defs.Offset); - first2 = false; - } - first = false; - } - sb.Append("}"); - } - - if (this.StoreTo != null && this.StoreTo.Count > 0) { - sb.Append(" StoreTo={"); - bool first = true; - foreach (ILVariable stackVar in this.StoreTo) { - if (!first) sb.Append(","); - sb.Append(stackVar.Name); - first = false; - } - sb.Append("}"); - } - - if (this.VariablesBefore != null) { - sb.Append(" VarsBefore={"); - bool first = true; - foreach (VariableSlot varSlot in this.VariablesBefore) { - if (!first) sb.Append(","); - if (varSlot.UnknownDefinition) { - sb.Append("?"); - } else { - bool first2 = true; - foreach (ByteCode storedBy in varSlot.Definitions) { - if (!first2) sb.Append("|"); - sb.AppendFormat("IL_{0:X2}", storedBy.Offset); - first2 = false; - } - } - first = false; - } - sb.Append("}"); - } - - return sb.ToString(); - } - } - - MethodDefinition methodDef; - bool optimize; - - // Virtual instructions to load exception on stack - Dictionary<ExceptionHandler, ByteCode> ldexceptions = new Dictionary<ExceptionHandler, ILAstBuilder.ByteCode>(); - - DecompilerContext context; - - public List<ILNode> Build(MethodDefinition methodDef, bool optimize, DecompilerContext context) - { - this.methodDef = methodDef; - this.optimize = optimize; - this.context = context; - - if (methodDef.Body.Instructions.Count == 0) return new List<ILNode>(); - - List<ByteCode> body = StackAnalysis(methodDef); - - List<ILNode> ast = ConvertToAst(body, new HashSet<ExceptionHandler>(methodDef.Body.ExceptionHandlers)); - - return ast; - } - - List<ByteCode> StackAnalysis(MethodDefinition methodDef) - { - Dictionary<Instruction, ByteCode> instrToByteCode = new Dictionary<Instruction, ByteCode>(); - - // Create temporary structure for the stack analysis - List<ByteCode> body = new List<ByteCode>(methodDef.Body.Instructions.Count); - List<Instruction> prefixes = null; - foreach(Instruction inst in methodDef.Body.Instructions) { - if (inst.OpCode.OpCodeType == OpCodeType.Prefix) { - if (prefixes == null) - prefixes = new List<Instruction>(1); - prefixes.Add(inst); - continue; - } - ILCode code = (ILCode)inst.OpCode.Code; - object operand = inst.Operand; - ILCodeUtil.ExpandMacro(ref code, ref operand, methodDef.Body); - ByteCode byteCode = new ByteCode() { - Offset = inst.Offset, - EndOffset = inst.Next != null ? inst.Next.Offset : methodDef.Body.CodeSize, - Code = code, - Operand = operand, - PopCount = inst.GetPopDelta(methodDef), - PushCount = inst.GetPushDelta() - }; - if (prefixes != null) { - instrToByteCode[prefixes[0]] = byteCode; - byteCode.Offset = prefixes[0].Offset; - byteCode.Prefixes = prefixes.ToArray(); - prefixes = null; - } else { - instrToByteCode[inst] = byteCode; - } - body.Add(byteCode); - } - for (int i = 0; i < body.Count - 1; i++) { - body[i].Next = body[i + 1]; - } - - Stack<ByteCode> agenda = new Stack<ByteCode>(); - - int varCount = methodDef.Body.Variables.Count; - - var exceptionHandlerStarts = new HashSet<ByteCode>(methodDef.Body.ExceptionHandlers.Select(eh => instrToByteCode[eh.HandlerStart])); - - // Add known states - if(methodDef.Body.HasExceptionHandlers) { - foreach(ExceptionHandler ex in methodDef.Body.ExceptionHandlers) { - ByteCode handlerStart = instrToByteCode[ex.HandlerStart]; - handlerStart.StackBefore = new StackSlot[0]; - handlerStart.VariablesBefore = VariableSlot.MakeUknownState(varCount); - if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter) { - // Catch and Filter handlers start with the exeption on the stack - ByteCode ldexception = new ByteCode() { - Code = ILCode.Ldexception, - Operand = ex.CatchType, - PopCount = 0, - PushCount = 1 - }; - ldexceptions[ex] = ldexception; - handlerStart.StackBefore = new StackSlot[] { new StackSlot(new [] { ldexception }, null) }; - } - agenda.Push(handlerStart); - - if (ex.HandlerType == ExceptionHandlerType.Filter) - { - ByteCode filterStart = instrToByteCode[ex.FilterStart]; - ByteCode ldexception = new ByteCode() { - Code = ILCode.Ldexception, - Operand = ex.CatchType, - PopCount = 0, - PushCount = 1 - }; - // TODO: ldexceptions[ex] = ldexception; - filterStart.StackBefore = new StackSlot[] { new StackSlot(new [] { ldexception }, null) }; - filterStart.VariablesBefore = VariableSlot.MakeUknownState(varCount); - agenda.Push(filterStart); - } - } - } - - body[0].StackBefore = new StackSlot[0]; - body[0].VariablesBefore = VariableSlot.MakeUknownState(varCount); - agenda.Push(body[0]); - - // Process agenda - while(agenda.Count > 0) { - ByteCode byteCode = agenda.Pop(); - - // Calculate new stack - StackSlot[] newStack = StackSlot.ModifyStack(byteCode.StackBefore, byteCode.PopCount ?? byteCode.StackBefore.Length, byteCode.PushCount, byteCode); - - // Calculate new variable state - VariableSlot[] newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore); - if (byteCode.IsVariableDefinition) { - newVariableState[((VariableReference)byteCode.Operand).Index] = new VariableSlot(new [] { byteCode }, false); - } - - // After the leave, finally block might have touched the variables - if (byteCode.Code == ILCode.Leave) { - newVariableState = VariableSlot.MakeUknownState(varCount); - } - - // Find all successors - List<ByteCode> branchTargets = new List<ByteCode>(); - if (!byteCode.Code.IsUnconditionalControlFlow()) { - if (exceptionHandlerStarts.Contains(byteCode.Next)) { - // Do not fall though down to exception handler - // It is invalid IL as per ECMA-335 §12.4.2.8.1, but some obfuscators produce it - } else { - branchTargets.Add(byteCode.Next); - } - } - if (byteCode.Operand is Instruction[]) { - foreach(Instruction inst in (Instruction[])byteCode.Operand) { - ByteCode target = instrToByteCode[inst]; - branchTargets.Add(target); - // The target of a branch must have label - if (target.Label == null) { - target.Label = new ILLabel() { Name = target.Name }; - } - } - } else if (byteCode.Operand is Instruction) { - ByteCode target = instrToByteCode[(Instruction)byteCode.Operand]; - branchTargets.Add(target); - // The target of a branch must have label - if (target.Label == null) { - target.Label = new ILLabel() { Name = target.Name }; - } - } - - // Apply the state to successors - foreach (ByteCode branchTarget in branchTargets) { - if (branchTarget.StackBefore == null && branchTarget.VariablesBefore == null) { - if (branchTargets.Count == 1) { - branchTarget.StackBefore = newStack; - branchTarget.VariablesBefore = newVariableState; - } else { - // Do not share data for several bytecodes - branchTarget.StackBefore = StackSlot.ModifyStack(newStack, 0, 0, null); - branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState); - } - agenda.Push(branchTarget); - } else { - if (branchTarget.StackBefore.Length != newStack.Length) { - throw new Exception("Inconsistent stack size at " + byteCode.Name); - } - - // Be careful not to change our new data - it might be reused for several branch targets. - // In general, be careful that two bytecodes never share data structures. - - bool modified = false; - - // Merge stacks - modify the target - for (int i = 0; i < newStack.Length; i++) { - ByteCode[] oldDefs = branchTarget.StackBefore[i].Definitions; - ByteCode[] newDefs = oldDefs.Union(newStack[i].Definitions); - if (newDefs.Length > oldDefs.Length) { - branchTarget.StackBefore[i] = new StackSlot(newDefs, null); - modified = true; - } - } - - // Merge variables - modify the target - for (int i = 0; i < newVariableState.Length; i++) { - VariableSlot oldSlot = branchTarget.VariablesBefore[i]; - VariableSlot newSlot = newVariableState[i]; - if (!oldSlot.UnknownDefinition) { - if (newSlot.UnknownDefinition) { - branchTarget.VariablesBefore[i] = newSlot; - modified = true; - } else { - ByteCode[] oldDefs = oldSlot.Definitions; - ByteCode[] newDefs = oldDefs.Union(newSlot.Definitions); - if (newDefs.Length > oldDefs.Length) { - branchTarget.VariablesBefore[i] = new VariableSlot(newDefs, false); - modified = true; - } - } - } - } - - if (modified) { - agenda.Push(branchTarget); - } - } - } - } - - // Occasionally the compilers or obfuscators generate unreachable code (which might be intentionally invalid) - // I believe it is safe to just remove it - body.RemoveAll(b => b.StackBefore == null); - - // Generate temporary variables to replace stack - foreach(ByteCode byteCode in body) { - int argIdx = 0; - int popCount = byteCode.PopCount ?? byteCode.StackBefore.Length; - for (int i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++) { - ILVariable tmpVar = new ILVariable() { Name = string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), IsGenerated = true }; - byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].Definitions, tmpVar); - foreach(ByteCode pushedBy in byteCode.StackBefore[i].Definitions) { - if (pushedBy.StoreTo == null) { - pushedBy.StoreTo = new List<ILVariable>(1); - } - pushedBy.StoreTo.Add(tmpVar); - } - argIdx++; - } - } - - // Try to use single temporary variable insted of several if possilbe (especially useful for dup) - // This has to be done after all temporary variables are assigned so we know about all loads - foreach(ByteCode byteCode in body) { - if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1) { - var locVars = byteCode.StoreTo; - // For each of the variables, find the location where it is loaded - there should be preciesly one - var loadedBy = locVars.Select(locVar => body.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList(); - // We now know that all the variables have a single load, - // Let's make sure that they have also a single store - us - if (loadedBy.All(slot => slot.Definitions.Length == 1 && slot.Definitions[0] == byteCode)) { - // Great - we can reduce everything into single variable - ILVariable tmpVar = new ILVariable() { Name = string.Format("expr_{0:X2}", byteCode.Offset), IsGenerated = true }; - byteCode.StoreTo = new List<ILVariable>() { tmpVar }; - foreach(ByteCode bc in body) { - for (int i = 0; i < bc.StackBefore.Length; i++) { - // Is it one of the variable to be merged? - if (locVars.Contains(bc.StackBefore[i].LoadFrom)) { - // Replace with the new temp variable - bc.StackBefore[i] = new StackSlot(bc.StackBefore[i].Definitions, tmpVar); - } - } - } - } - } - } - - // Split and convert the normal local variables - ConvertLocalVariables(body, methodDef); - - // Convert branch targets to labels - foreach(ByteCode byteCode in body) { - if (byteCode.Operand is Instruction[]) { - List<ILLabel> newOperand = new List<ILLabel>(); - foreach(Instruction target in (Instruction[])byteCode.Operand) { - newOperand.Add(instrToByteCode[target].Label); - } - byteCode.Operand = newOperand.ToArray(); - } else if (byteCode.Operand is Instruction) { - byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label; - } - } - - // Convert parameters to ILVariables - ConvertParameters(body); - - return body; - } - - static bool IsDeterministicLdloca(ByteCode b) - { - var v = b.Operand; - b = b.Next; - if (b.Code == ILCode.Initobj) return true; - - // instance method calls on value types use the variable ref deterministically - int stack = 1; - while (true) { - if (b.PopCount == null) return false; - stack -= b.PopCount.GetValueOrDefault(); - if (stack == 0) break; - if (stack < 0) return false; - if (b.Code.IsConditionalControlFlow() || b.Code.IsUnconditionalControlFlow()) return false; - switch (b.Code) { - case ILCode.Ldloc: - case ILCode.Ldloca: - case ILCode.Stloc: - if (b.Operand == v) return false; - break; - } - stack += b.PushCount; - b = b.Next; - if (b == null) return false; - } - if (b.Code == ILCode.Ldfld || b.Code == ILCode.Stfld) - return true; - return (b.Code == ILCode.Call || b.Code == ILCode.Callvirt) && ((MethodReference)b.Operand).HasThis; - } - - sealed class VariableInfo - { - public ILVariable Variable; - public List<ByteCode> Defs; - public List<ByteCode> Uses; - } - - /// <summary> - /// If possible, separates local variables into several independent variables. - /// It should undo any compilers merging. - /// </summary> - void ConvertLocalVariables(List<ByteCode> body, MethodDefinition mdef) - { - foreach(VariableDefinition varDef in methodDef.Body.Variables) { - - // Find all definitions and uses of this variable - var defs = body.Where(b => b.Operand == varDef && b.IsVariableDefinition).ToList(); - var uses = body.Where(b => b.Operand == varDef && !b.IsVariableDefinition).ToList(); - - List<VariableInfo> newVars; - - // If the variable is pinned, use single variable. - // If any of the uses is from unknown definition, use single variable - // If any of the uses is ldloca with a nondeterministic usage pattern, use single variable - if (!optimize || varDef.IsPinned || uses.Any(b => b.VariablesBefore[varDef.Index].UnknownDefinition || (b.Code == ILCode.Ldloca && !IsDeterministicLdloca(b)))) { - string varName; - if (!mdef.DebugInformation.TryGetName (varDef, out varName)) - varName = "var_" + varDef.Index; - newVars = new List<VariableInfo>(1) { new VariableInfo() { - Variable = new ILVariable() { - Name = varName, - Type = varDef.IsPinned ? ((PinnedType)varDef.VariableType).ElementType : varDef.VariableType, - OriginalVariable = varDef - }, - Defs = defs, - Uses = uses - }}; - } else { - string varName; - if (!mdef.DebugInformation.TryGetName (varDef, out varName)) - varName = "var_" + varDef.Index; - // Create a new variable for each definition - newVars = defs.Select(def => new VariableInfo() { - Variable = new ILVariable() { - Name = varName + "_" + def.Offset.ToString("X2"), - Type = varDef.VariableType, - OriginalVariable = varDef - }, - Defs = new List<ByteCode>() { def }, - Uses = new List<ByteCode>() - }).ToList(); - - // VB.NET uses the 'init' to allow use of uninitialized variables. - // We do not really care about them too much - if the original variable - // was uninitialized at that point it means that no store was called and - // thus all our new variables must be uninitialized as well. - // So it does not matter which one we load. - - // TODO: We should add explicit initialization so that C# code compiles. - // Remember to handle cases where one path inits the variable, but other does not. - - // Add loads to the data structure; merge variables if necessary - foreach(ByteCode use in uses) { - ByteCode[] useDefs = use.VariablesBefore[varDef.Index].Definitions; - if (useDefs.Length == 1) { - VariableInfo newVar = newVars.Single(v => v.Defs.Contains(useDefs[0])); - newVar.Uses.Add(use); - } else { - List<VariableInfo> mergeVars = newVars.Where(v => v.Defs.Intersect(useDefs).Any()).ToList(); - VariableInfo mergedVar = new VariableInfo() { - Variable = mergeVars[0].Variable, - Defs = mergeVars.SelectMany(v => v.Defs).ToList(), - Uses = mergeVars.SelectMany(v => v.Uses).ToList() - }; - mergedVar.Uses.Add(use); - newVars = newVars.Except(mergeVars).ToList(); - newVars.Add(mergedVar); - } - } - } - - // Set bytecode operands - foreach(VariableInfo newVar in newVars) { - foreach(ByteCode def in newVar.Defs) { - def.Operand = newVar.Variable; - } - foreach(ByteCode use in newVar.Uses) { - use.Operand = newVar.Variable; - } - } - } - } - - public List<ILVariable> Parameters = new List<ILVariable>(); - - void ConvertParameters(List<ByteCode> body) - { - ILVariable thisParameter = null; - if (methodDef.HasThis) { - TypeReference type = methodDef.DeclaringType; - thisParameter = new ILVariable(); - thisParameter.Type = type.IsValueType ? new ByReferenceType(type) : type; - thisParameter.Name = "this"; - thisParameter.OriginalParameter = methodDef.Body.ThisParameter; - } - foreach (ParameterDefinition p in methodDef.Parameters) { - this.Parameters.Add(new ILVariable { Type = p.ParameterType, Name = p.Name, OriginalParameter = p }); - } - if (this.Parameters.Count > 0 && (methodDef.IsSetter || methodDef.IsAddOn || methodDef.IsRemoveOn)) { - // last parameter must be 'value', so rename it - this.Parameters.Last().Name = "value"; - } - foreach (ByteCode byteCode in body) { - ParameterDefinition p; - switch (byteCode.Code) { - case ILCode.__Ldarg: - p = (ParameterDefinition)byteCode.Operand; - byteCode.Code = ILCode.Ldloc; - byteCode.Operand = p.Index < 0 ? thisParameter : this.Parameters[p.Index]; - break; - case ILCode.__Starg: - p = (ParameterDefinition)byteCode.Operand; - byteCode.Code = ILCode.Stloc; - byteCode.Operand = p.Index < 0 ? thisParameter : this.Parameters[p.Index]; - break; - case ILCode.__Ldarga: - p = (ParameterDefinition)byteCode.Operand; - byteCode.Code = ILCode.Ldloca; - byteCode.Operand = p.Index < 0 ? thisParameter : this.Parameters[p.Index]; - break; - } - } - if (thisParameter != null) - this.Parameters.Add(thisParameter); - } - - List<ILNode> ConvertToAst(List<ByteCode> body, HashSet<ExceptionHandler> ehs) - { - List<ILNode> ast = new List<ILNode>(); - - while (ehs.Any()) { - ILTryCatchBlock tryCatchBlock = new ILTryCatchBlock(); - - // Find the first and widest scope - int tryStart = ehs.Min(eh => eh.TryStart.Offset); - int tryEnd = ehs.Where(eh => eh.TryStart.Offset == tryStart).Max(eh => eh.TryEnd.Offset); - var handlers = ehs.Where(eh => eh.TryStart.Offset == tryStart && eh.TryEnd.Offset == tryEnd).ToList(); - - // Remember that any part of the body migt have been removed due to unreachability - - // Cut all instructions up to the try block - { - int tryStartIdx = 0; - while (tryStartIdx < body.Count && body[tryStartIdx].Offset < tryStart) tryStartIdx++; - ast.AddRange(ConvertToAst(body.CutRange(0, tryStartIdx))); - } - - // Cut the try block - { - HashSet<ExceptionHandler> nestedEHs = new HashSet<ExceptionHandler>(ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd))); - ehs.ExceptWith(nestedEHs); - int tryEndIdx = 0; - while (tryEndIdx < body.Count && body[tryEndIdx].Offset < tryEnd) tryEndIdx++; - tryCatchBlock.TryBlock = new ILBlock(ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs)); - } - - // Cut all handlers - tryCatchBlock.CatchBlocks = new List<ILTryCatchBlock.CatchBlock>(); - foreach(ExceptionHandler eh in handlers) { - int handlerEndOffset = eh.HandlerEnd == null ? methodDef.Body.CodeSize : eh.HandlerEnd.Offset; - int startIdx = 0; - while (startIdx < body.Count && body[startIdx].Offset < eh.HandlerStart.Offset) startIdx++; - int endIdx = 0; - while (endIdx < body.Count && body[endIdx].Offset < handlerEndOffset) endIdx++; - HashSet<ExceptionHandler> nestedEHs = new HashSet<ExceptionHandler>(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < handlerEndOffset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= handlerEndOffset))); - ehs.ExceptWith(nestedEHs); - List<ILNode> handlerAst = ConvertToAst(body.CutRange(startIdx, endIdx - startIdx), nestedEHs); - if (eh.HandlerType == ExceptionHandlerType.Catch) { - ILTryCatchBlock.CatchBlock catchBlock = new ILTryCatchBlock.CatchBlock() { - ExceptionType = eh.CatchType, - Body = handlerAst - }; - // Handle the automatically pushed exception on the stack - ByteCode ldexception = ldexceptions[eh]; - if (ldexception.StoreTo == null || ldexception.StoreTo.Count == 0) { - // Exception is not used - catchBlock.ExceptionVariable = null; - } else if (ldexception.StoreTo.Count == 1) { - ILExpression first = catchBlock.Body[0] as ILExpression; - if (first != null && - first.Code == ILCode.Pop && - first.Arguments[0].Code == ILCode.Ldloc && - first.Arguments[0].Operand == ldexception.StoreTo[0]) - { - // The exception is just poped - optimize it all away; - if (context.Settings.AlwaysGenerateExceptionVariableForCatchBlocks) - catchBlock.ExceptionVariable = new ILVariable() { Name = "ex_" + eh.HandlerStart.Offset.ToString("X2"), IsGenerated = true }; - else - catchBlock.ExceptionVariable = null; - catchBlock.Body.RemoveAt(0); - } else { - catchBlock.ExceptionVariable = ldexception.StoreTo[0]; - } - } else { - ILVariable exTemp = new ILVariable() { Name = "ex_" + eh.HandlerStart.Offset.ToString("X2"), IsGenerated = true }; - catchBlock.ExceptionVariable = exTemp; - foreach(ILVariable storeTo in ldexception.StoreTo) { - catchBlock.Body.Insert(0, new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, exTemp))); - } - } - tryCatchBlock.CatchBlocks.Add(catchBlock); - } else if (eh.HandlerType == ExceptionHandlerType.Finally) { - tryCatchBlock.FinallyBlock = new ILBlock(handlerAst); - } else if (eh.HandlerType == ExceptionHandlerType.Fault) { - tryCatchBlock.FaultBlock = new ILBlock(handlerAst); - } else { - // TODO: ExceptionHandlerType.Filter - } - } - - ehs.ExceptWith(handlers); - - ast.Add(tryCatchBlock); - } - - // Add whatever is left - ast.AddRange(ConvertToAst(body)); - - return ast; - } - - List<ILNode> ConvertToAst(List<ByteCode> body) - { - List<ILNode> ast = new List<ILNode>(); - - // Convert stack-based IL code to ILAst tree - foreach(ByteCode byteCode in body) { - ILRange ilRange = new ILRange(byteCode.Offset, byteCode.EndOffset); - - if (byteCode.StackBefore == null) { - // Unreachable code - continue; - } - - ILExpression expr = new ILExpression(byteCode.Code, byteCode.Operand); - expr.ILRanges.Add(ilRange); - if (byteCode.Prefixes != null && byteCode.Prefixes.Length > 0) { - ILExpressionPrefix[] prefixes = new ILExpressionPrefix[byteCode.Prefixes.Length]; - for (int i = 0; i < prefixes.Length; i++) { - prefixes[i] = new ILExpressionPrefix((ILCode)byteCode.Prefixes[i].OpCode.Code, byteCode.Prefixes[i].Operand); - } - expr.Prefixes = prefixes; - } - - // Label for this instruction - if (byteCode.Label != null) { - ast.Add(byteCode.Label); - } - - // Reference arguments using temporary variables - int popCount = byteCode.PopCount ?? byteCode.StackBefore.Length; - for (int i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++) { - StackSlot slot = byteCode.StackBefore[i]; - expr.Arguments.Add(new ILExpression(ILCode.Ldloc, slot.LoadFrom)); - } - - // Store the result to temporary variable(s) if needed - if (byteCode.StoreTo == null || byteCode.StoreTo.Count == 0) { - ast.Add(expr); - } else if (byteCode.StoreTo.Count == 1) { - ast.Add(new ILExpression(ILCode.Stloc, byteCode.StoreTo[0], expr)); - } else { - ILVariable tmpVar = new ILVariable() { Name = "expr_" + byteCode.Offset.ToString("X2"), IsGenerated = true }; - ast.Add(new ILExpression(ILCode.Stloc, tmpVar, expr)); - foreach(ILVariable storeTo in byteCode.StoreTo.AsEnumerable().Reverse()) { - ast.Add(new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, tmpVar))); - } - } - } - - return ast; - } - } - - public static class ILAstBuilderExtensionMethods - { - public static List<T> CutRange<T>(this List<T> list, int start, int count) - { - List<T> ret = new List<T>(count); - for (int i = 0; i < count; i++) { - ret.Add(list[start + i]); - } - list.RemoveRange(start, count); - return ret; - } - - public static T[] Union<T>(this T[] a, T b) - { - if (a.Length == 0) - return new[] { b }; - if (Array.IndexOf(a, b) >= 0) - return a; - var res = new T[a.Length + 1]; - Array.Copy(a, 0, res, 0, a.Length); - res[res.Length - 1] = b; - return res; - } - - public static T[] Union<T>(this T[] a, T[] b) - { - if (a == b) - return a; - if (a.Length == 0) - return b; - if (b.Length == 0) - return a; - if (a.Length == 1) { - if (b.Length == 1) - return a[0].Equals(b[0]) ? a : new[] { a[0], b[0] }; - return b.Union(a[0]); - } - if (b.Length == 1) - return a.Union(b[0]); - return Enumerable.Union(a, b).ToArray(); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs deleted file mode 100644 index ecf1e9fc5f..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ /dev/null @@ -1,1060 +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.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using ICSharpCode.Decompiler.FlowAnalysis; -using ICSharpCode.NRefactory.Utils; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.ILAst -{ - public enum ILAstOptimizationStep - { - RemoveRedundantCode, - ReduceBranchInstructionSet, - InlineVariables, - CopyPropagation, - YieldReturn, - AsyncAwait, - PropertyAccessInstructions, - SplitToMovableBlocks, - TypeInference, - HandlePointerArithmetic, - SimplifyShortCircuit, - SimplifyTernaryOperator, - SimplifyNullCoalescing, - JoinBasicBlocks, - SimplifyLogicNot, - SimplifyShiftOperators, - TypeConversionSimplifications, - SimplifyLdObjAndStObj, - SimplifyCustomShortCircuit, - SimplifyLiftedOperators, - TransformArrayInitializers, - TransformMultidimensionalArrayInitializers, - TransformObjectInitializers, - MakeAssignmentExpression, - IntroducePostIncrement, - InlineExpressionTreeParameterDeclarations, - InlineVariables2, - FindLoops, - FindConditions, - FlattenNestedMovableBlocks, - RemoveEndFinally, - RemoveRedundantCode2, - GotoRemoval, - DuplicateReturns, - GotoRemoval2, - ReduceIfNesting, - InlineVariables3, - CachedDelegateInitialization, - IntroduceFixedStatements, - RecombineVariables, - TypeInference2, - RemoveRedundantCode3, - None - } - - public partial class ILAstOptimizer - { - int nextLabelIndex = 0; - - DecompilerContext context; - TypeSystem typeSystem; - ILBlock method; - - public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) - { - this.context = context; - this.typeSystem = context.CurrentMethod.Module.TypeSystem; - this.method = method; - - if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode) return; - RemoveRedundantCode(method); - - if (abortBeforeStep == ILAstOptimizationStep.ReduceBranchInstructionSet) return; - foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - ReduceBranchInstructionSet(block); - } - // ReduceBranchInstructionSet runs before inlining because the non-aggressive inlining heuristic - // looks at which type of instruction consumes the inlined variable. - - if (abortBeforeStep == ILAstOptimizationStep.InlineVariables) return; - // Works better after simple goto removal because of the following debug pattern: stloc X; br Next; Next:; ldloc X - ILInlining inlining1 = new ILInlining(method); - inlining1.InlineAllVariables(); - - if (abortBeforeStep == ILAstOptimizationStep.CopyPropagation) return; - inlining1.CopyPropagation(); - - if (abortBeforeStep == ILAstOptimizationStep.YieldReturn) return; - YieldReturnDecompiler.Run(context, method); - AsyncDecompiler.RunStep1(context, method); - - if (abortBeforeStep == ILAstOptimizationStep.AsyncAwait) return; - AsyncDecompiler.RunStep2(context, method); - - if (abortBeforeStep == ILAstOptimizationStep.PropertyAccessInstructions) return; - IntroducePropertyAccessInstructions(method); - - if (abortBeforeStep == ILAstOptimizationStep.SplitToMovableBlocks) return; - foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - SplitToBasicBlocks(block); - } - - if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return; - // Types are needed for the ternary operator optimization - TypeAnalysis.Run(context, method); - - if (abortBeforeStep == ILAstOptimizationStep.HandlePointerArithmetic) return; - HandlePointerArithmetic(method); - - foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - bool modified; - do { - modified = false; - - if (abortBeforeStep == ILAstOptimizationStep.SimplifyShortCircuit) return; - modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyShortCircuit); - - if (abortBeforeStep == ILAstOptimizationStep.SimplifyTernaryOperator) return; - modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyTernaryOperator); - - if (abortBeforeStep == ILAstOptimizationStep.SimplifyNullCoalescing) return; - modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyNullCoalescing); - - if (abortBeforeStep == ILAstOptimizationStep.JoinBasicBlocks) return; - modified |= block.RunOptimization(new SimpleControlFlow(context, method).JoinBasicBlocks); - - if (abortBeforeStep == ILAstOptimizationStep.SimplifyLogicNot) return; - modified |= block.RunOptimization(SimplifyLogicNot); - - if (abortBeforeStep == ILAstOptimizationStep.SimplifyShiftOperators) return; - modified |= block.RunOptimization(SimplifyShiftOperators); - - if (abortBeforeStep == ILAstOptimizationStep.TypeConversionSimplifications) return; - modified |= block.RunOptimization(TypeConversionSimplifications); - - if (abortBeforeStep == ILAstOptimizationStep.SimplifyLdObjAndStObj) return; - modified |= block.RunOptimization(SimplifyLdObjAndStObj); - - if (abortBeforeStep == ILAstOptimizationStep.SimplifyCustomShortCircuit) return; - modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyCustomShortCircuit); - - if (abortBeforeStep == ILAstOptimizationStep.SimplifyLiftedOperators) return; - modified |= block.RunOptimization(SimplifyLiftedOperators); - - if (abortBeforeStep == ILAstOptimizationStep.TransformArrayInitializers) return; - modified |= block.RunOptimization(TransformArrayInitializers); - - if (abortBeforeStep == ILAstOptimizationStep.TransformMultidimensionalArrayInitializers) return; - modified |= block.RunOptimization(TransformMultidimensionalArrayInitializers); - - if (abortBeforeStep == ILAstOptimizationStep.TransformObjectInitializers) return; - modified |= block.RunOptimization(TransformObjectInitializers); - - if (abortBeforeStep == ILAstOptimizationStep.MakeAssignmentExpression) return; - if (context.Settings.MakeAssignmentExpressions) { - modified |= block.RunOptimization(MakeAssignmentExpression); - } - modified |= block.RunOptimization(MakeCompoundAssignments); - - if (abortBeforeStep == ILAstOptimizationStep.IntroducePostIncrement) return; - if (context.Settings.IntroduceIncrementAndDecrement) { - modified |= block.RunOptimization(IntroducePostIncrement); - } - - if (abortBeforeStep == ILAstOptimizationStep.InlineExpressionTreeParameterDeclarations) return; - if (context.Settings.ExpressionTrees) { - modified |= block.RunOptimization(InlineExpressionTreeParameterDeclarations); - } - - if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return; - modified |= new ILInlining(method).InlineAllInBlock(block); - new ILInlining(method).CopyPropagation(); - - } while(modified); - } - - if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return; - foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - new LoopsAndConditions(context).FindLoops(block); - } - - if (abortBeforeStep == ILAstOptimizationStep.FindConditions) return; - foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - new LoopsAndConditions(context).FindConditions(block); - } - - if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return; - FlattenBasicBlocks(method); - - if (abortBeforeStep == ILAstOptimizationStep.RemoveEndFinally) return; - RemoveEndFinally(method); - - if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode2) return; - RemoveRedundantCode(method); - - if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return; - new GotoRemoval().RemoveGotos(method); - - if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return; - DuplicateReturnStatements(method); - - if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval2) return; - new GotoRemoval().RemoveGotos(method); - - if (abortBeforeStep == ILAstOptimizationStep.ReduceIfNesting) return; - ReduceIfNesting(method); - - if (abortBeforeStep == ILAstOptimizationStep.InlineVariables3) return; - // The 2nd inlining pass is necessary because DuplicateReturns and the introduction of ternary operators - // open up additional inlining possibilities. - new ILInlining(method).InlineAllVariables(); - - if (abortBeforeStep == ILAstOptimizationStep.CachedDelegateInitialization) return; - if (context.Settings.AnonymousMethods) { - foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - for (int i = 0; i < block.Body.Count; i++) { - // TODO: Move before loops - CachedDelegateInitializationWithField(block, ref i); - CachedDelegateInitializationWithLocal(block, ref i); - } - } - } - - if (abortBeforeStep == ILAstOptimizationStep.IntroduceFixedStatements) return; - // we need post-order traversal, not pre-order, for "fixed" to work correctly - foreach (ILBlock block in TreeTraversal.PostOrder<ILNode>(method, n => n.GetChildren()).OfType<ILBlock>()) { - for (int i = block.Body.Count - 1; i >= 0; i--) { - // TODO: Move before loops - if (i < block.Body.Count) - IntroduceFixedStatements(block.Body, i); - } - } - - if (abortBeforeStep == ILAstOptimizationStep.RecombineVariables) return; - RecombineVariables(method); - - if (abortBeforeStep == ILAstOptimizationStep.TypeInference2) return; - TypeAnalysis.Reset(method); - TypeAnalysis.Run(context, method); - - if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode3) return; - GotoRemoval.RemoveRedundantCode(method); - - // ReportUnassignedILRanges(method); - } - - /// <summary> - /// Removes redundatant Br, Nop, Dup, Pop - /// Ignore arguments of 'leave' - /// </summary> - /// <param name="method"></param> - internal static void RemoveRedundantCode(ILBlock method) - { - Dictionary<ILLabel, int> labelRefCount = new Dictionary<ILLabel, int>(); - foreach (ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { - labelRefCount[target] = labelRefCount.GetOrDefault(target) + 1; - } - - foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - List<ILNode> body = block.Body; - List<ILNode> newBody = new List<ILNode>(body.Count); - for (int i = 0; i < body.Count; i++) { - ILLabel target; - ILExpression popExpr; - if (body[i].Match(ILCode.Br, out target) && i+1 < body.Count && body[i+1] == target) { - // Ignore the branch - if (labelRefCount[target] == 1) - i++; // Ignore the label as well - } else if (body[i].Match(ILCode.Nop)){ - // Ignore nop - } else if (body[i].Match(ILCode.Pop, out popExpr)) { - ILVariable v; - if (!popExpr.Match(ILCode.Ldloc, out v)) - throw new Exception("Pop should have just ldloc at this stage"); - // Best effort to move the ILRange to previous statement - ILVariable prevVar; - ILExpression prevExpr; - if (i - 1 >= 0 && body[i - 1].Match(ILCode.Stloc, out prevVar, out prevExpr) && prevVar == v) - prevExpr.ILRanges.AddRange(((ILExpression)body[i]).ILRanges); - // Ignore pop - } else { - ILLabel label = body[i] as ILLabel; - if (label != null) { - if (labelRefCount.GetOrDefault(label) > 0) - newBody.Add(label); - } else { - newBody.Add(body[i]); - } - } - } - block.Body = newBody; - } - - // Ignore arguments of 'leave' - foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.Code == ILCode.Leave)) { - if (expr.Arguments.Any(arg => !arg.Match(ILCode.Ldloc))) - throw new Exception("Leave should have just ldloc at this stage"); - expr.Arguments.Clear(); - } - - // 'dup' removal - foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) { - for (int i = 0; i < expr.Arguments.Count; i++) { - ILExpression child; - if (expr.Arguments[i].Match(ILCode.Dup, out child)) { - child.ILRanges.AddRange(expr.Arguments[i].ILRanges); - expr.Arguments[i] = child; - } - } - } - } - - /// <summary> - /// Reduces the branch codes to just br and brtrue. - /// Moves ILRanges to the branch argument - /// </summary> - void ReduceBranchInstructionSet(ILBlock block) - { - for (int i = 0; i < block.Body.Count; i++) { - ILExpression expr = block.Body[i] as ILExpression; - if (expr != null && expr.Prefixes == null) { - ILCode op; - switch(expr.Code) { - case ILCode.Switch: - case ILCode.Brtrue: - expr.Arguments.Single().ILRanges.AddRange(expr.ILRanges); - expr.ILRanges.Clear(); - continue; - case ILCode.__Brfalse: op = ILCode.LogicNot; break; - case ILCode.__Beq: op = ILCode.Ceq; break; - case ILCode.__Bne_Un: op = ILCode.Cne; break; - case ILCode.__Bgt: op = ILCode.Cgt; break; - case ILCode.__Bgt_Un: op = ILCode.Cgt_Un; break; - case ILCode.__Ble: op = ILCode.Cle; break; - case ILCode.__Ble_Un: op = ILCode.Cle_Un; break; - case ILCode.__Blt: op = ILCode.Clt; break; - case ILCode.__Blt_Un: op = ILCode.Clt_Un; break; - case ILCode.__Bge: op = ILCode.Cge; break; - case ILCode.__Bge_Un: op = ILCode.Cge_Un; break; - default: - continue; - } - var newExpr = new ILExpression(op, null, expr.Arguments); - block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, newExpr); - newExpr.ILRanges = expr.ILRanges; - } - } - } - - /// <summary> - /// Converts call and callvirt instructions that read/write properties into CallGetter/CallSetter instructions. - /// - /// CallGetter/CallSetter is used to allow the ILAst to represent "while ((SomeProperty = value) != null)". - /// - /// Also simplifies 'newobj(SomeDelegate, target, ldvirtftn(F, target))' to 'newobj(SomeDelegate, target, ldvirtftn(F))' - /// </summary> - void IntroducePropertyAccessInstructions(ILNode node) - { - ILExpression parentExpr = node as ILExpression; - if (parentExpr != null) { - for (int i = 0; i < parentExpr.Arguments.Count; i++) { - ILExpression expr = parentExpr.Arguments[i]; - IntroducePropertyAccessInstructions(expr); - IntroducePropertyAccessInstructions(expr, parentExpr, i); - } - } else { - foreach (ILNode child in node.GetChildren()) { - IntroducePropertyAccessInstructions(child); - ILExpression expr = child as ILExpression; - if (expr != null) { - IntroducePropertyAccessInstructions(expr, null, -1); - } - } - } - } - - void IntroducePropertyAccessInstructions(ILExpression expr, ILExpression parentExpr, int posInParent) - { - if (expr.Code == ILCode.Call || expr.Code == ILCode.Callvirt) { - MethodReference cecilMethod = (MethodReference)expr.Operand; - if (cecilMethod.DeclaringType is ArrayType) { - switch (cecilMethod.Name) { - case "Get": - expr.Code = ILCode.CallGetter; - break; - case "Set": - expr.Code = ILCode.CallSetter; - break; - case "Address": - ByReferenceType brt = cecilMethod.ReturnType as ByReferenceType; - if (brt != null) { - MethodReference getMethod = new MethodReference("Get", brt.ElementType, cecilMethod.DeclaringType); - foreach (var p in cecilMethod.Parameters) - getMethod.Parameters.Add(p); - getMethod.HasThis = cecilMethod.HasThis; - expr.Operand = getMethod; - } - expr.Code = ILCode.CallGetter; - if (parentExpr != null) { - parentExpr.Arguments[posInParent] = new ILExpression(ILCode.AddressOf, null, expr); - } - break; - } - } else { - MethodDefinition cecilMethodDef = cecilMethod.Resolve(); - if (cecilMethodDef != null) { - if (cecilMethodDef.IsGetter) - expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallGetter : ILCode.CallvirtGetter; - else if (cecilMethodDef.IsSetter) - expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallSetter : ILCode.CallvirtSetter; - } - } - } else if (expr.Code == ILCode.Newobj && expr.Arguments.Count == 2) { - // Might be 'newobj(SomeDelegate, target, ldvirtftn(F, target))'. - ILVariable target; - if (expr.Arguments[0].Match(ILCode.Ldloc, out target) - && expr.Arguments[1].Code == ILCode.Ldvirtftn - && expr.Arguments[1].Arguments.Count == 1 - && expr.Arguments[1].Arguments[0].MatchLdloc(target)) - { - // Remove the 'target' argument from the ldvirtftn instruction. - // It's not needed in the translation to C#, and needs to be eliminated so that the target expression - // can be inlined. - expr.Arguments[1].Arguments.Clear(); - } - } - } - - /// <summary> - /// Group input into a set of blocks that can be later arbitraliby schufled. - /// The method adds necessary branches to make control flow between blocks - /// explicit and thus order independent. - /// </summary> - void SplitToBasicBlocks(ILBlock block) - { - List<ILNode> basicBlocks = new List<ILNode>(); - - ILLabel entryLabel = block.Body.FirstOrDefault() as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++) }; - ILBasicBlock basicBlock = new ILBasicBlock(); - basicBlocks.Add(basicBlock); - basicBlock.Body.Add(entryLabel); - block.EntryGoto = new ILExpression(ILCode.Br, entryLabel); - - if (block.Body.Count > 0) { - if (block.Body[0] != entryLabel) - basicBlock.Body.Add(block.Body[0]); - - for (int i = 1; i < block.Body.Count; i++) { - ILNode lastNode = block.Body[i - 1]; - ILNode currNode = block.Body[i]; - - // Start a new basic block if necessary - if (currNode is ILLabel || - currNode is ILTryCatchBlock || // Counts as label - lastNode.IsConditionalControlFlow() || - lastNode.IsUnconditionalControlFlow()) - { - // Try to reuse the label - ILLabel label = currNode as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++).ToString() }; - - // Terminate the last block - if (!lastNode.IsUnconditionalControlFlow()) { - // Explicit branch from one block to other - basicBlock.Body.Add(new ILExpression(ILCode.Br, label)); - } - - // Start the new block - basicBlock = new ILBasicBlock(); - basicBlocks.Add(basicBlock); - basicBlock.Body.Add(label); - - // Add the node to the basic block - if (currNode != label) - basicBlock.Body.Add(currNode); - } else { - basicBlock.Body.Add(currNode); - } - } - } - - block.Body = basicBlocks; - return; - } - - void DuplicateReturnStatements(ILBlock method) - { - Dictionary<ILLabel, ILNode> nextSibling = new Dictionary<ILLabel, ILNode>(); - - // Build navigation data - foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - for (int i = 0; i < block.Body.Count - 1; i++) { - ILLabel curr = block.Body[i] as ILLabel; - if (curr != null) { - nextSibling[curr] = block.Body[i + 1]; - } - } - } - - // Duplicate returns - foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - for (int i = 0; i < block.Body.Count; i++) { - ILLabel targetLabel; - if (block.Body[i].Match(ILCode.Br, out targetLabel) || block.Body[i].Match(ILCode.Leave, out targetLabel)) { - // Skip extra labels - while(nextSibling.ContainsKey(targetLabel) && nextSibling[targetLabel] is ILLabel) { - targetLabel = (ILLabel)nextSibling[targetLabel]; - } - - // Inline return statement - ILNode target; - List<ILExpression> retArgs; - if (nextSibling.TryGetValue(targetLabel, out target)) { - if (target.Match(ILCode.Ret, out retArgs)) { - ILVariable locVar; - object constValue; - if (retArgs.Count == 0) { - block.Body[i] = new ILExpression(ILCode.Ret, null); - } else if (retArgs.Single().Match(ILCode.Ldloc, out locVar)) { - block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldloc, locVar)); - } else if (retArgs.Single().Match(ILCode.Ldc_I4, out constValue)) { - block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldc_I4, constValue)); - } - } - } else { - if (method.Body.Count > 0 && method.Body.Last() == targetLabel) { - // It exits the main method - so it is same as return; - block.Body[i] = new ILExpression(ILCode.Ret, null); - } - } - } - } - } - } - - /// <summary> - /// Flattens all nested basic blocks, except the the top level 'node' argument - /// </summary> - void FlattenBasicBlocks(ILNode node) - { - ILBlock block = node as ILBlock; - if (block != null) { - List<ILNode> flatBody = new List<ILNode>(); - foreach (ILNode child in block.GetChildren()) { - FlattenBasicBlocks(child); - ILBasicBlock childAsBB = child as ILBasicBlock; - if (childAsBB != null) { - if (!(childAsBB.Body.FirstOrDefault() is ILLabel)) - throw new Exception("Basic block has to start with a label. \n" + childAsBB.ToString()); - if (childAsBB.Body.LastOrDefault() is ILExpression && !childAsBB.Body.LastOrDefault().IsUnconditionalControlFlow()) - throw new Exception("Basci block has to end with unconditional control flow. \n" + childAsBB.ToString()); - flatBody.AddRange(childAsBB.GetChildren()); - } else { - flatBody.Add(child); - } - } - block.EntryGoto = null; - block.Body = flatBody; - } else if (node is ILExpression) { - // Optimization - no need to check expressions - } else if (node != null) { - // Recursively find all ILBlocks - foreach(ILNode child in node.GetChildren()) { - FlattenBasicBlocks(child); - } - } - } - - /// <summary> - /// Replace endfinally with jump to the end of the finally block - /// </summary> - void RemoveEndFinally(ILBlock method) - { - // Go thought the list in reverse so that we do the nested blocks first - foreach(var tryCatch in method.GetSelfAndChildrenRecursive<ILTryCatchBlock>(tc => tc.FinallyBlock != null).Reverse()) { - ILLabel label = new ILLabel() { Name = "EndFinally_" + nextLabelIndex++ }; - tryCatch.FinallyBlock.Body.Add(label); - foreach(var block in tryCatch.FinallyBlock.GetSelfAndChildrenRecursive<ILBlock>()) { - for (int i = 0; i < block.Body.Count; i++) { - if (block.Body[i].Match(ILCode.Endfinally)) { - block.Body[i] = new ILExpression(ILCode.Br, label).WithILRanges(((ILExpression)block.Body[i]).ILRanges); - } - } - } - } - } - - /// <summary> - /// Reduce the nesting of conditions. - /// It should be done on flat data that already had most gotos removed - /// </summary> - void ReduceIfNesting(ILNode node) - { - ILBlock block = node as ILBlock; - if (block != null) { - for (int i = 0; i < block.Body.Count; i++) { - ILCondition cond = block.Body[i] as ILCondition; - if (cond != null) { - bool trueExits = cond.TrueBlock.Body.LastOrDefault().IsUnconditionalControlFlow(); - bool falseExits = cond.FalseBlock.Body.LastOrDefault().IsUnconditionalControlFlow(); - - if (trueExits) { - // Move the false block after the condition - block.Body.InsertRange(i + 1, cond.FalseBlock.GetChildren()); - cond.FalseBlock = new ILBlock(); - } else if (falseExits) { - // Move the true block after the condition - block.Body.InsertRange(i + 1, cond.TrueBlock.GetChildren()); - cond.TrueBlock = new ILBlock(); - } - - // Eliminate empty true block - if (!cond.TrueBlock.GetChildren().Any() && cond.FalseBlock.GetChildren().Any()) { - // Swap bodies - ILBlock tmp = cond.TrueBlock; - cond.TrueBlock = cond.FalseBlock; - cond.FalseBlock = tmp; - cond.Condition = new ILExpression(ILCode.LogicNot, null, cond.Condition); - } - } - } - } - - // We are changing the number of blocks so we use plain old recursion to get all blocks - foreach(ILNode child in node.GetChildren()) { - if (child != null && !(child is ILExpression)) - ReduceIfNesting(child); - } - } - - void RecombineVariables(ILBlock method) - { - // Recombine variables that were split when the ILAst was created - // This ensures that a single IL variable is a single C# variable (gets assigned only one name) - // The DeclareVariables transformation might then split up the C# variable again if it is used indendently in two separate scopes. - Dictionary<VariableDefinition, ILVariable> dict = new Dictionary<VariableDefinition, ILVariable>(); - ReplaceVariables( - method, - delegate(ILVariable v) { - if (v.OriginalVariable == null) - return v; - ILVariable combinedVariable; - if (!dict.TryGetValue(v.OriginalVariable, out combinedVariable)) { - dict.Add(v.OriginalVariable, v); - combinedVariable = v; - } - return combinedVariable; - }); - } - - void HandlePointerArithmetic(ILNode method) - { - foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) { - List<ILExpression> args = expr.Arguments; - switch (expr.Code) { - case ILCode.Localloc: - { - PointerType type = expr.InferredType as PointerType; - if (type != null) { - ILExpression arg0 = args[0]; - ILExpression expr2 = expr; - DivideOrMultiplyBySize(ref expr2, ref arg0, type.ElementType, true); - // expr shouldn't change - if (expr2 != expr) - throw new InvalidOperationException(); - args[0] = arg0; - } - break; - } - case ILCode.Add: - case ILCode.Add_Ovf: - case ILCode.Add_Ovf_Un: - { - ILExpression arg0 = args[0]; - ILExpression arg1 = args[1]; - if (expr.InferredType is PointerType) { - if (arg0.ExpectedType is PointerType) { - DivideOrMultiplyBySize(ref arg0, ref arg1, ((PointerType)expr.InferredType).ElementType, true); - } else if (arg1.ExpectedType is PointerType) - DivideOrMultiplyBySize(ref arg1, ref arg0, ((PointerType)expr.InferredType).ElementType, true); - } - args[0] = arg0; - args[1] = arg1; - break; - } - case ILCode.Sub: - case ILCode.Sub_Ovf: - case ILCode.Sub_Ovf_Un: - { - ILExpression arg0 = args[0]; - ILExpression arg1 = args[1]; - if (expr.InferredType is PointerType) { - if (arg0.ExpectedType is PointerType && !(arg1.InferredType is PointerType)) - DivideOrMultiplyBySize(ref arg0, ref arg1, ((PointerType)expr.InferredType).ElementType, true); - } - args[0] = arg0; - args[1] = arg1; - break; - } - case ILCode.Conv_I8: - { - ILExpression arg0 = args[0]; - // conv.i8(div:intptr(p0 - p1)) - if (arg0.Code == ILCode.Div && arg0.InferredType.FullName == "System.IntPtr") - { - ILExpression dividend = arg0.Arguments[0]; - if (dividend.InferredType.FullName == "System.IntPtr" && - (dividend.Code == ILCode.Sub || dividend.Code == ILCode.Sub_Ovf || dividend.Code == ILCode.Sub_Ovf_Un)) - { - PointerType pointerType0 = dividend.Arguments[0].InferredType as PointerType; - PointerType pointerType1 = dividend.Arguments[1].InferredType as PointerType; - - if (pointerType0 != null && pointerType1 != null) { - if (pointerType0.ElementType.FullName == "System.Void" || - pointerType0.ElementType.FullName != pointerType1.ElementType.FullName) { - pointerType0 = pointerType1 = new PointerType(typeSystem.Byte); - dividend.Arguments[0] = Cast(dividend.Arguments[0], pointerType0); - dividend.Arguments[1] = Cast(dividend.Arguments[1], pointerType1); - } - - DivideOrMultiplyBySize(ref dividend, ref arg0, pointerType0.ElementType, false); - // dividend shouldn't change - if (args[0].Arguments[0] != dividend) - throw new InvalidOperationException(); - } - } - } - args[0] = arg0; - break; - } - } - } - } - - static ILExpression UnwrapIntPtrCast(ILExpression expr) - { - if (expr.Code != ILCode.Conv_I && expr.Code != ILCode.Conv_U) - return expr; - - ILExpression arg = expr.Arguments[0]; - switch (arg.InferredType.MetadataType) { - case MetadataType.Byte: - case MetadataType.SByte: - case MetadataType.UInt16: - case MetadataType.Int16: - case MetadataType.UInt32: - case MetadataType.Int32: - case MetadataType.UInt64: - case MetadataType.Int64: - return arg; - } - - return expr; - } - - static ILExpression Cast(ILExpression expr, TypeReference type) - { - return new ILExpression(ILCode.Castclass, type, expr) - { - InferredType = type, - ExpectedType = type - }; - } - - void DivideOrMultiplyBySize(ref ILExpression pointerExpr, ref ILExpression adjustmentExpr, TypeReference elementType, bool divide) - { - adjustmentExpr = UnwrapIntPtrCast(adjustmentExpr); - - ILExpression sizeOfExpression; - switch (TypeAnalysis.GetInformationAmount(elementType)) { - case 0: // System.Void - pointerExpr = Cast(pointerExpr, new PointerType(typeSystem.Byte)); - goto case 1; - case 1: - case 8: - sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 1); - break; - case 16: - sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 2); - break; - case 32: - sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 4); - break; - case 64: - sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 8); - break; - default: - sizeOfExpression = new ILExpression(ILCode.Sizeof, elementType); - break; - } - - if (divide && (adjustmentExpr.Code == ILCode.Mul || adjustmentExpr.Code == ILCode.Mul_Ovf || adjustmentExpr.Code == ILCode.Mul_Ovf_Un) || - !divide && (adjustmentExpr.Code == ILCode.Div || adjustmentExpr.Code == ILCode.Div_Un)) { - ILExpression mulArg = adjustmentExpr.Arguments[1]; - if (mulArg.Code == sizeOfExpression.Code && sizeOfExpression.Operand.Equals(mulArg.Operand)) { - adjustmentExpr = UnwrapIntPtrCast(adjustmentExpr.Arguments[0]); - return; - } - } - - if (adjustmentExpr.Code == sizeOfExpression.Code) { - if (sizeOfExpression.Operand.Equals(adjustmentExpr.Operand)) { - adjustmentExpr = new ILExpression(ILCode.Ldc_I4, 1); - return; - } - - if (adjustmentExpr.Code == ILCode.Ldc_I4) { - int offsetInBytes = (int)adjustmentExpr.Operand; - int elementSize = (int)sizeOfExpression.Operand; - - if (offsetInBytes % elementSize != 0) { - pointerExpr = Cast(pointerExpr, new PointerType(typeSystem.Byte)); - return; - } - - adjustmentExpr.Operand = offsetInBytes / elementSize; - return; - } - } - - if (!(sizeOfExpression.Code == ILCode.Ldc_I4 && (int)sizeOfExpression.Operand == 1)) - adjustmentExpr = new ILExpression(divide ? ILCode.Div_Un : ILCode.Mul, null, adjustmentExpr, sizeOfExpression); - } - - public static void ReplaceVariables(ILNode node, Func<ILVariable, ILVariable> variableMapping) - { - ILExpression expr = node as ILExpression; - if (expr != null) { - ILVariable v = expr.Operand as ILVariable; - if (v != null) - expr.Operand = variableMapping(v); - foreach (ILExpression child in expr.Arguments) - ReplaceVariables(child, variableMapping); - } else { - var catchBlock = node as ILTryCatchBlock.CatchBlock; - if (catchBlock != null && catchBlock.ExceptionVariable != null) { - catchBlock.ExceptionVariable = variableMapping(catchBlock.ExceptionVariable); - } - - foreach (ILNode child in node.GetChildren()) - ReplaceVariables(child, variableMapping); - } - } - - void ReportUnassignedILRanges(ILBlock method) - { - var unassigned = ILRange.Invert(method.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.ILRanges), context.CurrentMethod.Body.CodeSize).ToList(); - if (unassigned.Count > 0) - Debug.WriteLine(string.Format("Unassigned ILRanges for {0}.{1}: {2}", this.context.CurrentMethod.DeclaringType.Name, this.context.CurrentMethod.Name, string.Join(", ", unassigned.Select(r => r.ToString())))); - } - } - - public static class ILAstOptimizerExtensionMethods - { - /// <summary> - /// Perform one pass of a given optimization on this block. - /// This block must consist of only basicblocks. - /// </summary> - public static bool RunOptimization(this ILBlock block, Func<List<ILNode>, ILBasicBlock, int, bool> optimization) - { - bool modified = false; - List<ILNode> body = block.Body; - for (int i = body.Count - 1; i >= 0; i--) { - if (i < body.Count && optimization(body, (ILBasicBlock)body[i], i)) { - modified = true; - } - } - return modified; - } - - public static bool RunOptimization(this ILBlock block, Func<List<ILNode>, ILExpression, int, bool> optimization) - { - bool modified = false; - foreach (ILBasicBlock bb in block.Body) { - for (int i = bb.Body.Count - 1; i >= 0; i--) { - ILExpression expr = bb.Body.ElementAtOrDefault(i) as ILExpression; - if (expr != null && optimization(bb.Body, expr, i)) { - modified = true; - } - } - } - return modified; - } - - public static bool IsConditionalControlFlow(this ILNode node) - { - ILExpression expr = node as ILExpression; - return expr != null && expr.Code.IsConditionalControlFlow(); - } - - public static bool IsUnconditionalControlFlow(this ILNode node) - { - ILExpression expr = node as ILExpression; - return expr != null && expr.Code.IsUnconditionalControlFlow(); - } - - /// <summary> - /// The expression has no effect on the program and can be removed - /// if its return value is not needed. - /// </summary> - public static bool HasNoSideEffects(this ILExpression expr) - { - // Remember that if expression can throw an exception, it is a side effect - - switch(expr.Code) { - case ILCode.Ldloc: - case ILCode.Ldloca: - case ILCode.Ldstr: - case ILCode.Ldnull: - case ILCode.Ldc_I4: - case ILCode.Ldc_I8: - case ILCode.Ldc_R4: - case ILCode.Ldc_R8: - case ILCode.Ldc_Decimal: - return true; - default: - return false; - } - } - - public static bool IsStoreToArray(this ILCode code) - { - switch (code) { - case ILCode.Stelem_Any: - case ILCode.Stelem_I: - case ILCode.Stelem_I1: - case ILCode.Stelem_I2: - case ILCode.Stelem_I4: - case ILCode.Stelem_I8: - case ILCode.Stelem_R4: - case ILCode.Stelem_R8: - case ILCode.Stelem_Ref: - return true; - default: - return false; - } - } - - public static bool IsLoadFromArray(this ILCode code) - { - switch (code) { - case ILCode.Ldelem_Any: - case ILCode.Ldelem_I: - case ILCode.Ldelem_I1: - case ILCode.Ldelem_I2: - case ILCode.Ldelem_I4: - case ILCode.Ldelem_I8: - case ILCode.Ldelem_U1: - case ILCode.Ldelem_U2: - case ILCode.Ldelem_U4: - case ILCode.Ldelem_R4: - case ILCode.Ldelem_R8: - case ILCode.Ldelem_Ref: - return true; - default: - return false; - } - } - - /// <summary> - /// Can the expression be used as a statement in C#? - /// </summary> - public static bool CanBeExpressionStatement(this ILExpression expr) - { - switch(expr.Code) { - case ILCode.Call: - case ILCode.Callvirt: - // property getters can't be expression statements, but all other method calls can be - MethodReference mr = (MethodReference)expr.Operand; - return !mr.Name.StartsWith("get_", StringComparison.Ordinal); - case ILCode.CallSetter: - case ILCode.CallvirtSetter: - case ILCode.Newobj: - case ILCode.Newarr: - case ILCode.Stloc: - case ILCode.Stobj: - case ILCode.Stsfld: - case ILCode.Stfld: - case ILCode.Stind_Ref: - case ILCode.Stelem_Any: - case ILCode.Stelem_I: - case ILCode.Stelem_I1: - case ILCode.Stelem_I2: - case ILCode.Stelem_I4: - case ILCode.Stelem_I8: - case ILCode.Stelem_R4: - case ILCode.Stelem_R8: - case ILCode.Stelem_Ref: - return true; - default: - return false; - } - } - - public static ILExpression WithILRanges(this ILExpression expr, IEnumerable<ILRange> ilranges) - { - expr.ILRanges.AddRange(ilranges); - return expr; - } - - public static void RemoveTail(this List<ILNode> body, params ILCode[] codes) - { - for (int i = 0; i < codes.Length; i++) { - if (((ILExpression)body[body.Count - codes.Length + i]).Code != codes[i]) - throw new Exception("Tailing code does not match expected."); - } - body.RemoveRange(body.Count - codes.Length, codes.Length); - } - - public static V GetOrDefault<K,V>(this Dictionary<K, V> dict, K key) - { - V ret; - dict.TryGetValue(key, out ret); - return ret; - } - - public static void RemoveOrThrow<T>(this ICollection<T> collection, T item) - { - if (!collection.Remove(item)) - throw new Exception("The item was not found in the collection"); - } - - public static void RemoveOrThrow<K,V>(this Dictionary<K,V> collection, K key) - { - if (!collection.Remove(key)) - throw new Exception("The key was not found in the dictionary"); - } - - public static bool ContainsReferenceTo(this ILExpression expr, ILVariable v) - { - if (expr.Operand == v) - return true; - foreach (var arg in expr.Arguments) { - if (ContainsReferenceTo(arg, v)) - return true; - } - return false; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs deleted file mode 100644 index 179b40a673..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ /dev/null @@ -1,600 +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.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using ICSharpCode.Decompiler; -using ICSharpCode.Decompiler.Disassembler; -using ICSharpCode.NRefactory.Utils; -using Mono.Cecil; -using Mono.Cecil.Cil; -using Cecil = Mono.Cecil; - -namespace ICSharpCode.Decompiler.ILAst -{ - public abstract class ILNode - { - public IEnumerable<T> GetSelfAndChildrenRecursive<T>(Func<T, bool> predicate = null) where T: ILNode - { - List<T> result = new List<T>(16); - AccumulateSelfAndChildrenRecursive(result, predicate); - return result; - } - - void AccumulateSelfAndChildrenRecursive<T>(List<T> list, Func<T, bool> predicate) where T:ILNode - { - // Note: RemoveEndFinally depends on self coming before children - T thisAsT = this as T; - if (thisAsT != null && (predicate == null || predicate(thisAsT))) - list.Add(thisAsT); - foreach (ILNode node in this.GetChildren()) { - if (node != null) - node.AccumulateSelfAndChildrenRecursive(list, predicate); - } - } - - public virtual IEnumerable<ILNode> GetChildren() - { - yield break; - } - - public override string ToString() - { - StringWriter w = new StringWriter(); - WriteTo(new PlainTextOutput(w)); - return w.ToString().Replace("\r\n", "; "); - } - - public abstract void WriteTo(ITextOutput output); - } - - public class ILBlock: ILNode - { - public ILExpression EntryGoto; - - public List<ILNode> Body; - - public ILBlock(params ILNode[] body) - { - this.Body = new List<ILNode>(body); - } - - public ILBlock(List<ILNode> body) - { - this.Body = body; - } - - public override IEnumerable<ILNode> GetChildren() - { - if (this.EntryGoto != null) - yield return this.EntryGoto; - foreach(ILNode child in this.Body) { - yield return child; - } - } - - public override void WriteTo(ITextOutput output) - { - foreach(ILNode child in this.GetChildren()) { - child.WriteTo(output); - output.WriteLine(); - } - } - } - - public class ILBasicBlock: ILNode - { - /// <remarks> Body has to start with a label and end with unconditional control flow </remarks> - public List<ILNode> Body = new List<ILNode>(); - - public override IEnumerable<ILNode> GetChildren() - { - return this.Body; - } - - public override void WriteTo(ITextOutput output) - { - foreach(ILNode child in this.GetChildren()) { - child.WriteTo(output); - output.WriteLine(); - } - } - } - - public class ILLabel: ILNode - { - public string Name; - - public override void WriteTo(ITextOutput output) - { - output.WriteDefinition(Name + ":", this); - } - } - - public class ILTryCatchBlock: ILNode - { - public class CatchBlock: ILBlock - { - public TypeReference ExceptionType; - public ILVariable ExceptionVariable; - - public override void WriteTo(ITextOutput output) - { - output.Write("catch "); - output.WriteReference(ExceptionType.FullName, ExceptionType); - if (ExceptionVariable != null) { - output.Write(' '); - output.Write(ExceptionVariable.Name); - } - output.WriteLine(" {"); - output.Indent(); - base.WriteTo(output); - output.Unindent(); - output.WriteLine("}"); - } - } - - public ILBlock TryBlock; - public List<CatchBlock> CatchBlocks; - public ILBlock FinallyBlock; - public ILBlock FaultBlock; - - public override IEnumerable<ILNode> GetChildren() - { - if (this.TryBlock != null) - yield return this.TryBlock; - foreach (var catchBlock in this.CatchBlocks) { - yield return catchBlock; - } - if (this.FaultBlock != null) - yield return this.FaultBlock; - if (this.FinallyBlock != null) - yield return this.FinallyBlock; - } - - public override void WriteTo(ITextOutput output) - { - output.WriteLine(".try {"); - output.Indent(); - TryBlock.WriteTo(output); - output.Unindent(); - output.WriteLine("}"); - foreach (CatchBlock block in CatchBlocks) { - block.WriteTo(output); - } - if (FaultBlock != null) { - output.WriteLine("fault {"); - output.Indent(); - FaultBlock.WriteTo(output); - output.Unindent(); - output.WriteLine("}"); - } - if (FinallyBlock != null) { - output.WriteLine("finally {"); - output.Indent(); - FinallyBlock.WriteTo(output); - output.Unindent(); - output.WriteLine("}"); - } - } - } - - public class ILVariable - { - public string Name; - public bool IsGenerated; - public TypeReference Type; - public VariableDefinition OriginalVariable; - public ParameterDefinition OriginalParameter; - - public bool IsPinned { - get { return OriginalVariable != null && OriginalVariable.IsPinned; } - } - - public bool IsParameter { - get { return OriginalParameter != null; } - } - - public override string ToString() - { - return Name; - } - } - - public struct ILRange - { - public readonly int From; - public readonly int To; // Exlusive - - public ILRange(int @from, int to) - { - this.From = @from; - this.To = to; - } - - public override string ToString() - { - return string.Format("{0:X2}-{1:X2}", From, To); - } - - public static List<ILRange> OrderAndJoin(IEnumerable<ILRange> input) - { - if (input == null) - throw new ArgumentNullException("Input is null!"); - - List<ILRange> result = new List<ILRange>(); - foreach(ILRange curr in input.OrderBy(r => r.From)) { - if (result.Count > 0) { - // Merge consequtive ranges if possible - ILRange last = result[result.Count - 1]; - if (curr.From <= last.To) { - result[result.Count - 1] = new ILRange(last.From, Math.Max(last.To, curr.To)); - continue; - } - } - result.Add(curr); - } - return result; - } - - public static List<ILRange> Invert(IEnumerable<ILRange> input, int codeSize) - { - if (input == null) - throw new ArgumentNullException("Input is null!"); - - if (codeSize <= 0) - throw new ArgumentException("Code size must be grater than 0"); - - List<ILRange> ordered = OrderAndJoin(input); - List<ILRange> result = new List<ILRange>(ordered.Count + 1); - if (ordered.Count == 0) { - result.Add(new ILRange(0, codeSize)); - } else { - // Gap before the first element - if (ordered.First().From != 0) - result.Add(new ILRange(0, ordered.First().From)); - - // Gaps between elements - for (int i = 0; i < ordered.Count - 1; i++) - result.Add(new ILRange(ordered[i].To, ordered[i + 1].From)); - - // Gap after the last element - Debug.Assert(ordered.Last().To <= codeSize); - if (ordered.Last().To != codeSize) - result.Add(new ILRange(ordered.Last().To, codeSize)); - } - return result; - } - } - - public class ILExpressionPrefix - { - public readonly ILCode Code; - public readonly object Operand; - - public ILExpressionPrefix(ILCode code, object operand = null) - { - this.Code = code; - this.Operand = operand; - } - } - - public class ILExpression : ILNode - { - public ILCode Code { get; set; } - public object Operand { get; set; } - public List<ILExpression> Arguments { get; set; } - public ILExpressionPrefix[] Prefixes { get; set; } - // Mapping to the original instructions (useful for debugging) - public List<ILRange> ILRanges { get; set; } - - public TypeReference ExpectedType { get; set; } - public TypeReference InferredType { get; set; } - - public static readonly object AnyOperand = new object(); - - public ILExpression(ILCode code, object operand, List<ILExpression> args) - { - if (operand is ILExpression) - throw new ArgumentException("operand"); - - this.Code = code; - this.Operand = operand; - this.Arguments = new List<ILExpression>(args); - this.ILRanges = new List<ILRange>(1); - } - - public ILExpression(ILCode code, object operand, params ILExpression[] args) - { - if (operand is ILExpression) - throw new ArgumentException("operand"); - - this.Code = code; - this.Operand = operand; - this.Arguments = new List<ILExpression>(args); - this.ILRanges = new List<ILRange>(1); - } - - public void AddPrefix(ILExpressionPrefix prefix) - { - ILExpressionPrefix[] arr = this.Prefixes; - if (arr == null) - arr = new ILExpressionPrefix[1]; - else - Array.Resize(ref arr, arr.Length + 1); - arr[arr.Length - 1] = prefix; - this.Prefixes = arr; - } - - public ILExpressionPrefix GetPrefix(ILCode code) - { - var prefixes = this.Prefixes; - if (prefixes != null) { - foreach (ILExpressionPrefix p in prefixes) { - if (p.Code == code) - return p; - } - } - return null; - } - - public override IEnumerable<ILNode> GetChildren() - { - return Arguments; - } - - public bool IsBranch() - { - return this.Operand is ILLabel || this.Operand is ILLabel[]; - } - - public IEnumerable<ILLabel> GetBranchTargets() - { - if (this.Operand is ILLabel) { - return new ILLabel[] { (ILLabel)this.Operand }; - } else if (this.Operand is ILLabel[]) { - return (ILLabel[])this.Operand; - } else { - return new ILLabel[] { }; - } - } - - public override void WriteTo(ITextOutput output) - { - if (Operand is ILVariable && ((ILVariable)Operand).IsGenerated) { - if (Code == ILCode.Stloc && this.InferredType == null) { - output.Write(((ILVariable)Operand).Name); - output.Write(" = "); - Arguments.First().WriteTo(output); - return; - } else if (Code == ILCode.Ldloc) { - output.Write(((ILVariable)Operand).Name); - if (this.InferredType != null) { - output.Write(':'); - this.InferredType.WriteTo(output, ILNameSyntax.ShortTypeName); - if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) { - output.Write("[exp:"); - this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName); - output.Write(']'); - } - } - return; - } - } - - if (this.Prefixes != null) { - foreach (var prefix in this.Prefixes) { - output.Write(prefix.Code.GetName()); - output.Write(". "); - } - } - - output.Write(Code.GetName()); - if (this.InferredType != null) { - output.Write(':'); - this.InferredType.WriteTo(output, ILNameSyntax.ShortTypeName); - if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) { - output.Write("[exp:"); - this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName); - output.Write(']'); - } - } else if (this.ExpectedType != null) { - output.Write("[exp:"); - this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName); - output.Write(']'); - } - output.Write('('); - bool first = true; - if (Operand != null) { - if (Operand is ILLabel) { - output.WriteReference(((ILLabel)Operand).Name, Operand); - } else if (Operand is ILLabel[]) { - ILLabel[] labels = (ILLabel[])Operand; - for (int i = 0; i < labels.Length; i++) { - if (i > 0) - output.Write(", "); - output.WriteReference(labels[i].Name, labels[i]); - } - } else if (Operand is MethodReference) { - MethodReference method = (MethodReference)Operand; - if (method.DeclaringType != null) { - method.DeclaringType.WriteTo(output, ILNameSyntax.ShortTypeName); - output.Write("::"); - } - output.WriteReference(method.Name, method); - } else if (Operand is FieldReference) { - FieldReference field = (FieldReference)Operand; - field.DeclaringType.WriteTo(output, ILNameSyntax.ShortTypeName); - output.Write("::"); - output.WriteReference(field.Name, field); - } else { - DisassemblerHelpers.WriteOperand(output, Operand); - } - first = false; - } - foreach (ILExpression arg in this.Arguments) { - if (!first) output.Write(", "); - arg.WriteTo(output); - first = false; - } - output.Write(')'); - } - } - - public class ILWhileLoop : ILNode - { - public ILExpression Condition; - public ILBlock BodyBlock; - - public override IEnumerable<ILNode> GetChildren() - { - if (this.Condition != null) - yield return this.Condition; - if (this.BodyBlock != null) - yield return this.BodyBlock; - } - - public override void WriteTo(ITextOutput output) - { - output.WriteLine(""); - output.Write("loop ("); - if (this.Condition != null) - this.Condition.WriteTo(output); - output.WriteLine(") {"); - output.Indent(); - this.BodyBlock.WriteTo(output); - output.Unindent(); - output.WriteLine("}"); - } - } - - public class ILCondition : ILNode - { - public ILExpression Condition; - public ILBlock TrueBlock; // Branch was taken - public ILBlock FalseBlock; // Fall-though - - public override IEnumerable<ILNode> GetChildren() - { - if (this.Condition != null) - yield return this.Condition; - if (this.TrueBlock != null) - yield return this.TrueBlock; - if (this.FalseBlock != null) - yield return this.FalseBlock; - } - - public override void WriteTo(ITextOutput output) - { - output.Write("if ("); - Condition.WriteTo(output); - output.WriteLine(") {"); - output.Indent(); - TrueBlock.WriteTo(output); - output.Unindent(); - output.Write("}"); - if (FalseBlock != null) { - output.WriteLine(" else {"); - output.Indent(); - FalseBlock.WriteTo(output); - output.Unindent(); - output.WriteLine("}"); - } - } - } - - public class ILSwitch: ILNode - { - public class CaseBlock: ILBlock - { - public List<int> Values; // null for the default case - - public override void WriteTo(ITextOutput output) - { - if (this.Values != null) { - foreach (int i in this.Values) { - output.WriteLine("case {0}:", i); - } - } else { - output.WriteLine("default:"); - } - output.Indent(); - base.WriteTo(output); - output.Unindent(); - } - } - - public ILExpression Condition; - public List<CaseBlock> CaseBlocks = new List<CaseBlock>(); - - public override IEnumerable<ILNode> GetChildren() - { - if (this.Condition != null) - yield return this.Condition; - foreach (ILBlock caseBlock in this.CaseBlocks) { - yield return caseBlock; - } - } - - public override void WriteTo(ITextOutput output) - { - output.Write("switch ("); - Condition.WriteTo(output); - output.WriteLine(") {"); - output.Indent(); - foreach (CaseBlock caseBlock in this.CaseBlocks) { - caseBlock.WriteTo(output); - } - output.Unindent(); - output.WriteLine("}"); - } - } - - public class ILFixedStatement : ILNode - { - public List<ILExpression> Initializers = new List<ILExpression>(); - public ILBlock BodyBlock; - - public override IEnumerable<ILNode> GetChildren() - { - foreach (ILExpression initializer in this.Initializers) - yield return initializer; - if (this.BodyBlock != null) - yield return this.BodyBlock; - } - - public override void WriteTo(ITextOutput output) - { - output.Write("fixed ("); - for (int i = 0; i < this.Initializers.Count; i++) { - if (i > 0) - output.Write(", "); - this.Initializers[i].WriteTo(output); - } - output.WriteLine(") {"); - output.Indent(); - this.BodyBlock.WriteTo(output); - output.Unindent(); - output.WriteLine("}"); - } - } -}
\ No newline at end of file diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/ILCodes.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/ILCodes.cs deleted file mode 100644 index 5ca063b752..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/ILCodes.cs +++ /dev/null @@ -1,490 +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 Mono.Cecil; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.ILAst -{ - public enum ILCode - { - // For convenience, the start is exactly identical to Mono.Cecil.Cil.Code - // Instructions that should not be used are prepended by __ - Nop, - Break, - __Ldarg_0, - __Ldarg_1, - __Ldarg_2, - __Ldarg_3, - __Ldloc_0, - __Ldloc_1, - __Ldloc_2, - __Ldloc_3, - __Stloc_0, - __Stloc_1, - __Stloc_2, - __Stloc_3, - __Ldarg_S, - __Ldarga_S, - __Starg_S, - __Ldloc_S, - __Ldloca_S, - __Stloc_S, - Ldnull, - __Ldc_I4_M1, - __Ldc_I4_0, - __Ldc_I4_1, - __Ldc_I4_2, - __Ldc_I4_3, - __Ldc_I4_4, - __Ldc_I4_5, - __Ldc_I4_6, - __Ldc_I4_7, - __Ldc_I4_8, - __Ldc_I4_S, - Ldc_I4, - Ldc_I8, - Ldc_R4, - Ldc_R8, - Dup, - Pop, - Jmp, - Call, - Calli, - Ret, - __Br_S, - __Brfalse_S, - __Brtrue_S, - __Beq_S, - __Bge_S, - __Bgt_S, - __Ble_S, - __Blt_S, - __Bne_Un_S, - __Bge_Un_S, - __Bgt_Un_S, - __Ble_Un_S, - __Blt_Un_S, - Br, - __Brfalse, - Brtrue, - __Beq, - __Bge, - __Bgt, - __Ble, - __Blt, - __Bne_Un, - __Bge_Un, - __Bgt_Un, - __Ble_Un, - __Blt_Un, - Switch, - __Ldind_I1, - __Ldind_U1, - __Ldind_I2, - __Ldind_U2, - __Ldind_I4, - __Ldind_U4, - __Ldind_I8, - __Ldind_I, - __Ldind_R4, - __Ldind_R8, - Ldind_Ref, - Stind_Ref, - __Stind_I1, - __Stind_I2, - __Stind_I4, - __Stind_I8, - __Stind_R4, - __Stind_R8, - Add, - Sub, - Mul, - Div, - Div_Un, - Rem, - Rem_Un, - And, - Or, - Xor, - Shl, - Shr, - Shr_Un, - Neg, - Not, - Conv_I1, - Conv_I2, - Conv_I4, - Conv_I8, - Conv_R4, - Conv_R8, - Conv_U4, - Conv_U8, - Callvirt, - Cpobj, - Ldobj, - Ldstr, - Newobj, - Castclass, - Isinst, - Conv_R_Un, - Unbox, - Throw, - Ldfld, - Ldflda, - Stfld, - Ldsfld, - Ldsflda, - Stsfld, - Stobj, - Conv_Ovf_I1_Un, - Conv_Ovf_I2_Un, - Conv_Ovf_I4_Un, - Conv_Ovf_I8_Un, - Conv_Ovf_U1_Un, - Conv_Ovf_U2_Un, - Conv_Ovf_U4_Un, - Conv_Ovf_U8_Un, - Conv_Ovf_I_Un, - Conv_Ovf_U_Un, - Box, - Newarr, - Ldlen, - Ldelema, - Ldelem_I1, - Ldelem_U1, - Ldelem_I2, - Ldelem_U2, - Ldelem_I4, - Ldelem_U4, - Ldelem_I8, - Ldelem_I, - Ldelem_R4, - Ldelem_R8, - Ldelem_Ref, - Stelem_I, - Stelem_I1, - Stelem_I2, - Stelem_I4, - Stelem_I8, - Stelem_R4, - Stelem_R8, - Stelem_Ref, - Ldelem_Any, - Stelem_Any, - Unbox_Any, - Conv_Ovf_I1, - Conv_Ovf_U1, - Conv_Ovf_I2, - Conv_Ovf_U2, - Conv_Ovf_I4, - Conv_Ovf_U4, - Conv_Ovf_I8, - Conv_Ovf_U8, - Refanyval, - Ckfinite, - Mkrefany, - Ldtoken, - Conv_U2, - Conv_U1, - Conv_I, - Conv_Ovf_I, - Conv_Ovf_U, - Add_Ovf, - Add_Ovf_Un, - Mul_Ovf, - Mul_Ovf_Un, - Sub_Ovf, - Sub_Ovf_Un, - Endfinally, - Leave, - __Leave_S, - __Stind_I, - Conv_U, - Arglist, - Ceq, - Cgt, - Cgt_Un, - Clt, - Clt_Un, - Ldftn, - Ldvirtftn, - __Ldarg, - __Ldarga, - __Starg, - Ldloc, - Ldloca, - Stloc, - Localloc, - Endfilter, - Unaligned, - Volatile, - Tail, - Initobj, - Constrained, - Cpblk, - Initblk, - No, - Rethrow, - Sizeof, - Refanytype, - Readonly, - - // Virtual codes - defined for convenience - Cne, - Cge, - Cge_Un, - Cle, - Cle_Un, - Ldexception, // Operand holds the CatchType for catch handler, null for filter - LogicNot, - LogicAnd, - LogicOr, - NullCoalescing, - InitArray, // Array Initializer - - /// <summary> - /// Defines a barrier between the parent expression and the argument expression that prevents combining them - /// </summary> - Wrap, - - // new Class { Prop = 1, Collection = { { 2, 3 }, {4, 5} }} - // is represented as: - // InitObject(newobj Class, - // CallSetter(Prop, InitializedObject, 1), - // InitCollection(CallGetter(Collection, InitializedObject))), - // Call(Add, InitializedObject, 2, 3), - // Call(Add, InitializedObject, 4, 5))) - InitObject, // Object initializer: first arg is newobj/defaultvalue, remaining args are the initializing statements - InitCollection, // Collection initializer: first arg is newobj/defaultvalue, remaining args are the initializing statements - InitializedObject, // Refers the the object being initialized (refers to first arg in parent InitObject or InitCollection instruction) - - TernaryOp, // ?: - LoopOrSwitchBreak, - LoopContinue, - Ldc_Decimal, - YieldBreak, - YieldReturn, - /// <summary> - /// Represents the 'default(T)' instruction. - /// </summary> - /// <remarks>Introduced by SimplifyLdObjAndStObj step</remarks> - DefaultValue, - /// <summary> - /// ILExpression with a single child: binary operator. - /// This expression means that the binary operator will also assign the new value to its left-hand side. - /// 'CompoundAssignment' must not be used for local variables, as inlining (and other) optimizations don't know that it modifies the variable. - /// </summary> - /// <remarks>Introduced by MakeCompoundAssignments step</remarks> - CompoundAssignment, - /// <summary> - /// Represents the post-increment operator. - /// The first argument is the address of the variable to increment (ldloca instruction). - /// The second arugment is the amount the variable is incremented by (ldc.i4 instruction) - /// </summary> - /// <remarks>Introduced by IntroducePostIncrement step</remarks> - PostIncrement, - PostIncrement_Ovf, // checked variant of PostIncrement - PostIncrement_Ovf_Un, // checked variant of PostIncrement, for unsigned integers - /// <summary>Calls the getter of a static property (or indexer), or of an instance property on 'base'</summary> - CallGetter, - /// <summary>Calls the getter of an instance property (or indexer)</summary> - CallvirtGetter, - /// <summary>Calls the setter of a static property (or indexer), or of an instance property on 'base'</summary> - /// <remarks>This allows us to represent "while ((SomeProperty = val) != null) {}"</remarks> - CallSetter, - /// <summary>Calls the setter of a instance property (or indexer)</summary> - CallvirtSetter, - /// <summary>Simulates getting the address of the argument instruction.</summary> - /// <remarks> - /// Used for postincrement for properties, and to represent the Address() method on multi-dimensional arrays. - /// Also used when inlining a method call on a value type: "stloc(v, ...); call(M, ldloca(v));" becomes "call(M, AddressOf(...))" - /// </remarks> - AddressOf, - /// <summary>Simulates getting the value of a lifted operator's nullable argument</summary> - /// <remarks> - /// For example "stloc(v1, ...); stloc(v2, ...); logicand(ceq(call(Nullable`1::GetValueOrDefault, ldloca(v1)), ldloc(v2)), callgetter(Nullable`1::get_HasValue, ldloca(v1)))" becomes "wrap(ceq(ValueOf(...), ...))" - /// </remarks> - ValueOf, - /// <summary>Simulates creating a new nullable value from a value type argument</summary> - /// <remarks> - /// For example "stloc(v1, ...); stloc(v2, ...); ternaryop(callgetter(Nullable`1::get_HasValue, ldloca(v1)), newobj(Nullable`1::.ctor, add(call(Nullable`1::GetValueOrDefault, ldloca(v1)), ldloc(v2))), defaultvalue(Nullable`1))" - /// becomes "NullableOf(add(valueof(...), ...))" - /// </remarks> - NullableOf, - /// <summary> - /// Declares parameters that are used in an expression tree. - /// The last child of this node is the call constructing the expression tree, all other children are the - /// assignments to the ParameterExpression variables. - /// </summary> - ExpressionTreeParameterDeclarations, - /// <summary> - /// C# 5 await - /// </summary> - Await - } - - public static class ILCodeUtil - { - public static string GetName(this ILCode code) - { - return code.ToString().ToLowerInvariant().TrimStart('_').Replace('_','.'); - } - - public static bool IsConditionalControlFlow(this ILCode code) - { - switch(code) { - case ILCode.__Brfalse_S: - case ILCode.__Brtrue_S: - case ILCode.__Beq_S: - case ILCode.__Bge_S: - case ILCode.__Bgt_S: - case ILCode.__Ble_S: - case ILCode.__Blt_S: - case ILCode.__Bne_Un_S: - case ILCode.__Bge_Un_S: - case ILCode.__Bgt_Un_S: - case ILCode.__Ble_Un_S: - case ILCode.__Blt_Un_S: - case ILCode.__Brfalse: - case ILCode.Brtrue: - case ILCode.__Beq: - case ILCode.__Bge: - case ILCode.__Bgt: - case ILCode.__Ble: - case ILCode.__Blt: - case ILCode.__Bne_Un: - case ILCode.__Bge_Un: - case ILCode.__Bgt_Un: - case ILCode.__Ble_Un: - case ILCode.__Blt_Un: - case ILCode.Switch: - return true; - default: - return false; - } - } - - public static bool IsUnconditionalControlFlow(this ILCode code) - { - switch(code) { - case ILCode.Br: - case ILCode.__Br_S: - case ILCode.Leave: - case ILCode.__Leave_S: - case ILCode.Ret: - case ILCode.Endfilter: - case ILCode.Endfinally: - case ILCode.Throw: - case ILCode.Rethrow: - case ILCode.LoopContinue: - case ILCode.LoopOrSwitchBreak: - case ILCode.YieldBreak: - return true; - default: - return false; - } - } - - public static void ExpandMacro(ref ILCode code, ref object operand, MethodBody methodBody) - { - switch (code) { - case ILCode.__Ldarg_0: code = ILCode.__Ldarg; operand = methodBody.GetParameter(0); break; - case ILCode.__Ldarg_1: code = ILCode.__Ldarg; operand = methodBody.GetParameter(1); break; - case ILCode.__Ldarg_2: code = ILCode.__Ldarg; operand = methodBody.GetParameter(2); break; - case ILCode.__Ldarg_3: code = ILCode.__Ldarg; operand = methodBody.GetParameter(3); break; - case ILCode.__Ldloc_0: code = ILCode.Ldloc; operand = methodBody.Variables[0]; break; - case ILCode.__Ldloc_1: code = ILCode.Ldloc; operand = methodBody.Variables[1]; break; - case ILCode.__Ldloc_2: code = ILCode.Ldloc; operand = methodBody.Variables[2]; break; - case ILCode.__Ldloc_3: code = ILCode.Ldloc; operand = methodBody.Variables[3]; break; - case ILCode.__Stloc_0: code = ILCode.Stloc; operand = methodBody.Variables[0]; break; - case ILCode.__Stloc_1: code = ILCode.Stloc; operand = methodBody.Variables[1]; break; - case ILCode.__Stloc_2: code = ILCode.Stloc; operand = methodBody.Variables[2]; break; - case ILCode.__Stloc_3: code = ILCode.Stloc; operand = methodBody.Variables[3]; break; - case ILCode.__Ldarg_S: code = ILCode.__Ldarg; break; - case ILCode.__Ldarga_S: code = ILCode.__Ldarga; break; - case ILCode.__Starg_S: code = ILCode.__Starg; break; - case ILCode.__Ldloc_S: code = ILCode.Ldloc; break; - case ILCode.__Ldloca_S: code = ILCode.Ldloca; break; - case ILCode.__Stloc_S: code = ILCode.Stloc; break; - case ILCode.__Ldc_I4_M1: code = ILCode.Ldc_I4; operand = -1; break; - case ILCode.__Ldc_I4_0: code = ILCode.Ldc_I4; operand = 0; break; - case ILCode.__Ldc_I4_1: code = ILCode.Ldc_I4; operand = 1; break; - case ILCode.__Ldc_I4_2: code = ILCode.Ldc_I4; operand = 2; break; - case ILCode.__Ldc_I4_3: code = ILCode.Ldc_I4; operand = 3; break; - case ILCode.__Ldc_I4_4: code = ILCode.Ldc_I4; operand = 4; break; - case ILCode.__Ldc_I4_5: code = ILCode.Ldc_I4; operand = 5; break; - case ILCode.__Ldc_I4_6: code = ILCode.Ldc_I4; operand = 6; break; - case ILCode.__Ldc_I4_7: code = ILCode.Ldc_I4; operand = 7; break; - case ILCode.__Ldc_I4_8: code = ILCode.Ldc_I4; operand = 8; break; - case ILCode.__Ldc_I4_S: code = ILCode.Ldc_I4; operand = (int) (sbyte) operand; break; - case ILCode.__Br_S: code = ILCode.Br; break; - case ILCode.__Brfalse_S: code = ILCode.__Brfalse; break; - case ILCode.__Brtrue_S: code = ILCode.Brtrue; break; - case ILCode.__Beq_S: code = ILCode.__Beq; break; - case ILCode.__Bge_S: code = ILCode.__Bge; break; - case ILCode.__Bgt_S: code = ILCode.__Bgt; break; - case ILCode.__Ble_S: code = ILCode.__Ble; break; - case ILCode.__Blt_S: code = ILCode.__Blt; break; - case ILCode.__Bne_Un_S: code = ILCode.__Bne_Un; break; - case ILCode.__Bge_Un_S: code = ILCode.__Bge_Un; break; - case ILCode.__Bgt_Un_S: code = ILCode.__Bgt_Un; break; - case ILCode.__Ble_Un_S: code = ILCode.__Ble_Un; break; - case ILCode.__Blt_Un_S: code = ILCode.__Blt_Un; break; - case ILCode.__Leave_S: code = ILCode.Leave; break; - case ILCode.__Ldind_I: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.IntPtr; break; - case ILCode.__Ldind_I1: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.SByte; break; - case ILCode.__Ldind_I2: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.Int16; break; - case ILCode.__Ldind_I4: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.Int32; break; - case ILCode.__Ldind_I8: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.Int64; break; - case ILCode.__Ldind_U1: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.Byte; break; - case ILCode.__Ldind_U2: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.UInt16; break; - case ILCode.__Ldind_U4: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.UInt32; break; - case ILCode.__Ldind_R4: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.Single; break; - case ILCode.__Ldind_R8: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.Double; break; - case ILCode.__Stind_I: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.IntPtr; break; - case ILCode.__Stind_I1: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Byte; break; - case ILCode.__Stind_I2: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Int16; break; - case ILCode.__Stind_I4: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Int32; break; - case ILCode.__Stind_I8: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Int64; break; - case ILCode.__Stind_R4: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Single; break; - case ILCode.__Stind_R8: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Double; break; - } - } - - public static ParameterDefinition GetParameter (this MethodBody self, int index) - { - var method = self.Method; - - if (method.HasThis) { - if (index == 0) - return self.ThisParameter; - - index--; - } - - var parameters = method.Parameters; - - if (index < 0 || index >= parameters.Count) - return null; - - return parameters [index]; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/ILInlining.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/ILInlining.cs deleted file mode 100644 index 11140ef083..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/ILInlining.cs +++ /dev/null @@ -1,524 +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.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.ILAst -{ - /// <summary> - /// Performs inlining transformations. - /// </summary> - public class ILInlining - { - readonly ILBlock method; - internal Dictionary<ILVariable, int> numStloc = new Dictionary<ILVariable, int>(); - internal Dictionary<ILVariable, int> numLdloc = new Dictionary<ILVariable, int>(); - internal Dictionary<ILVariable, int> numLdloca = new Dictionary<ILVariable, int>(); - - public ILInlining(ILBlock method) - { - this.method = method; - AnalyzeMethod(); - } - - void AnalyzeMethod() - { - numStloc.Clear(); - numLdloc.Clear(); - numLdloca.Clear(); - - // Analyse the whole method - AnalyzeNode(method); - } - - /// <summary> - /// For each variable reference, adds <paramref name="direction"/> to the num* dicts. - /// Direction will be 1 for analysis, and -1 when removing a node from analysis. - /// </summary> - void AnalyzeNode(ILNode node, int direction = 1) - { - ILExpression expr = node as ILExpression; - if (expr != null) { - ILVariable locVar = expr.Operand as ILVariable; - if (locVar != null) { - if (expr.Code == ILCode.Stloc) { - numStloc[locVar] = numStloc.GetOrDefault(locVar) + direction; - } else if (expr.Code == ILCode.Ldloc) { - numLdloc[locVar] = numLdloc.GetOrDefault(locVar) + direction; - } else if (expr.Code == ILCode.Ldloca) { - numLdloca[locVar] = numLdloca.GetOrDefault(locVar) + direction; - } else { - throw new NotSupportedException(expr.Code.ToString()); - } - } - foreach (ILExpression child in expr.Arguments) - AnalyzeNode(child, direction); - } else { - var catchBlock = node as ILTryCatchBlock.CatchBlock; - if (catchBlock != null && catchBlock.ExceptionVariable != null) { - numStloc[catchBlock.ExceptionVariable] = numStloc.GetOrDefault(catchBlock.ExceptionVariable) + direction; - } - - foreach (ILNode child in node.GetChildren()) - AnalyzeNode(child, direction); - } - } - - public bool InlineAllVariables() - { - bool modified = false; - ILInlining i = new ILInlining(method); - foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) - modified |= i.InlineAllInBlock(block); - return modified; - } - - public bool InlineAllInBlock(ILBlock block) - { - bool modified = false; - List<ILNode> body = block.Body; - if (block is ILTryCatchBlock.CatchBlock && body.Count > 1) { - ILVariable v = ((ILTryCatchBlock.CatchBlock)block).ExceptionVariable; - if (v != null && v.IsGenerated) { - if (numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 1 && numLdloc.GetOrDefault(v) == 1) { - ILVariable v2; - ILExpression ldException; - if (body[0].Match(ILCode.Stloc, out v2, out ldException) && ldException.MatchLdloc(v)) { - body.RemoveAt(0); - ((ILTryCatchBlock.CatchBlock)block).ExceptionVariable = v2; - modified = true; - } - } - } - } - for(int i = 0; i < body.Count - 1;) { - ILVariable locVar; - ILExpression expr; - if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(block.Body, i, aggressive: false)) { - modified = true; - i = Math.Max(0, i - 1); // Go back one step - } else { - i++; - } - } - foreach(ILBasicBlock bb in body.OfType<ILBasicBlock>()) { - modified |= InlineAllInBasicBlock(bb); - } - return modified; - } - - public bool InlineAllInBasicBlock(ILBasicBlock bb) - { - bool modified = false; - List<ILNode> body = bb.Body; - for(int i = 0; i < body.Count;) { - ILVariable locVar; - ILExpression expr; - if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(bb.Body, i, aggressive: false)) { - modified = true; - i = Math.Max(0, i - 1); // Go back one step - } else { - i++; - } - } - return modified; - } - - /// <summary> - /// Inlines instructions before pos into block.Body[pos]. - /// </summary> - /// <returns>The number of instructions that were inlined.</returns> - public int InlineInto(List<ILNode> body, int pos, bool aggressive) - { - if (pos >= body.Count) - return 0; - int count = 0; - while (--pos >= 0) { - ILExpression expr = body[pos] as ILExpression; - if (expr == null || expr.Code != ILCode.Stloc) - break; - if (InlineOneIfPossible(body, pos, aggressive)) - count++; - else - break; - } - return count; - } - - /// <summary> - /// Aggressively inlines the stloc instruction at block.Body[pos] into the next instruction, if possible. - /// If inlining was possible; we will continue to inline (non-aggressively) into the the combined instruction. - /// </summary> - /// <remarks> - /// After the operation, pos will point to the new combined instruction. - /// </remarks> - public bool InlineIfPossible(List<ILNode> body, ref int pos) - { - if (InlineOneIfPossible(body, pos, true)) { - pos -= InlineInto(body, pos, false); - return true; - } - return false; - } - - /// <summary> - /// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible. - /// </summary> - public bool InlineOneIfPossible(List<ILNode> body, int pos, bool aggressive) - { - ILVariable v; - ILExpression inlinedExpression; - if (body[pos].Match(ILCode.Stloc, out v, out inlinedExpression) && !v.IsPinned) { - if (InlineIfPossible(v, inlinedExpression, body.ElementAtOrDefault(pos+1), aggressive)) { - // Assign the ranges of the stloc instruction: - inlinedExpression.ILRanges.AddRange(((ILExpression)body[pos]).ILRanges); - // Remove the stloc instruction: - body.RemoveAt(pos); - return true; - } else if (numLdloc.GetOrDefault(v) == 0 && numLdloca.GetOrDefault(v) == 0) { - // The variable is never loaded - if (inlinedExpression.HasNoSideEffects()) { - // Remove completely - AnalyzeNode(body[pos], -1); - body.RemoveAt(pos); - return true; - } else if (inlinedExpression.CanBeExpressionStatement() && v.IsGenerated) { - // Assign the ranges of the stloc instruction: - inlinedExpression.ILRanges.AddRange(((ILExpression)body[pos]).ILRanges); - // Remove the stloc, but keep the inner expression - body[pos] = inlinedExpression; - return true; - } - } - } - return false; - } - - /// <summary> - /// Inlines 'expr' into 'next', if possible. - /// </summary> - bool InlineIfPossible(ILVariable v, ILExpression inlinedExpression, ILNode next, bool aggressive) - { - // ensure the variable is accessed only a single time - if (numStloc.GetOrDefault(v) != 1) - return false; - int ldloc = numLdloc.GetOrDefault(v); - if (ldloc > 1 || ldloc + numLdloca.GetOrDefault(v) != 1) - return false; - - if (next is ILCondition) - next = ((ILCondition)next).Condition; - else if (next is ILWhileLoop) - next = ((ILWhileLoop)next).Condition; - - ILExpression parent; - int pos; - if (FindLoadInNext(next as ILExpression, v, inlinedExpression, out parent, out pos) == true) { - if (ldloc == 0) { - if (!IsGeneratedValueTypeTemporary((ILExpression)next, parent, pos, v, inlinedExpression)) - return false; - } else { - if (!aggressive && !v.IsGenerated && !NonAggressiveInlineInto((ILExpression)next, parent, inlinedExpression)) - return false; - } - - // Assign the ranges of the ldloc instruction: - inlinedExpression.ILRanges.AddRange(parent.Arguments[pos].ILRanges); - - if (ldloc == 0) { - // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof' so that the types - // comes out correctly - parent.Arguments[pos] = new ILExpression(ILCode.AddressOf, null, inlinedExpression); - } else { - parent.Arguments[pos] = inlinedExpression; - } - return true; - } - return false; - } - - /// <summary> - /// Is this a temporary variable generated by the C# compiler for instance method calls on value type values - /// </summary> - /// <param name="next">The next top-level expression</param> - /// <param name="parent">The direct parent of the load within 'next'</param> - /// <param name="pos">Index of the load within 'parent'</param> - /// <param name="v">The variable being inlined.</param> - /// <param name="inlinedExpression">The expression being inlined</param> - bool IsGeneratedValueTypeTemporary(ILExpression next, ILExpression parent, int pos, ILVariable v, ILExpression inlinedExpression) - { - if (pos == 0 && v.Type != null && v.Type.IsValueType) { - // Inlining a value type variable is allowed only if the resulting code will maintain the semantics - // that the method is operating on a copy. - // Thus, we have to disallow inlining of other locals, fields, array elements, dereferenced pointers - switch (inlinedExpression.Code) { - case ILCode.Ldloc: - case ILCode.Stloc: - case ILCode.CompoundAssignment: - case ILCode.Ldelem_Any: - case ILCode.Ldelem_I: - case ILCode.Ldelem_I1: - case ILCode.Ldelem_I2: - case ILCode.Ldelem_I4: - case ILCode.Ldelem_I8: - case ILCode.Ldelem_R4: - case ILCode.Ldelem_R8: - case ILCode.Ldelem_Ref: - case ILCode.Ldelem_U1: - case ILCode.Ldelem_U2: - case ILCode.Ldelem_U4: - case ILCode.Ldobj: - case ILCode.Ldind_Ref: - return false; - case ILCode.Ldfld: - case ILCode.Stfld: - case ILCode.Ldsfld: - case ILCode.Stsfld: - // allow inlining field access only if it's a readonly field - FieldDefinition f = ((FieldReference)inlinedExpression.Operand).Resolve(); - if (!(f != null && f.IsInitOnly)) - return false; - break; - case ILCode.Call: - case ILCode.CallGetter: - // inlining runs both before and after IntroducePropertyAccessInstructions, - // so we have to handle both 'call' and 'callgetter' - MethodReference mr = (MethodReference)inlinedExpression.Operand; - // ensure that it's not an multi-dimensional array getter - if (mr.DeclaringType is ArrayType) - return false; - goto case ILCode.Callvirt; - case ILCode.Callvirt: - case ILCode.CallvirtGetter: - // don't inline foreach loop variables: - mr = (MethodReference)inlinedExpression.Operand; - if (mr.Name == "get_Current" && mr.HasThis) - return false; - break; - case ILCode.Castclass: - case ILCode.Unbox_Any: - // These are valid, but might occur as part of a foreach loop variable. - ILExpression arg = inlinedExpression.Arguments[0]; - if (arg.Code == ILCode.CallGetter || arg.Code == ILCode.CallvirtGetter || arg.Code == ILCode.Call || arg.Code == ILCode.Callvirt) { - mr = (MethodReference)arg.Operand; - if (mr.Name == "get_Current" && mr.HasThis) - return false; // looks like a foreach loop variable, so don't inline it - } - break; - } - - // inline the compiler-generated variable that are used when accessing a member on a value type: - switch (parent.Code) { - case ILCode.Call: - case ILCode.CallGetter: - case ILCode.CallSetter: - case ILCode.Callvirt: - case ILCode.CallvirtGetter: - case ILCode.CallvirtSetter: - MethodReference mr = (MethodReference)parent.Operand; - return mr.HasThis; - case ILCode.Stfld: - case ILCode.Ldfld: - case ILCode.Ldflda: - case ILCode.Await: - return true; - } - } - return false; - } - - /// <summary> - /// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable. - /// </summary> - /// <param name="next">The next top-level expression</param> - /// <param name="parent">The direct parent of the load within 'next'</param> - /// <param name="inlinedExpression">The expression being inlined</param> - bool NonAggressiveInlineInto(ILExpression next, ILExpression parent, ILExpression inlinedExpression) - { - if (inlinedExpression.Code == ILCode.DefaultValue) - return true; - - switch (next.Code) { - case ILCode.Ret: - case ILCode.Brtrue: - return parent == next; - case ILCode.Switch: - return parent == next || (parent.Code == ILCode.Sub && parent == next.Arguments[0]); - default: - return false; - } - } - - /// <summary> - /// Gets whether 'expressionBeingMoved' can be inlined into 'expr'. - /// </summary> - public bool CanInlineInto(ILExpression expr, ILVariable v, ILExpression expressionBeingMoved) - { - ILExpression parent; - int pos; - return FindLoadInNext(expr, v, expressionBeingMoved, out parent, out pos) == true; - } - - /// <summary> - /// Finds the position to inline to. - /// </summary> - /// <returns>true = found; false = cannot continue search; null = not found</returns> - bool? FindLoadInNext(ILExpression expr, ILVariable v, ILExpression expressionBeingMoved, out ILExpression parent, out int pos) - { - parent = null; - pos = 0; - if (expr == null) - return false; - for (int i = 0; i < expr.Arguments.Count; i++) { - // Stop when seeing an opcode that does not guarantee that its operands will be evaluated. - // Inlining in that case might result in the inlined expresion not being evaluted. - if (i == 1 && (expr.Code == ILCode.LogicAnd || expr.Code == ILCode.LogicOr || expr.Code == ILCode.TernaryOp || expr.Code == ILCode.NullCoalescing)) - return false; - - ILExpression arg = expr.Arguments[i]; - - if ((arg.Code == ILCode.Ldloc || arg.Code == ILCode.Ldloca) && arg.Operand == v) { - parent = expr; - pos = i; - return true; - } - bool? r = FindLoadInNext(arg, v, expressionBeingMoved, out parent, out pos); - if (r != null) - return r; - } - if (IsSafeForInlineOver(expr, expressionBeingMoved)) - return null; // continue searching - else - return false; // abort, inlining not possible - } - - /// <summary> - /// Determines whether it is safe to move 'expressionBeingMoved' past 'expr' - /// </summary> - bool IsSafeForInlineOver(ILExpression expr, ILExpression expressionBeingMoved) - { - switch (expr.Code) { - case ILCode.Ldloc: - ILVariable loadedVar = (ILVariable)expr.Operand; - if (numLdloca.GetOrDefault(loadedVar) != 0) { - // abort, inlining is not possible - return false; - } - foreach (ILExpression potentialStore in expressionBeingMoved.GetSelfAndChildrenRecursive<ILExpression>()) { - if (potentialStore.Code == ILCode.Stloc && potentialStore.Operand == loadedVar) - return false; - } - // the expression is loading a non-forbidden variable - return true; - case ILCode.Ldloca: - case ILCode.Ldflda: - case ILCode.Ldsflda: - case ILCode.Ldelema: - case ILCode.AddressOf: - case ILCode.ValueOf: - case ILCode.NullableOf: - // address-loading instructions are safe if their arguments are safe - foreach (ILExpression arg in expr.Arguments) { - if (!IsSafeForInlineOver(arg, expressionBeingMoved)) - return false; - } - return true; - default: - // instructions with no side-effects are safe (except for Ldloc and Ldloca which are handled separately) - return expr.HasNoSideEffects(); - } - } - - /// <summary> - /// Runs a very simple form of copy propagation. - /// Copy propagation is used in two cases: - /// 1) assignments from arguments to local variables - /// If the target variable is assigned to only once (so always is that argument) and the argument is never changed (no ldarga/starg), - /// then we can replace the variable with the argument. - /// 2) assignments of address-loading instructions to local variables - /// </summary> - public void CopyPropagation() - { - foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - for (int i = 0; i < block.Body.Count; i++) { - ILVariable v; - ILExpression copiedExpr; - if (block.Body[i].Match(ILCode.Stloc, out v, out copiedExpr) - && !v.IsParameter && numStloc.GetOrDefault(v) == 1 && numLdloca.GetOrDefault(v) == 0 - && CanPerformCopyPropagation(copiedExpr, v)) - { - // un-inline the arguments of the ldArg instruction - ILVariable[] uninlinedArgs = new ILVariable[copiedExpr.Arguments.Count]; - for (int j = 0; j < uninlinedArgs.Length; j++) { - uninlinedArgs[j] = new ILVariable { IsGenerated = true, Name = v.Name + "_cp_" + j }; - block.Body.Insert(i++, new ILExpression(ILCode.Stloc, uninlinedArgs[j], copiedExpr.Arguments[j])); - } - - // perform copy propagation: - foreach (var expr in method.GetSelfAndChildrenRecursive<ILExpression>()) { - if (expr.Code == ILCode.Ldloc && expr.Operand == v) { - expr.Code = copiedExpr.Code; - expr.Operand = copiedExpr.Operand; - for (int j = 0; j < uninlinedArgs.Length; j++) { - expr.Arguments.Add(new ILExpression(ILCode.Ldloc, uninlinedArgs[j])); - } - } - } - - block.Body.RemoveAt(i); - if (uninlinedArgs.Length > 0) { - // if we un-inlined stuff; we need to update the usage counters - AnalyzeMethod(); - } - InlineInto(block.Body, i, aggressive: false); // maybe inlining gets possible after the removal of block.Body[i] - i -= uninlinedArgs.Length + 1; - } - } - } - } - - bool CanPerformCopyPropagation(ILExpression expr, ILVariable copyVariable) - { - switch (expr.Code) { - case ILCode.Ldloca: - case ILCode.Ldelema: - case ILCode.Ldflda: - case ILCode.Ldsflda: - // All address-loading instructions always return the same value for a given operand/argument combination, - // so they can be safely copied. - return true; - case ILCode.Ldloc: - ILVariable v = (ILVariable)expr.Operand; - if (v.IsParameter) { - // Parameters can be copied only if they aren't assigned to (directly or indirectly via ldarga) - return numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 0; - } else { - // Variables are be copied only if both they and the target copy variable are generated, - // and if the variable has only a single assignment - return v.IsGenerated && copyVariable.IsGenerated && numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 1; - } - default: - return false; - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs deleted file mode 100644 index 9f933b89b6..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs +++ /dev/null @@ -1,543 +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.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.ILAst -{ - /// <summary> - /// IL AST transformation that introduces array, object and collection initializers. - /// </summary> - partial class ILAstOptimizer - { - #region Array Initializers - bool TransformArrayInitializers(List<ILNode> body, ILExpression expr, int pos) - { - ILVariable v, v3; - ILExpression newarrExpr; - TypeReference elementType; - ILExpression lengthExpr; - int arrayLength; - if (expr.Match(ILCode.Stloc, out v, out newarrExpr) && - newarrExpr.Match(ILCode.Newarr, out elementType, out lengthExpr) && - lengthExpr.Match(ILCode.Ldc_I4, out arrayLength) && - arrayLength > 0) { - ILExpression[] newArr; - int initArrayPos; - if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, elementType, arrayLength, out newArr, out initArrayPos)) { - var arrayType = new ArrayType(elementType, 1); - arrayType.Dimensions[0] = new ArrayDimension(0, arrayLength); - body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr)); - body.RemoveAt(initArrayPos); - } - // Put in a limit so that we don't consume too much memory if the code allocates a huge array - // and populates it extremely sparsly. However, 255 "null" elements in a row actually occur in the Mono C# compiler! - const int maxConsecutiveDefaultValueExpressions = 300; - List<ILExpression> operands = new List<ILExpression>(); - int numberOfInstructionsToRemove = 0; - for (int j = pos + 1; j < body.Count; j++) { - ILExpression nextExpr = body[j] as ILExpression; - int arrayPos; - if (nextExpr != null && - nextExpr.Code.IsStoreToArray() && - nextExpr.Arguments[0].Match(ILCode.Ldloc, out v3) && - v == v3 && - nextExpr.Arguments[1].Match(ILCode.Ldc_I4, out arrayPos) && - arrayPos >= operands.Count && - arrayPos <= operands.Count + maxConsecutiveDefaultValueExpressions && - !nextExpr.Arguments[2].ContainsReferenceTo(v3)) - { - while (operands.Count < arrayPos) - operands.Add(new ILExpression(ILCode.DefaultValue, elementType)); - operands.Add(nextExpr.Arguments[2]); - numberOfInstructionsToRemove++; - } else { - break; - } - } - if (operands.Count == arrayLength) { - var arrayType = new ArrayType(elementType, 1); - arrayType.Dimensions[0] = new ArrayDimension(0, arrayLength); - expr.Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands); - body.RemoveRange(pos + 1, numberOfInstructionsToRemove); - - new ILInlining(method).InlineIfPossible(body, ref pos); - return true; - } - } - return false; - } - - bool TransformMultidimensionalArrayInitializers(List<ILNode> body, ILExpression expr, int pos) - { - ILVariable v; - ILExpression newarrExpr; - MethodReference ctor; - List<ILExpression> ctorArgs; - ArrayType arrayType; - if (expr.Match(ILCode.Stloc, out v, out newarrExpr) && - newarrExpr.Match(ILCode.Newobj, out ctor, out ctorArgs) && - (arrayType = (ctor.DeclaringType as ArrayType)) != null && - arrayType.Rank == ctorArgs.Count) { - // Clone the type, so we can muck about with the Dimensions - arrayType = new ArrayType(arrayType.ElementType, arrayType.Rank); - var arrayLengths = new int[arrayType.Rank]; - for (int i = 0; i < arrayType.Rank; i++) { - if (!ctorArgs[i].Match(ILCode.Ldc_I4, out arrayLengths[i])) return false; - if (arrayLengths[i] <= 0) return false; - arrayType.Dimensions[i] = new ArrayDimension(0, arrayLengths[i]); - } - - var totalElements = arrayLengths.Aggregate(1, (t, l) => t * l); - ILExpression[] newArr; - int initArrayPos; - if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, arrayType, totalElements, out newArr, out initArrayPos)) { - body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr)); - body.RemoveAt(initArrayPos); - return true; - } - } - return false; - } - - bool ForwardScanInitializeArrayRuntimeHelper(List<ILNode> body, int pos, ILVariable array, TypeReference arrayType, int arrayLength, out ILExpression[] values, out int foundPos) - { - ILVariable v2; - MethodReference methodRef; - ILExpression methodArg1; - ILExpression methodArg2; - FieldReference fieldRef; - if (body.ElementAtOrDefault(pos).Match(ILCode.Call, out methodRef, out methodArg1, out methodArg2) && - methodRef.DeclaringType.FullName == "System.Runtime.CompilerServices.RuntimeHelpers" && - methodRef.Name == "InitializeArray" && - methodArg1.Match(ILCode.Ldloc, out v2) && - array == v2 && - methodArg2.Match(ILCode.Ldtoken, out fieldRef)) - { - FieldDefinition fieldDef = fieldRef.ResolveWithinSameModule(); - if (fieldDef != null && fieldDef.InitialValue != null) { - ILExpression[] newArr = new ILExpression[arrayLength]; - if (DecodeArrayInitializer(arrayType.GetElementType(), fieldDef.InitialValue, newArr)) - { - values = newArr; - foundPos = pos; - return true; - } - } - } - values = null; - foundPos = -1; - return false; - } - - static bool DecodeArrayInitializer(TypeReference elementTypeRef, byte[] initialValue, ILExpression[] output) - { - TypeCode elementType = TypeAnalysis.GetTypeCode(elementTypeRef); - switch (elementType) { - case TypeCode.Boolean: - case TypeCode.Byte: - return DecodeArrayInitializer(initialValue, output, elementType, (d, i) => (int)d[i]); - case TypeCode.SByte: - return DecodeArrayInitializer(initialValue, output, elementType, (d, i) => (int)unchecked((sbyte)d[i])); - case TypeCode.Int16: - return DecodeArrayInitializer(initialValue, output, elementType, (d, i) => (int)BitConverter.ToInt16(d, i)); - case TypeCode.Char: - case TypeCode.UInt16: - return DecodeArrayInitializer(initialValue, output, elementType, (d, i) => (int)BitConverter.ToUInt16(d, i)); - case TypeCode.Int32: - case TypeCode.UInt32: - return DecodeArrayInitializer(initialValue, output, elementType, BitConverter.ToInt32); - case TypeCode.Int64: - case TypeCode.UInt64: - return DecodeArrayInitializer(initialValue, output, elementType, BitConverter.ToInt64); - case TypeCode.Single: - return DecodeArrayInitializer(initialValue, output, elementType, BitConverter.ToSingle); - case TypeCode.Double: - return DecodeArrayInitializer(initialValue, output, elementType, BitConverter.ToDouble); - case TypeCode.Object: - var typeDef = elementTypeRef.ResolveWithinSameModule(); - if (typeDef != null && typeDef.IsEnum) - return DecodeArrayInitializer(typeDef.GetEnumUnderlyingType(), initialValue, output); - - return false; - default: - return false; - } - } - - static bool DecodeArrayInitializer<T>(byte[] initialValue, ILExpression[] output, TypeCode elementType, Func<byte[], int, T> decoder) - { - int elementSize = ElementSizeOf(elementType); - if (initialValue.Length < (output.Length * elementSize)) - return false; - - ILCode code = LoadCodeFor(elementType); - for (int i = 0; i < output.Length; i++) - output[i] = new ILExpression(code, decoder(initialValue, i * elementSize)); - - return true; - } - - private static ILCode LoadCodeFor(TypeCode elementType) - { - switch (elementType) { - case TypeCode.Boolean: - case TypeCode.Byte: - case TypeCode.SByte: - case TypeCode.Char: - case TypeCode.Int16: - case TypeCode.UInt16: - case TypeCode.Int32: - case TypeCode.UInt32: - return ILCode.Ldc_I4; - case TypeCode.Int64: - case TypeCode.UInt64: - return ILCode.Ldc_I8; - case TypeCode.Single: - return ILCode.Ldc_R4; - case TypeCode.Double: - return ILCode.Ldc_R8; - default: - throw new ArgumentOutOfRangeException("elementType"); - } - } - - private static int ElementSizeOf(TypeCode elementType) - { - switch (elementType) { - case TypeCode.Boolean: - case TypeCode.Byte: - case TypeCode.SByte: - return 1; - case TypeCode.Char: - case TypeCode.Int16: - case TypeCode.UInt16: - return 2; - case TypeCode.Int32: - case TypeCode.UInt32: - case TypeCode.Single: - return 4; - case TypeCode.Int64: - case TypeCode.UInt64: - case TypeCode.Double: - return 8; - default: - throw new ArgumentOutOfRangeException("elementType"); - } - } - #endregion - - /// <summary> - /// Handles both object and collection initializers. - /// </summary> - bool TransformObjectInitializers(List<ILNode> body, ILExpression expr, int pos) - { - if (!context.Settings.ObjectOrCollectionInitializers) - return false; - - Debug.Assert(body[pos] == expr); // should be called for top-level expressions only - ILVariable v; - ILExpression newObjExpr; - TypeReference newObjType; - bool isValueType; - MethodReference ctor; - List<ILExpression> ctorArgs; - if (expr.Match(ILCode.Stloc, out v, out newObjExpr)) { - if (newObjExpr.Match(ILCode.Newobj, out ctor, out ctorArgs)) { - // v = newObj(ctor, ctorArgs) - newObjType = ctor.DeclaringType; - isValueType = false; - } else if (newObjExpr.Match(ILCode.DefaultValue, out newObjType)) { - // v = defaultvalue(type) - isValueType = true; - } else { - return false; - } - } else if (expr.Match(ILCode.Call, out ctor, out ctorArgs)) { - // call(SomeStruct::.ctor, ldloca(v), remainingArgs) - if (ctorArgs.Count > 0 && ctorArgs[0].Match(ILCode.Ldloca, out v)) { - isValueType = true; - newObjType = ctor.DeclaringType; - ctorArgs = new List<ILExpression>(ctorArgs); - ctorArgs.RemoveAt(0); - newObjExpr = new ILExpression(ILCode.Newobj, ctor, ctorArgs); - } else { - return false; - } - } else { - return false; - } - if (newObjType.IsValueType != isValueType) - return false; - - int originalPos = pos; - - // don't use object initializer syntax for closures - if (Ast.Transforms.DelegateConstruction.IsPotentialClosure(context, newObjType.ResolveWithinSameModule())) - return false; - - ILExpression initializer = ParseObjectInitializer(body, ref pos, v, newObjExpr, IsCollectionType(newObjType), isValueType); - - if (initializer.Arguments.Count == 1) // only newobj argument, no initializer elements - return false; - int totalElementCount = pos - originalPos - 1; // totalElementCount: includes elements from nested collections - Debug.Assert(totalElementCount >= initializer.Arguments.Count - 1); - - // Verify that we can inline 'v' into the next instruction: - - if (pos >= body.Count) - return false; // reached end of block, but there should be another instruction which consumes the initialized object - - ILInlining inlining = new ILInlining(method); - if (isValueType) { - // one ldloc for the use of the initialized object - if (inlining.numLdloc.GetOrDefault(v) != 1) - return false; - // one ldloca for each initializer argument, and also for the ctor call (if it exists) - if (inlining.numLdloca.GetOrDefault(v) != totalElementCount + (expr.Code == ILCode.Call ? 1 : 0)) - return false; - // one stloc for the initial store (if no ctor call was used) - if (inlining.numStloc.GetOrDefault(v) != (expr.Code == ILCode.Call ? 0 : 1)) - return false; - } else { - // one ldloc for each initializer argument, and another ldloc for the use of the initialized object - if (inlining.numLdloc.GetOrDefault(v) != totalElementCount + 1) - return false; - if (!(inlining.numStloc.GetOrDefault(v) == 1 && inlining.numLdloca.GetOrDefault(v) == 0)) - return false; - } - ILExpression nextExpr = body[pos] as ILExpression; - if (!inlining.CanInlineInto(nextExpr, v, initializer)) - return false; - - if (expr.Code == ILCode.Stloc) { - expr.Arguments[0] = initializer; - } else { - Debug.Assert(expr.Code == ILCode.Call); - expr.Code = ILCode.Stloc; - expr.Operand = v; - expr.Arguments.Clear(); - expr.Arguments.Add(initializer); - } - // remove all the instructions that were pulled into the initializer - body.RemoveRange(originalPos + 1, pos - originalPos - 1); - - // now that we know that it's an object initializer, change all the first arguments to 'InitializedObject' - ChangeFirstArgumentToInitializedObject(initializer); - - inlining = new ILInlining(method); - inlining.InlineIfPossible(body, ref originalPos); - - return true; - } - - /// <summary> - /// Gets whether the type supports collection initializers. - /// </summary> - static bool IsCollectionType(TypeReference tr) - { - if (tr == null) - return false; - TypeDefinition td = tr.Resolve(); - while (td != null) { - if (td.Interfaces.Select(x => x.InterfaceType).Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections")) - return true; - td = td.BaseType != null ? td.BaseType.Resolve() : null; - } - return false; - } - - /// <summary> - /// Gets whether 'expr' represents a setter in an object initializer. - /// ('CallvirtSetter(Property, v, value)') - /// </summary> - static bool IsSetterInObjectInitializer(ILExpression expr) - { - if (expr == null) - return false; - if (expr.Code == ILCode.CallvirtSetter || expr.Code == ILCode.CallSetter || expr.Code == ILCode.Stfld) { - return expr.Arguments.Count == 2; - } - return false; - } - - /// <summary> - /// Gets whether 'expr' represents the invocation of an 'Add' method in a collection initializer. - /// </summary> - static bool IsAddMethodCall(ILExpression expr) - { - MethodReference addMethod; - List<ILExpression> args; - if (expr.Match(ILCode.Callvirt, out addMethod, out args) || expr.Match(ILCode.Call, out addMethod, out args)) { - if (addMethod.Name == "Add" && addMethod.HasThis) { - return args.Count >= 2; - } - } - return false; - } - - /// <summary> - /// Parses an object initializer. - /// </summary> - /// <param name="body">ILAst block</param> - /// <param name="pos"> - /// Input: position of the instruction assigning to 'v'. - /// Output: first position after the object initializer - /// </param> - /// <param name="v">The variable that holds the object being initialized</param> - /// <param name="newObjExpr">The newobj instruction</param> - /// <returns>InitObject instruction</returns> - ILExpression ParseObjectInitializer(List<ILNode> body, ref int pos, ILVariable v, ILExpression newObjExpr, bool isCollection, bool isValueType) - { - // Take care not to modify any existing ILExpressions in here. - // We just construct new ones around the old ones, any modifications must wait until the whole - // object/collection initializer was analyzed. - ILExpression objectInitializer = new ILExpression(isCollection ? ILCode.InitCollection : ILCode.InitObject, null, newObjExpr); - List<ILExpression> initializerStack = new List<ILExpression>(); - initializerStack.Add(objectInitializer); - while (++pos < body.Count) { - ILExpression nextExpr = body[pos] as ILExpression; - if (IsSetterInObjectInitializer(nextExpr)) { - if (!AdjustInitializerStack(initializerStack, nextExpr.Arguments[0], v, false, isValueType)) { - CleanupInitializerStackAfterFailedAdjustment(initializerStack); - break; - } - initializerStack[initializerStack.Count - 1].Arguments.Add(nextExpr); - } else if (IsAddMethodCall(nextExpr)) { - if (!AdjustInitializerStack(initializerStack, nextExpr.Arguments[0], v, true, isValueType)) { - CleanupInitializerStackAfterFailedAdjustment(initializerStack); - break; - } - initializerStack[initializerStack.Count - 1].Arguments.Add(nextExpr); - } else { - // can't match any more initializers: end of object initializer - break; - } - } - return objectInitializer; - } - - static bool AdjustInitializerStack(List<ILExpression> initializerStack, ILExpression argument, ILVariable v, bool isCollection, bool isValueType) - { - // Argument is of the form 'getter(getter(...(v)))' - // Unpack it into a list of getters: - List<ILExpression> getters = new List<ILExpression>(); - while (argument.Code == ILCode.CallvirtGetter || argument.Code == ILCode.CallGetter || argument.Code == ILCode.Ldfld) { - getters.Add(argument); - if (argument.Arguments.Count != 1) - return false; - argument = argument.Arguments[0]; - } - // Ensure that the final argument is 'v' - if (isValueType) { - ILVariable loadedVar; - if (!(argument.Match(ILCode.Ldloca, out loadedVar) && loadedVar == v)) - return false; - } else { - if (!argument.MatchLdloc(v)) - return false; - } - // Now compare the getters with those that are currently active on the initializer stack: - int i; - for (i = 1; i <= Math.Min(getters.Count, initializerStack.Count - 1); i++) { - ILExpression g1 = initializerStack[i].Arguments[0]; // getter stored in initializer - ILExpression g2 = getters[getters.Count - i]; // matching getter from argument - if (g1.Operand != g2.Operand) { - // operands differ, so we abort the comparison - break; - } - } - // Remove all initializers from the stack that were not matched with one from the argument: - initializerStack.RemoveRange(i, initializerStack.Count - i); - // Now create new initializers for the remaining arguments: - for (; i <= getters.Count; i++) { - ILExpression g = getters[getters.Count - i]; - MemberReference mr = (MemberReference)g.Operand; - TypeReference returnType; - if (mr is FieldReference) - returnType = TypeAnalysis.GetFieldType((FieldReference)mr); - else - returnType = TypeAnalysis.SubstituteTypeArgs(((MethodReference)mr).ReturnType, mr); - - ILExpression nestedInitializer = new ILExpression( - IsCollectionType(returnType) ? ILCode.InitCollection : ILCode.InitObject, - null, g); - // add new initializer to its parent: - ILExpression parentInitializer = initializerStack[initializerStack.Count - 1]; - if (parentInitializer.Code == ILCode.InitCollection) { - // can't add children to collection initializer - if (parentInitializer.Arguments.Count == 1) { - // convert empty collection initializer to object initializer - parentInitializer.Code = ILCode.InitObject; - } else { - return false; - } - } - parentInitializer.Arguments.Add(nestedInitializer); - initializerStack.Add(nestedInitializer); - } - ILExpression lastInitializer = initializerStack[initializerStack.Count - 1]; - if (isCollection) { - return lastInitializer.Code == ILCode.InitCollection; - } else { - if (lastInitializer.Code == ILCode.InitCollection) { - if (lastInitializer.Arguments.Count == 1) { - // convert empty collection initializer to object initializer - lastInitializer.Code = ILCode.InitObject; - return true; - } else { - return false; - } - } else { - return true; - } - } - } - - static void CleanupInitializerStackAfterFailedAdjustment(List<ILExpression> initializerStack) - { - // There might be empty nested initializers left over; so we'll remove those: - while (initializerStack.Count > 1 && initializerStack[initializerStack.Count - 1].Arguments.Count == 1) { - ILExpression parent = initializerStack[initializerStack.Count - 2]; - Debug.Assert(parent.Arguments.Last() == initializerStack[initializerStack.Count - 1]); - parent.Arguments.RemoveAt(parent.Arguments.Count - 1); - initializerStack.RemoveAt(initializerStack.Count - 1); - } - } - - static void ChangeFirstArgumentToInitializedObject(ILExpression initializer) - { - // Go through all elements in the initializer (so skip the newobj-instr. at the start) - for (int i = 1; i < initializer.Arguments.Count; i++) { - ILExpression element = initializer.Arguments[i]; - if (element.Code == ILCode.InitCollection || element.Code == ILCode.InitObject) { - // nested collection/object initializer - ILExpression getCollection = element.Arguments[0]; - getCollection.Arguments[0] = new ILExpression(ILCode.InitializedObject, null); - ChangeFirstArgumentToInitializedObject(element); // handle the collection elements - } else { - element.Arguments[0] = new ILExpression(ILCode.InitializedObject, null); - } - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/LiftedOperators.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/LiftedOperators.cs deleted file mode 100644 index f4d4109bcb..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/LiftedOperators.cs +++ /dev/null @@ -1,528 +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.Collections.Generic; -using System.Linq; - -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.ILAst -{ - partial class ILAstOptimizer - { - bool SimplifyLiftedOperators(List<ILNode> body, ILExpression expr, int pos) - { - if (!new PatternMatcher(typeSystem).SimplifyLiftedOperators(expr)) return false; - - var inlining = new ILInlining(method); - while (--pos >= 0 && inlining.InlineIfPossible(body, ref pos)) ; - - return true; - } - - sealed class PatternMatcher - { - readonly TypeSystem typeSystem; - public PatternMatcher(TypeSystem typeSystem) - { - this.typeSystem = typeSystem; - } - - public bool SimplifyLiftedOperators(ILExpression expr) - { - if (Simplify(expr)) return true; - - bool modified = false; - foreach (var a in expr.Arguments) - modified |= SimplifyLiftedOperators(a); - return modified; - } - - abstract class Pattern - { - public readonly Pattern[] Arguments; - - protected Pattern(Pattern[] arguments) - { - this.Arguments = arguments; - } - - public virtual bool Match(PatternMatcher pm, ILExpression e) - { - if (e.Arguments.Count != this.Arguments.Length || e.Prefixes != null) return false; - for (int i = 0; i < this.Arguments.Length; i++) - if (!this.Arguments[i].Match(pm, e.Arguments[i])) return false; - return true; - } - - public virtual ILExpression BuildNew(PatternMatcher pm) - { - throw new NotSupportedException(); - } - - public static Pattern operator &(Pattern a, Pattern b) - { - return new ILPattern(ILCode.LogicAnd, a, b); - } - - public static Pattern operator |(Pattern a, Pattern b) - { - return new ILPattern(ILCode.LogicOr, a, b); - } - - public static Pattern operator !(Pattern a) - { - return new ILPattern(ILCode.LogicNot, a); - } - } - - sealed class ILPattern : Pattern - { - readonly ILCode code; - - public ILPattern(ILCode code, params Pattern[] arguments) - : base(arguments) - { - this.code = code; - } - - public override bool Match(PatternMatcher pm, ILExpression e) - { - return e.Code == this.code && base.Match(pm, e); - } - - public override ILExpression BuildNew(PatternMatcher pm) - { - var args = new ILExpression[this.Arguments.Length]; - for (int i = 0; i < args.Length; i++) args[i] = this.Arguments[i].BuildNew(pm); - TypeReference t = null; - switch (code) { - case ILCode.Ceq: - case ILCode.Cne: - t = pm.typeSystem.Boolean; - break; - case ILCode.NullCoalescing: - t = args[1].InferredType; - break; - } - return new ILExpression(code, null, args) { InferredType = t }; - } - } - - sealed class MethodPattern : Pattern - { - readonly ILCode code; - readonly string method; - - public MethodPattern(ILCode code, string method, params Pattern[] arguments) - : base(arguments) - { - this.code = code; - this.method = method; - } - - public override bool Match(PatternMatcher pm, ILExpression e) - { - if (e.Code != this.code) return false; - var m = (MethodReference)e.Operand; - return m.Name == this.method && TypeAnalysis.IsNullableType(m.DeclaringType) && base.Match(pm, e); - } - } - - enum OperatorType - { - Equality, InEquality, Comparison, Other - } - - sealed class OperatorPattern : Pattern - { - OperatorType type; - bool simple; - - public OperatorPattern() : base(null) { } - - public OperatorPattern(OperatorType type, bool simple) - : this() - { - this.type = type; - this.simple = simple; - } - - public override bool Match(PatternMatcher pm, ILExpression e) - { - switch (e.Code) { - case ILCode.Ceq: - if (type != OperatorType.Equality) return false; - break; - case ILCode.Cne: - if (type != OperatorType.InEquality) return false; - break; - case ILCode.Cgt: - case ILCode.Cgt_Un: - case ILCode.Cge: - case ILCode.Cge_Un: - case ILCode.Clt: - case ILCode.Clt_Un: - case ILCode.Cle: - case ILCode.Cle_Un: - if (type != OperatorType.Comparison) return false; - break; - case ILCode.Add: - case ILCode.Add_Ovf: - case ILCode.Add_Ovf_Un: - case ILCode.Sub: - case ILCode.Sub_Ovf: - case ILCode.Sub_Ovf_Un: - case ILCode.Mul: - case ILCode.Mul_Ovf: - case ILCode.Mul_Ovf_Un: - case ILCode.Div: - case ILCode.Div_Un: - case ILCode.Rem: - case ILCode.Rem_Un: - case ILCode.And: - case ILCode.Or: - case ILCode.Xor: - case ILCode.Shl: - case ILCode.Shr: - case ILCode.Shr_Un: - case ILCode.Not: - case ILCode.Neg: - case ILCode.LogicNot: - if (type != OperatorType.Other) return false; - break; - case ILCode.Call: - var m = e.Operand as MethodReference; - if (m == null || m.HasThis || !m.HasParameters || e.Arguments.Count > 2 || !IsCustomOperator(m.Name)) return false; - break; - default: return false; - } - if (pm.Operator != null) throw new InvalidOperationException(); - pm.Operator = e; - - var a0 = e.Arguments[0]; - if (!simple) return VariableAGetValueOrDefault.Match(pm, a0) && VariableBGetValueOrDefault.Match(pm, e.Arguments[1]); - if (e.Arguments.Count == 1) return VariableAGetValueOrDefault.Match(pm, a0); - if (VariableAGetValueOrDefault.Match(pm, a0)) { - pm.SimpleOperand = e.Arguments[1]; - pm.SimpleLeftOperand = false; - return true; - } - if (VariableAGetValueOrDefault.Match(pm, e.Arguments[1])) { - pm.SimpleOperand = a0; - pm.SimpleLeftOperand = true; - return true; - } - return false; - } - - bool IsCustomOperator(string s) - { - switch (type) { - case OperatorType.Equality: return s == "op_Equality"; - case OperatorType.InEquality: return s == "op_Inequality"; - case OperatorType.Comparison: - if (s.Length < 11 || !s.StartsWith("op_", StringComparison.Ordinal)) return false; - switch (s) { - case "op_GreaterThan": - case "op_GreaterThanOrEqual": - case "op_LessThan": - case "op_LessThanOrEqual": return true; - default: return false; - } - default: - if (s.Length < 10 || !s.StartsWith("op_", StringComparison.Ordinal)) return false; - switch (s) { - case "op_Addition": - case "op_Subtraction": - case "op_Multiply": - case "op_Division": - case "op_Modulus": - case "op_BitwiseAnd": - case "op_BitwiseOr": - case "op_ExclusiveOr": - case "op_LeftShift": - case "op_RightShift": - case "op_UnaryNegation": - case "op_UnaryPlus": - case "op_LogicalNot": - case "op_OnesComplement": - case "op_Increment": - case "op_Decrement": return true; - default: return false; - } - } - } - - public override ILExpression BuildNew(PatternMatcher pm) - { - var res = pm.Operator; - res.Arguments.Clear(); - if (pm.SimpleLeftOperand) res.Arguments.Add(pm.SimpleOperand); - res.Arguments.Add(VariableA.BuildNew(pm)); - if (pm.B != null) res.Arguments.Add(VariableB.BuildNew(pm)); - else if (pm.SimpleOperand != null && !pm.SimpleLeftOperand) res.Arguments.Add(pm.SimpleOperand); - return res; - } - } - - sealed class AnyPattern : Pattern - { - public AnyPattern() : base(null) { } - - public override bool Match(PatternMatcher pm, ILExpression e) - { - if (pm.SimpleOperand != null) throw new InvalidOperationException(); - pm.SimpleOperand = e; - return true; - } - - public override ILExpression BuildNew(PatternMatcher pm) - { - return pm.SimpleOperand; - } - } - - sealed class VariablePattern : Pattern - { - readonly ILCode code; - readonly bool b; - - public VariablePattern(ILCode code, bool b) - : base(null) - { - this.code = code; - this.b = b; - } - - public override bool Match(PatternMatcher pm, ILExpression e) - { - if (e.Code != this.code) return false; - var v = e.Operand as ILVariable; - return v != null && (this.b ? Capture(ref pm.B, v) : Capture(ref pm.A, v)); - } - - static bool Capture(ref ILVariable pmvar, ILVariable v) - { - if (pmvar != null) return pmvar == v; - pmvar = v; - return true; - } - - static readonly ILExpression[] EmptyArguments = new ILExpression[0]; - public override ILExpression BuildNew(PatternMatcher pm) - { - var v = this.b ? pm.B : pm.A; - var e = new ILExpression(ILCode.Ldloc, v, EmptyArguments); - if (TypeAnalysis.IsNullableType(v.Type)) e = new ILExpression(ILCode.ValueOf, null, e); - return e; - } - } - - sealed class BooleanPattern : Pattern - { - public static readonly Pattern False = new BooleanPattern(false), True = new BooleanPattern(true); - - readonly object value; - BooleanPattern(bool value) - : base(null) - { - this.value = Convert.ToInt32(value); - } - - public override bool Match(PatternMatcher pm, ILExpression e) - { - return e.Code == ILCode.Ldc_I4 && TypeAnalysis.IsBoolean(e.InferredType) && object.Equals(e.Operand, value); - } - - public override ILExpression BuildNew(PatternMatcher pm) - { - // boolean constants are wrapped inside a container to disable simplyfication of equality comparisons - return new ILExpression(ILCode.Wrap, null, new ILExpression(ILCode.Ldc_I4, value)); - } - } - - static readonly Pattern VariableRefA = new VariablePattern(ILCode.Ldloca, false), VariableRefB = new VariablePattern(ILCode.Ldloca, true); - static readonly Pattern VariableA = new VariablePattern(ILCode.Ldloc, false), VariableB = new VariablePattern(ILCode.Ldloc, true); - static readonly Pattern VariableAHasValue = new MethodPattern(ILCode.CallGetter, "get_HasValue", VariableRefA); - static readonly Pattern VariableAGetValueOrDefault = new MethodPattern(ILCode.Call, "GetValueOrDefault", VariableRefA); - static readonly Pattern VariableBHasValue = new MethodPattern(ILCode.CallGetter, "get_HasValue", VariableRefB); - static readonly Pattern VariableBGetValueOrDefault = new MethodPattern(ILCode.Call, "GetValueOrDefault", VariableRefB); - static readonly Pattern CeqHasValue = new ILPattern(ILCode.Ceq, VariableAHasValue, VariableBHasValue); - static readonly Pattern CneHasValue = new ILPattern(ILCode.Cne, VariableAHasValue, VariableBHasValue); - static readonly Pattern AndHasValue = new ILPattern(ILCode.And, VariableAHasValue, VariableBHasValue); - static readonly Pattern Any = new AnyPattern(); - static readonly Pattern OperatorVariableAB = new OperatorPattern(); - - static OperatorPattern OperatorNN(OperatorType type) - { - return new OperatorPattern(type, false); - } - - static OperatorPattern OperatorNV(OperatorType type) - { - return new OperatorPattern(type, true); - } - - static Pattern NewObj(Pattern p) - { - return new MethodPattern(ILCode.Newobj, ".ctor", p); - } - - static readonly Pattern[] Comparisons = new Pattern[] { - /* both operands nullable */ - // == (primitive, decimal) - OperatorNN(OperatorType.Equality) & CeqHasValue, - // == (struct) - CeqHasValue & (!VariableAHasValue | OperatorNN(OperatorType.Equality)), - // != (primitive, decimal) - OperatorNN(OperatorType.InEquality) | CneHasValue, - // != (struct) - CneHasValue | (VariableAHasValue & OperatorNN(OperatorType.InEquality)), - // > , < , >= , <= (primitive, decimal) - OperatorNN(OperatorType.Comparison) & AndHasValue, - // > , < , >= , <= (struct) - AndHasValue & OperatorNN(OperatorType.Comparison), - - /* only one operand nullable */ - // == (primitive, decimal) - OperatorNV(OperatorType.Equality) & VariableAHasValue, - // == (struct) - VariableAHasValue & OperatorNV(OperatorType.Equality), - // != (primitive, decimal) - OperatorNV(OperatorType.InEquality) | !VariableAHasValue, - // != (struct) - !VariableAHasValue | OperatorNV(OperatorType.InEquality), - // > , <, >= , <= (primitive, decimal) - OperatorNV(OperatorType.Comparison) & VariableAHasValue, - // > , < , >= , <= (struct) - VariableAHasValue & OperatorNV(OperatorType.Comparison), - }; - - static readonly Pattern[] Other = new Pattern[] { - /* both operands nullable */ - // & (bool) - new ILPattern(ILCode.TernaryOp, VariableAGetValueOrDefault | (!VariableBGetValueOrDefault & !VariableAHasValue), VariableB, VariableA), - new ILPattern(ILCode.And, VariableA, VariableB), - // | (bool) - new ILPattern(ILCode.TernaryOp, VariableAGetValueOrDefault | (!VariableBGetValueOrDefault & !VariableAHasValue), VariableA, VariableB), - new ILPattern(ILCode.Or, VariableA, VariableB), - // null coalescing - new ILPattern(ILCode.TernaryOp, VariableAHasValue, NewObj(VariableAGetValueOrDefault), VariableB), - new ILPattern(ILCode.NullCoalescing, VariableA, VariableB), - // all other - new ILPattern(ILCode.TernaryOp, AndHasValue, NewObj(OperatorNN(OperatorType.Other)), new ILPattern(ILCode.DefaultValue)), - OperatorVariableAB, - - /* only one operand nullable */ - // & (bool) - new ILPattern(ILCode.TernaryOp, Any, VariableA, NewObj(BooleanPattern.False)), - new ILPattern(ILCode.And, VariableA, Any), - // | (bool) - new ILPattern(ILCode.TernaryOp, Any, NewObj(BooleanPattern.True), VariableA), - new ILPattern(ILCode.Or, VariableA, Any), - // == true - VariableAGetValueOrDefault & VariableAHasValue, - new ILPattern(ILCode.Ceq, VariableA, BooleanPattern.True), - // != true - !VariableAGetValueOrDefault | !VariableAHasValue, - new ILPattern(ILCode.Cne, VariableA, BooleanPattern.True), - // == false - !VariableAGetValueOrDefault & VariableAHasValue, - new ILPattern(ILCode.Ceq, VariableA, BooleanPattern.False), - // != false - VariableAGetValueOrDefault | !VariableAHasValue, - new ILPattern(ILCode.Cne, VariableA, BooleanPattern.False), - // ?? true - !VariableAHasValue | VariableAGetValueOrDefault, - new ILPattern(ILCode.NullCoalescing, VariableA, BooleanPattern.True), - // ?? false - VariableAHasValue & VariableAGetValueOrDefault, - new ILPattern(ILCode.NullCoalescing, VariableA, BooleanPattern.False), - // null coalescing - new ILPattern(ILCode.TernaryOp, VariableAHasValue, VariableAGetValueOrDefault, Any), - new ILPattern(ILCode.NullCoalescing, VariableA, Any), - // all other - new ILPattern(ILCode.TernaryOp, VariableAHasValue, NewObj(OperatorNV(OperatorType.Other)), new ILPattern(ILCode.DefaultValue)), - OperatorVariableAB, - }; - - ILVariable A, B; - ILExpression Operator, SimpleOperand; - bool SimpleLeftOperand; - - void Reset() - { - this.A = null; - this.B = null; - this.Operator = null; - this.SimpleOperand = null; - this.SimpleLeftOperand = false; - } - - bool Simplify(ILExpression expr) - { - if (expr.Code == ILCode.TernaryOp || expr.Code == ILCode.LogicAnd || expr.Code == ILCode.LogicOr) { - Pattern[] ps; - if (expr.Code != ILCode.TernaryOp) { - ps = Comparisons; - for (int i = 0; i < ps.Length; i++) { - this.Reset(); - if (!ps[i].Match(this, expr)) continue; - SetResult(expr, OperatorVariableAB.BuildNew(this)); - return true; - } - } - ps = Other; - for (int i = 0; i < ps.Length; i += 2) { - this.Reset(); - if (!ps[i].Match(this, expr)) continue; - var n = ps[i + 1].BuildNew(this); - SetResult(expr, n); - if (n.Code == ILCode.NullCoalescing) { - // if both operands are nullable then the result is also nullable - if (n.Arguments[1].Code == ILCode.ValueOf) { - n.Arguments[0] = n.Arguments[0].Arguments[0]; - n.Arguments[1] = n.Arguments[1].Arguments[0]; - } - } else if (n.Code != ILCode.Ceq && n.Code != ILCode.Cne) { - expr.Code = ILCode.NullableOf; - expr.InferredType = expr.ExpectedType = null; - } - return true; - } - } - return false; - } - - static void SetResult(ILExpression expr, ILExpression n) - { - // IL ranges from removed nodes are assigned to the new operator expression - var removednodes = expr.GetSelfAndChildrenRecursive<ILExpression>().Except(n.GetSelfAndChildrenRecursive<ILExpression>()); - n.ILRanges.AddRange(removednodes.SelectMany(el => el.ILRanges)); - // the new expression is wrapped in a container so that negations aren't pushed through lifted comparison operations - expr.Code = ILCode.Wrap; - expr.Arguments.Clear(); - expr.Arguments.Add(n); - expr.ILRanges.Clear(); - expr.InferredType = n.InferredType; - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs deleted file mode 100644 index 32095b03eb..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs +++ /dev/null @@ -1,443 +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.Collections.Generic; -using System.Linq; - -using ICSharpCode.Decompiler.FlowAnalysis; - -namespace ICSharpCode.Decompiler.ILAst -{ - /// <summary> - /// Description of LoopsAndConditions. - /// </summary> - public class LoopsAndConditions - { - Dictionary<ILLabel, ControlFlowNode> labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>(); - - readonly DecompilerContext context; - - uint nextLabelIndex = 0; - - public LoopsAndConditions(DecompilerContext context) - { - this.context = context; - } - - public void FindLoops(ILBlock block) - { - if (block.Body.Count > 0) { - ControlFlowGraph graph; - graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand); - graph.ComputeDominance(context.CancellationToken); - graph.ComputeDominanceFrontier(); - block.Body = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint, false); - } - } - - public void FindConditions(ILBlock block) - { - if (block.Body.Count > 0) { - ControlFlowGraph graph; - graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand); - graph.ComputeDominance(context.CancellationToken); - graph.ComputeDominanceFrontier(); - block.Body = FindConditions(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint); - } - } - - ControlFlowGraph BuildGraph(List<ILNode> nodes, ILLabel entryLabel) - { - int index = 0; - List<ControlFlowNode> cfNodes = new List<ControlFlowNode>(); - ControlFlowNode entryPoint = new ControlFlowNode(index++, 0, ControlFlowNodeType.EntryPoint); - cfNodes.Add(entryPoint); - ControlFlowNode regularExit = new ControlFlowNode(index++, -1, ControlFlowNodeType.RegularExit); - cfNodes.Add(regularExit); - ControlFlowNode exceptionalExit = new ControlFlowNode(index++, -1, ControlFlowNodeType.ExceptionalExit); - cfNodes.Add(exceptionalExit); - - // Create graph nodes - labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>(); - Dictionary<ILNode, ControlFlowNode> astNodeToCfNode = new Dictionary<ILNode, ControlFlowNode>(); - foreach(ILBasicBlock node in nodes) { - ControlFlowNode cfNode = new ControlFlowNode(index++, -1, ControlFlowNodeType.Normal); - cfNodes.Add(cfNode); - astNodeToCfNode[node] = cfNode; - cfNode.UserData = node; - - // Find all contained labels - foreach(ILLabel label in node.GetSelfAndChildrenRecursive<ILLabel>()) { - labelToCfNode[label] = cfNode; - } - } - - // Entry endge - ControlFlowNode entryNode = labelToCfNode[entryLabel]; - ControlFlowEdge entryEdge = new ControlFlowEdge(entryPoint, entryNode, JumpType.Normal); - entryPoint.Outgoing.Add(entryEdge); - entryNode.Incoming.Add(entryEdge); - - // Create edges - foreach(ILBasicBlock node in nodes) { - ControlFlowNode source = astNodeToCfNode[node]; - - // Find all branches - foreach(ILLabel target in node.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { - ControlFlowNode destination; - // Labels which are out of out scope will not be in the collection - // Insert self edge only if we are sure we are a loop - if (labelToCfNode.TryGetValue(target, out destination) && (destination != source || target == node.Body.FirstOrDefault())) { - ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal); - source.Outgoing.Add(edge); - destination.Incoming.Add(edge); - } - } - } - - return new ControlFlowGraph(cfNodes.ToArray()); - } - - List<ILNode> FindLoops(HashSet<ControlFlowNode> scope, ControlFlowNode entryPoint, bool excludeEntryPoint) - { - List<ILNode> result = new List<ILNode>(); - - // Do not modify entry data - scope = new HashSet<ControlFlowNode>(scope); - - Queue<ControlFlowNode> agenda = new Queue<ControlFlowNode>(); - agenda.Enqueue(entryPoint); - while(agenda.Count > 0) { - ControlFlowNode node = agenda.Dequeue(); - - // If the node is a loop header - if (scope.Contains(node) - && node.DominanceFrontier.Contains(node) - && (node != entryPoint || !excludeEntryPoint)) - { - HashSet<ControlFlowNode> loopContents = FindLoopContent(scope, node); - - // If the first expression is a loop condition - ILBasicBlock basicBlock = (ILBasicBlock)node.UserData; - ILExpression condExpr; - ILLabel trueLabel; - ILLabel falseLabel; - // It has to be just brtrue - any preceding code would introduce goto - if(basicBlock.MatchSingleAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) - { - ControlFlowNode trueTarget; - labelToCfNode.TryGetValue(trueLabel, out trueTarget); - ControlFlowNode falseTarget; - labelToCfNode.TryGetValue(falseLabel, out falseTarget); - - // If one point inside the loop and the other outside - if ((!loopContents.Contains(trueTarget) && loopContents.Contains(falseTarget)) || - (loopContents.Contains(trueTarget) && !loopContents.Contains(falseTarget)) ) - { - loopContents.RemoveOrThrow(node); - scope.RemoveOrThrow(node); - - // If false means enter the loop - if (loopContents.Contains(falseTarget) || falseTarget == node) - { - // Negate the condition - condExpr = new ILExpression(ILCode.LogicNot, null, condExpr); - ILLabel tmp = trueLabel; - trueLabel = falseLabel; - falseLabel = tmp; - } - - ControlFlowNode postLoopTarget; - labelToCfNode.TryGetValue(falseLabel, out postLoopTarget); - if (postLoopTarget != null) { - // Pull more nodes into the loop - HashSet<ControlFlowNode> postLoopContents = FindDominatedNodes(scope, postLoopTarget); - var pullIn = scope.Except(postLoopContents).Where(n => node.Dominates(n)); - loopContents.UnionWith(pullIn); - } - - // Use loop to implement the brtrue - basicBlock.Body.RemoveTail(ILCode.Brtrue, ILCode.Br); - basicBlock.Body.Add(new ILWhileLoop() { - Condition = condExpr, - BodyBlock = new ILBlock() { - EntryGoto = new ILExpression(ILCode.Br, trueLabel), - Body = FindLoops(loopContents, node, false) - } - }); - basicBlock.Body.Add(new ILExpression(ILCode.Br, falseLabel)); - result.Add(basicBlock); - - scope.ExceptWith(loopContents); - } - } - - // Fallback method: while(true) - if (scope.Contains(node)) { - result.Add(new ILBasicBlock() { - Body = new List<ILNode>() { - new ILLabel() { Name = "Loop_" + (nextLabelIndex++) }, - new ILWhileLoop() { - BodyBlock = new ILBlock() { - EntryGoto = new ILExpression(ILCode.Br, (ILLabel)basicBlock.Body.First()), - Body = FindLoops(loopContents, node, true) - } - }, - }, - }); - - scope.ExceptWith(loopContents); - } - } - - // Using the dominator tree should ensure we find the the widest loop first - foreach(var child in node.DominatorTreeChildren) { - agenda.Enqueue(child); - } - } - - // Add whatever is left - foreach(var node in scope) { - result.Add((ILNode)node.UserData); - } - scope.Clear(); - - return result; - } - - List<ILNode> FindConditions(HashSet<ControlFlowNode> scope, ControlFlowNode entryNode) - { - List<ILNode> result = new List<ILNode>(); - - // Do not modify entry data - scope = new HashSet<ControlFlowNode>(scope); - - Stack<ControlFlowNode> agenda = new Stack<ControlFlowNode>(); - agenda.Push(entryNode); - while(agenda.Count > 0) { - ControlFlowNode node = agenda.Pop(); - - // Find a block that represents a simple condition - if (scope.Contains(node)) { - - ILBasicBlock block = (ILBasicBlock)node.UserData; - - { - // Switch - ILLabel[] caseLabels; - ILExpression switchArg; - ILLabel fallLabel; - if (block.MatchLastAndBr(ILCode.Switch, out caseLabels, out switchArg, out fallLabel)) { - - // Replace the switch code with ILSwitch - ILSwitch ilSwitch = new ILSwitch() { Condition = switchArg }; - block.Body.RemoveTail(ILCode.Switch, ILCode.Br); - block.Body.Add(ilSwitch); - block.Body.Add(new ILExpression(ILCode.Br, fallLabel)); - result.Add(block); - - // Remove the item so that it is not picked up as content - scope.RemoveOrThrow(node); - - // Find the switch offset - int addValue = 0; - List<ILExpression> subArgs; - if (ilSwitch.Condition.Match(ILCode.Sub, out subArgs) && subArgs[1].Match(ILCode.Ldc_I4, out addValue)) { - ilSwitch.Condition = subArgs[0]; - } - - // Pull in code of cases - ControlFlowNode fallTarget = null; - labelToCfNode.TryGetValue(fallLabel, out fallTarget); - - HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>(); - if (fallTarget != null) - frontiers.UnionWith(fallTarget.DominanceFrontier.Except(new [] { fallTarget })); - - foreach(ILLabel condLabel in caseLabels) { - ControlFlowNode condTarget = null; - labelToCfNode.TryGetValue(condLabel, out condTarget); - if (condTarget != null) - frontiers.UnionWith(condTarget.DominanceFrontier.Except(new [] { condTarget })); - } - - for (int i = 0; i < caseLabels.Length; i++) { - ILLabel condLabel = caseLabels[i]; - - // Find or create new case block - ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.FirstOrDefault(b => b.EntryGoto.Operand == condLabel); - if (caseBlock == null) { - caseBlock = new ILSwitch.CaseBlock() { - Values = new List<int>(), - EntryGoto = new ILExpression(ILCode.Br, condLabel) - }; - ilSwitch.CaseBlocks.Add(caseBlock); - - ControlFlowNode condTarget = null; - labelToCfNode.TryGetValue(condLabel, out condTarget); - if (condTarget != null && !frontiers.Contains(condTarget)) { - HashSet<ControlFlowNode> content = FindDominatedNodes(scope, condTarget); - scope.ExceptWith(content); - caseBlock.Body.AddRange(FindConditions(content, condTarget)); - // Add explicit break which should not be used by default, but the goto removal might decide to use it - caseBlock.Body.Add(new ILBasicBlock() { - Body = { - new ILLabel() { Name = "SwitchBreak_" + (nextLabelIndex++) }, - new ILExpression(ILCode.LoopOrSwitchBreak, null) - } - }); - } - } - caseBlock.Values.Add(i + addValue); - } - - // Heuristis to determine if we want to use fallthough as default case - if (fallTarget != null && !frontiers.Contains(fallTarget)) { - HashSet<ControlFlowNode> content = FindDominatedNodes(scope, fallTarget); - if (content.Any()) { - var caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, fallLabel) }; - ilSwitch.CaseBlocks.Add(caseBlock); - block.Body.RemoveTail(ILCode.Br); - - scope.ExceptWith(content); - caseBlock.Body.AddRange(FindConditions(content, fallTarget)); - // Add explicit break which should not be used by default, but the goto removal might decide to use it - caseBlock.Body.Add(new ILBasicBlock() { - Body = { - new ILLabel() { Name = "SwitchBreak_" + (nextLabelIndex++) }, - new ILExpression(ILCode.LoopOrSwitchBreak, null) - } - }); - } - } - } - - // Two-way branch - ILExpression condExpr; - ILLabel trueLabel; - ILLabel falseLabel; - if(block.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { - - // Swap bodies since that seems to be the usual C# order - ILLabel temp = trueLabel; - trueLabel = falseLabel; - falseLabel = temp; - condExpr = new ILExpression(ILCode.LogicNot, null, condExpr); - - // Convert the brtrue to ILCondition - ILCondition ilCond = new ILCondition() { - Condition = condExpr, - TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) }, - FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, falseLabel) } - }; - block.Body.RemoveTail(ILCode.Brtrue, ILCode.Br); - block.Body.Add(ilCond); - result.Add(block); - - // Remove the item immediately so that it is not picked up as content - scope.RemoveOrThrow(node); - - ControlFlowNode trueTarget = null; - labelToCfNode.TryGetValue(trueLabel, out trueTarget); - ControlFlowNode falseTarget = null; - labelToCfNode.TryGetValue(falseLabel, out falseTarget); - - // Pull in the conditional code - if (trueTarget != null && HasSingleEdgeEnteringBlock(trueTarget)) { - HashSet<ControlFlowNode> content = FindDominatedNodes(scope, trueTarget); - scope.ExceptWith(content); - ilCond.TrueBlock.Body.AddRange(FindConditions(content, trueTarget)); - } - if (falseTarget != null && HasSingleEdgeEnteringBlock(falseTarget)) { - HashSet<ControlFlowNode> content = FindDominatedNodes(scope, falseTarget); - scope.ExceptWith(content); - ilCond.FalseBlock.Body.AddRange(FindConditions(content, falseTarget)); - } - } - } - - // Add the node now so that we have good ordering - if (scope.Contains(node)) { - result.Add((ILNode)node.UserData); - scope.Remove(node); - } - } - - // depth-first traversal of dominator tree - for (int i = node.DominatorTreeChildren.Count - 1; i >= 0; i--) { - agenda.Push(node.DominatorTreeChildren[i]); - } - } - - // Add whatever is left - foreach(var node in scope) { - result.Add((ILNode)node.UserData); - } - - return result; - } - - static bool HasSingleEdgeEnteringBlock(ControlFlowNode node) - { - return node.Incoming.Count(edge => !node.Dominates(edge.Source)) == 1; - } - - static HashSet<ControlFlowNode> FindDominatedNodes(HashSet<ControlFlowNode> scope, ControlFlowNode head) - { - HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>(); - HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>(); - agenda.Add(head); - - while(agenda.Count > 0) { - ControlFlowNode addNode = agenda.First(); - agenda.Remove(addNode); - - if (scope.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) { - foreach (var successor in addNode.Successors) { - agenda.Add(successor); - } - } - } - - return result; - } - - static HashSet<ControlFlowNode> FindLoopContent(HashSet<ControlFlowNode> scope, ControlFlowNode head) - { - var viaBackEdges = head.Predecessors.Where(p => head.Dominates(p)); - HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>(viaBackEdges); - HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>(); - - while(agenda.Count > 0) { - ControlFlowNode addNode = agenda.First(); - agenda.Remove(addNode); - - if (scope.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) { - foreach (var predecessor in addNode.Predecessors) { - agenda.Add(predecessor); - } - } - } - if (scope.Contains(head)) - result.Add(head); - - return result; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/PatternMatching.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/PatternMatching.cs deleted file mode 100644 index 441088b903..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/PatternMatching.cs +++ /dev/null @@ -1,177 +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.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.ILAst -{ - public static class PatternMatching - { - public static bool Match(this ILNode node, ILCode code) - { - ILExpression expr = node as ILExpression; - return expr != null && expr.Prefixes == null && expr.Code == code; - } - - public static bool Match<T>(this ILNode node, ILCode code, out T operand) - { - ILExpression expr = node as ILExpression; - if (expr != null && expr.Prefixes == null && expr.Code == code && expr.Arguments.Count == 0) { - operand = (T)expr.Operand; - return true; - } - operand = default(T); - return false; - } - - public static bool Match(this ILNode node, ILCode code, out List<ILExpression> args) - { - ILExpression expr = node as ILExpression; - if (expr != null && expr.Prefixes == null && expr.Code == code) { - Debug.Assert(expr.Operand == null); - args = expr.Arguments; - return true; - } - args = null; - return false; - } - - public static bool Match(this ILNode node, ILCode code, out ILExpression arg) - { - List<ILExpression> args; - if (node.Match(code, out args) && args.Count == 1) { - arg = args[0]; - return true; - } - arg = null; - return false; - } - - public static bool Match<T>(this ILNode node, ILCode code, out T operand, out List<ILExpression> args) - { - ILExpression expr = node as ILExpression; - if (expr != null && expr.Prefixes == null && expr.Code == code) { - operand = (T)expr.Operand; - args = expr.Arguments; - return true; - } - operand = default(T); - args = null; - return false; - } - - public static bool Match<T>(this ILNode node, ILCode code, out T operand, out ILExpression arg) - { - List<ILExpression> args; - if (node.Match(code, out operand, out args) && args.Count == 1) { - arg = args[0]; - return true; - } - arg = null; - return false; - } - - public static bool Match<T>(this ILNode node, ILCode code, out T operand, out ILExpression arg1, out ILExpression arg2) - { - List<ILExpression> args; - if (node.Match(code, out operand, out args) && args.Count == 2) { - arg1 = args[0]; - arg2 = args[1]; - return true; - } - arg1 = null; - arg2 = null; - return false; - } - - public static bool MatchSingle<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg) - { - if (bb.Body.Count == 2 && - bb.Body[0] is ILLabel && - bb.Body[1].Match(code, out operand, out arg)) - { - return true; - } - operand = default(T); - arg = null; - return false; - } - - public static bool MatchSingleAndBr<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel brLabel) - { - if (bb.Body.Count == 3 && - bb.Body[0] is ILLabel && - bb.Body[1].Match(code, out operand, out arg) && - bb.Body[2].Match(ILCode.Br, out brLabel)) - { - return true; - } - operand = default(T); - arg = null; - brLabel = null; - return false; - } - - public static bool MatchLastAndBr<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel brLabel) - { - if (bb.Body.ElementAtOrDefault(bb.Body.Count - 2).Match(code, out operand, out arg) && - bb.Body.LastOrDefault().Match(ILCode.Br, out brLabel)) - { - return true; - } - operand = default(T); - arg = null; - brLabel = null; - return false; - } - - public static bool MatchThis(this ILNode node) - { - ILVariable v; - return node.Match(ILCode.Ldloc, out v) && v.IsParameter && v.OriginalParameter.Index == -1; - } - - public static bool MatchLdloc(this ILNode node, ILVariable expectedVar) - { - ILVariable v; - return node.Match(ILCode.Ldloc, out v) && v == expectedVar; - } - - public static bool MatchLdloca(this ILNode node, ILVariable expectedVar) - { - ILVariable v; - return node.Match(ILCode.Ldloca, out v) && v == expectedVar; - } - - public static bool MatchStloc(this ILNode node, ILVariable expectedVar, out ILExpression expr) - { - ILVariable v; - return node.Match(ILCode.Stloc, out v, out expr) && v == expectedVar; - } - - public static bool MatchLdcI4(this ILNode node, int expectedValue) - { - int v; - return node.Match(ILCode.Ldc_I4, out v) && v == expectedValue; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs deleted file mode 100644 index f514aa45f8..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs +++ /dev/null @@ -1,1103 +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.Collections.Generic; -using System.Diagnostics; -using System.Linq; - -using ICSharpCode.NRefactory.Utils; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.ILAst -{ - public partial class ILAstOptimizer - { - #region TypeConversionSimplifications - static bool TypeConversionSimplifications(List<ILNode> body, ILExpression expr, int pos) - { - bool modified = false; - modified |= TransformDecimalCtorToConstant(expr); - modified |= SimplifyLdcI4ConvI8(expr); - modified |= RemoveConvIFromArrayCreation(expr); - foreach(ILExpression arg in expr.Arguments) { - modified |= TypeConversionSimplifications(null, arg, -1); - } - return modified; - } - - static bool TransformDecimalCtorToConstant(ILExpression expr) - { - MethodReference r; - List<ILExpression> args; - if (expr.Match(ILCode.Newobj, out r, out args) && - r.DeclaringType.Namespace == "System" && - r.DeclaringType.Name == "Decimal") - { - if (args.Count == 1) { - int val; - if (args[0].Match(ILCode.Ldc_I4, out val)) { - expr.Code = ILCode.Ldc_Decimal; - expr.Operand = new decimal(val); - expr.InferredType = r.DeclaringType; - expr.Arguments.Clear(); - return true; - } - } else if (args.Count == 5) { - int lo, mid, hi, isNegative, scale; - if (expr.Arguments[0].Match(ILCode.Ldc_I4, out lo) && - expr.Arguments[1].Match(ILCode.Ldc_I4, out mid) && - expr.Arguments[2].Match(ILCode.Ldc_I4, out hi) && - expr.Arguments[3].Match(ILCode.Ldc_I4, out isNegative) && - expr.Arguments[4].Match(ILCode.Ldc_I4, out scale)) - { - expr.Code = ILCode.Ldc_Decimal; - expr.Operand = new decimal(lo, mid, hi, isNegative != 0, (byte)scale); - expr.InferredType = r.DeclaringType; - expr.Arguments.Clear(); - return true; - } - } - } - return false; - } - - static bool SimplifyLdcI4ConvI8(ILExpression expr) - { - ILExpression ldc; - int val; - if (expr.Match(ILCode.Conv_I8, out ldc) && ldc.Match(ILCode.Ldc_I4, out val)) { - expr.Code = ILCode.Ldc_I8; - expr.Operand = (long)val; - expr.Arguments.Clear(); - return true; - } - return false; - } - - static bool RemoveConvIFromArrayCreation(ILExpression expr) - { - TypeReference typeRef; - ILExpression length; - ILExpression input; - if (expr.Match(ILCode.Newarr, out typeRef, out length)) { - if (length.Match(ILCode.Conv_Ovf_I, out input) || length.Match(ILCode.Conv_I, out input) - || length.Match(ILCode.Conv_Ovf_I_Un, out input) || length.Match(ILCode.Conv_U, out input)) - { - expr.Arguments[0] = input; - return true; - } - } - return false; - } - #endregion - - #region SimplifyLdObjAndStObj - static bool SimplifyLdObjAndStObj(List<ILNode> body, ILExpression expr, int pos) - { - bool modified = false; - expr = SimplifyLdObjAndStObj(expr, ref modified); - if (modified && body != null) - body[pos] = expr; - for (int i = 0; i < expr.Arguments.Count; i++) { - expr.Arguments[i] = SimplifyLdObjAndStObj(expr.Arguments[i], ref modified); - modified |= SimplifyLdObjAndStObj(null, expr.Arguments[i], -1); - } - return modified; - } - - static ILExpression SimplifyLdObjAndStObj(ILExpression expr, ref bool modified) - { - if (expr.Code == ILCode.Initobj) { - expr.Code = ILCode.Stobj; - expr.Arguments.Add(new ILExpression(ILCode.DefaultValue, expr.Operand)); - modified = true; - } else if (expr.Code == ILCode.Cpobj) { - expr.Code = ILCode.Stobj; - expr.Arguments[1] = new ILExpression(ILCode.Ldobj, expr.Operand, expr.Arguments[1]); - modified = true; - } - ILExpression arg, arg2; - TypeReference type; - ILCode? newCode = null; - if (expr.Match(ILCode.Stobj, out type, out arg, out arg2)) { - switch (arg.Code) { - case ILCode.Ldelema: newCode = ILCode.Stelem_Any; break; - case ILCode.Ldloca: newCode = ILCode.Stloc; break; - case ILCode.Ldflda: newCode = ILCode.Stfld; break; - case ILCode.Ldsflda: newCode = ILCode.Stsfld; break; - } - } else if (expr.Match(ILCode.Ldobj, out type, out arg)) { - switch (arg.Code) { - case ILCode.Ldelema: newCode = ILCode.Ldelem_Any; break; - case ILCode.Ldloca: newCode = ILCode.Ldloc; break; - case ILCode.Ldflda: newCode = ILCode.Ldfld; break; - case ILCode.Ldsflda: newCode = ILCode.Ldsfld; break; - } - } - if (newCode != null) { - arg.Code = newCode.Value; - if (expr.Code == ILCode.Stobj) { - arg.InferredType = expr.InferredType; - arg.ExpectedType = expr.ExpectedType; - arg.Arguments.Add(arg2); - } - arg.ILRanges.AddRange(expr.ILRanges); - modified = true; - return arg; - } else { - return expr; - } - } - #endregion - - #region CachedDelegateInitialization - void CachedDelegateInitializationWithField(ILBlock block, ref int i) - { - // if (logicnot(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; - if (!c.Condition.Match(ILCode.LogicNot)) - return; - ILExpression condition = c.Condition.Arguments.Single() as ILExpression; - if (condition == null || condition.Code != ILCode.Ldsfld) - return; - FieldDefinition field = ((FieldReference)condition.Operand).ResolveWithinSameModule(); // 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 && ((FieldReference)stsfld.Operand).ResolveWithinSameModule() == 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 = ((MethodReference)newObj.Arguments[1].Operand).ResolveWithinSameModule(); // method is defined in current assembly - if (!Ast.Transforms.DelegateConstruction.IsAnonymousMethod(context, anonymousMethod)) - return; - - ILNode followingNode = block.Body.ElementAtOrDefault(i + 1); - if (followingNode != null && followingNode.GetSelfAndChildrenRecursive<ILExpression>().Count( - e => e.Code == ILCode.Ldsfld && ((FieldReference)e.Operand).ResolveWithinSameModule() == field) == 1) - { - foreach (ILExpression parent in followingNode.GetSelfAndChildrenRecursive<ILExpression>()) { - for (int j = 0; j < parent.Arguments.Count; j++) { - if (parent.Arguments[j].Code == ILCode.Ldsfld && ((FieldReference)parent.Arguments[j].Operand).ResolveWithinSameModule() == field) { - parent.Arguments[j] = newObj; - block.Body.RemoveAt(i); - i -= new ILInlining(method).InlineInto(block.Body, i, aggressive: false); - return; - } - } - } - } - } - - void CachedDelegateInitializationWithLocal(ILBlock block, ref int i) - { - // if (logicnot(ldloc(v))) { - // stloc(v, newobj(Action::.ctor, ldloc(displayClass), ldftn(method))) - // } else { - // } - // ...(..., ldloc(v), ...) - - 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; - if (!c.Condition.Match(ILCode.LogicNot)) - return; - ILExpression condition = c.Condition.Arguments.Single() as ILExpression; - if (condition == null || condition.Code != ILCode.Ldloc) - return; - ILVariable v = (ILVariable)condition.Operand; - ILExpression stloc = c.TrueBlock.Body[0] as ILExpression; - if (!(stloc != null && stloc.Code == ILCode.Stloc && (ILVariable)stloc.Operand == v)) - return; - ILExpression newObj = stloc.Arguments[0]; - if (!(newObj.Code == ILCode.Newobj && newObj.Arguments.Count == 2)) - return; - if (newObj.Arguments[0].Code != ILCode.Ldloc) - return; - if (newObj.Arguments[1].Code != ILCode.Ldftn) - return; - MethodDefinition anonymousMethod = ((MethodReference)newObj.Arguments[1].Operand).ResolveWithinSameModule(); // method is defined in current assembly - if (!Ast.Transforms.DelegateConstruction.IsAnonymousMethod(context, anonymousMethod)) - return; - - ILNode followingNode = block.Body.ElementAtOrDefault(i + 1); - if (followingNode != null && followingNode.GetSelfAndChildrenRecursive<ILExpression>().Count( - e => e.Code == ILCode.Ldloc && (ILVariable)e.Operand == v) == 1) - { - ILInlining inlining = new ILInlining(method); - if (!(inlining.numLdloc.GetOrDefault(v) == 2 && inlining.numStloc.GetOrDefault(v) == 2 && inlining.numLdloca.GetOrDefault(v) == 0)) - return; - - // Find the store instruction that initializes the local to null: - foreach (ILBlock storeBlock in method.GetSelfAndChildrenRecursive<ILBlock>()) { - for (int j = 0; j < storeBlock.Body.Count; j++) { - ILVariable storedVar; - ILExpression storedExpr; - if (storeBlock.Body[j].Match(ILCode.Stloc, out storedVar, out storedExpr) && storedVar == v && storedExpr.Match(ILCode.Ldnull)) { - // Remove the instruction - storeBlock.Body.RemoveAt(j); - if (storeBlock == block && j < i) - i--; - break; - } - } - } - - block.Body[i] = stloc; // remove the 'if (v==null)' - inlining = new ILInlining(method); - inlining.InlineIfPossible(block.Body, ref i); - } - } - #endregion - - #region MakeAssignmentExpression - bool MakeAssignmentExpression(List<ILNode> body, ILExpression expr, int pos) - { - // exprVar = ... - // stloc(v, exprVar) - // -> - // exprVar = stloc(v, ...)) - ILVariable exprVar; - ILExpression initializer; - if (!(expr.Match(ILCode.Stloc, out exprVar, out initializer) && exprVar.IsGenerated)) - return false; - ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression; - ILVariable v; - ILExpression stLocArg; - if (nextExpr.Match(ILCode.Stloc, out v, out stLocArg) && stLocArg.MatchLdloc(exprVar)) { - ILExpression store2 = body.ElementAtOrDefault(pos + 2) as ILExpression; - if (StoreCanBeConvertedToAssignment(store2, exprVar)) { - // expr_44 = ... - // stloc(v1, expr_44) - // anystore(v2, expr_44) - // -> - // stloc(v1, anystore(v2, ...)) - ILInlining inlining = new ILInlining(method); - if (inlining.numLdloc.GetOrDefault(exprVar) == 2 && inlining.numStloc.GetOrDefault(exprVar) == 1) { - body.RemoveAt(pos + 2); // remove store2 - body.RemoveAt(pos); // remove expr = ... - nextExpr.Arguments[0] = store2; - store2.Arguments[store2.Arguments.Count - 1] = initializer; - - inlining.InlineIfPossible(body, ref pos); - - return true; - } - } - - body.RemoveAt(pos + 1); // remove stloc - nextExpr.Arguments[0] = initializer; - ((ILExpression)body[pos]).Arguments[0] = nextExpr; - return true; - } else if ((nextExpr.Code == ILCode.Stsfld || nextExpr.Code == ILCode.CallSetter || nextExpr.Code == ILCode.CallvirtSetter) && nextExpr.Arguments.Count == 1) { - // exprVar = ... - // stsfld(fld, exprVar) - // -> - // exprVar = stsfld(fld, ...)) - if (nextExpr.Arguments[0].MatchLdloc(exprVar)) { - body.RemoveAt(pos + 1); // remove stsfld - nextExpr.Arguments[0] = initializer; - ((ILExpression)body[pos]).Arguments[0] = nextExpr; - return true; - } - } - return false; - } - - bool StoreCanBeConvertedToAssignment(ILExpression store, ILVariable exprVar) - { - if (store == null) - return false; - switch (store.Code) { - case ILCode.Stloc: - case ILCode.Stfld: - case ILCode.Stsfld: - case ILCode.Stobj: - case ILCode.CallSetter: - case ILCode.CallvirtSetter: - break; - default: - if (!store.Code.IsStoreToArray()) - return false; - break; - } - return store.Arguments.Last().Code == ILCode.Ldloc && store.Arguments.Last().Operand == exprVar; - } - #endregion - - #region MakeCompoundAssignments - bool MakeCompoundAssignments(List<ILNode> body, ILExpression expr, int pos) - { - bool modified = false; - modified |= MakeCompoundAssignment(expr); - // Static fields and local variables are not handled here - those are expressions without side effects - // and get handled by ReplaceMethodCallsWithOperators - // (which does a reversible transform to the short operator form, as the introduction of checked/unchecked might have to revert to the long form). - foreach (ILExpression arg in expr.Arguments) { - modified |= MakeCompoundAssignments(null, arg, -1); - } - if (modified && body != null) - new ILInlining(method).InlineInto(body, pos, aggressive: false); - return modified; - } - - bool MakeCompoundAssignment(ILExpression expr) - { - // stelem.any(T, ldloc(array), ldloc(pos), <OP>(ldelem.any(T, ldloc(array), ldloc(pos)), <RIGHT>)) - // or - // stobj(T, ldloc(ptr), <OP>(ldobj(T, ldloc(ptr)), <RIGHT>)) - ILCode expectedLdelemCode; - switch (expr.Code) { - case ILCode.Stelem_Any: - expectedLdelemCode = ILCode.Ldelem_Any; - break; - case ILCode.Stfld: - expectedLdelemCode = ILCode.Ldfld; - break; - case ILCode.Stobj: - expectedLdelemCode = ILCode.Ldobj; - break; - case ILCode.CallSetter: - expectedLdelemCode = ILCode.CallGetter; - break; - case ILCode.CallvirtSetter: - expectedLdelemCode = ILCode.CallvirtGetter; - break; - default: - return false; - } - - // all arguments except the last (so either array+pos, or ptr): - bool hasGeneratedVar = false; - for (int i = 0; i < expr.Arguments.Count - 1; i++) { - ILVariable inputVar; - if (!expr.Arguments[i].Match(ILCode.Ldloc, out inputVar)) - return false; - hasGeneratedVar |= inputVar.IsGenerated; - } - // At least one of the variables must be generated; otherwise we just keep the expanded form. - // We do this because we want compound assignments to be represented in ILAst only when strictly necessary; - // other compound assignments will be introduced by ReplaceMethodCallsWithOperator - // (which uses a reversible transformation, see ReplaceMethodCallsWithOperator.RestoreOriginalAssignOperatorAnnotation) - if (!hasGeneratedVar) - return false; - - ILExpression op = expr.Arguments.Last(); - // in case of compound assignments with a lifted operator the result is inside NullableOf and the operand is inside ValueOf - bool liftedOperator = false; - if (op.Code == ILCode.NullableOf) { - op = op.Arguments[0]; - liftedOperator = true; - } - if (!CanBeRepresentedAsCompoundAssignment(op)) - return false; - - ILExpression ldelem = op.Arguments[0]; - if (liftedOperator) { - if (ldelem.Code != ILCode.ValueOf) - return false; - ldelem = ldelem.Arguments[0]; - } - if (ldelem.Code != expectedLdelemCode) - return false; - Debug.Assert(ldelem.Arguments.Count == expr.Arguments.Count - 1); - for (int i = 0; i < ldelem.Arguments.Count; i++) { - if (!ldelem.Arguments[i].MatchLdloc((ILVariable)expr.Arguments[i].Operand)) - return false; - } - expr.Code = ILCode.CompoundAssignment; - expr.Operand = null; - expr.Arguments.RemoveRange(0, ldelem.Arguments.Count); - // result is "CompoundAssignment(<OP>(ldelem.any(...), <RIGHT>))" - return true; - } - - static bool CanBeRepresentedAsCompoundAssignment(ILExpression expr) - { - switch (expr.Code) { - case ILCode.Add: - case ILCode.Add_Ovf: - case ILCode.Add_Ovf_Un: - case ILCode.Sub: - case ILCode.Sub_Ovf: - case ILCode.Sub_Ovf_Un: - case ILCode.Mul: - case ILCode.Mul_Ovf: - case ILCode.Mul_Ovf_Un: - case ILCode.Div: - case ILCode.Div_Un: - case ILCode.Rem: - case ILCode.Rem_Un: - case ILCode.And: - case ILCode.Or: - case ILCode.Xor: - case ILCode.Shl: - case ILCode.Shr: - case ILCode.Shr_Un: - return true; - case ILCode.Call: - var m = expr.Operand as MethodReference; - if (m == null || m.HasThis || expr.Arguments.Count != 2) return false; - switch (m.Name) { - case "op_Addition": - case "op_Subtraction": - case "op_Multiply": - case "op_Division": - case "op_Modulus": - case "op_BitwiseAnd": - case "op_BitwiseOr": - case "op_ExclusiveOr": - case "op_LeftShift": - case "op_RightShift": - return true; - default: - return false; - } - default: - return false; - } - } - #endregion - - #region IntroducePostIncrement - - bool IntroducePostIncrement(List<ILNode> body, ILExpression expr, int pos) - { - bool modified = IntroducePostIncrementForVariables(body, expr, pos); - Debug.Assert(body[pos] == expr); // IntroducePostIncrementForVariables shouldn't change the expression reference - ILExpression newExpr = IntroducePostIncrementForInstanceFields(expr); - if (newExpr != null) { - modified = true; - body[pos] = newExpr; - new ILInlining(method).InlineIfPossible(body, ref pos); - } - return modified; - } - - bool IntroducePostIncrementForVariables(List<ILNode> body, ILExpression expr, int pos) - { - // Works for variables and static fields/properties - - // expr = ldloc(i) - // stloc(i, add(expr, ldc.i4(1))) - // -> - // expr = postincrement(1, ldloca(i)) - ILVariable exprVar; - ILExpression exprInit; - if (!(expr.Match(ILCode.Stloc, out exprVar, out exprInit) && exprVar.IsGenerated)) - return false; - - //The next expression - ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression; - if (nextExpr == null) - return false; - - ILCode loadInstruction = exprInit.Code; - ILCode storeInstruction = nextExpr.Code; - bool recombineVariable = false; - - // We only recognise local variables, static fields, and static getters with no arguments - switch (loadInstruction) { - case ILCode.Ldloc: - //Must be a matching store type - if (storeInstruction != ILCode.Stloc) - return false; - ILVariable loadVar = (ILVariable)exprInit.Operand; - ILVariable storeVar = (ILVariable)nextExpr.Operand; - if (loadVar != storeVar) { - if (loadVar.OriginalVariable != null && loadVar.OriginalVariable == storeVar.OriginalVariable) - recombineVariable = true; - else - return false; - } - break; - case ILCode.Ldsfld: - if (storeInstruction != ILCode.Stsfld) - return false; - if (exprInit.Operand != nextExpr.Operand) - return false; - break; - case ILCode.CallGetter: - // non-static getters would have the 'this' argument - if (exprInit.Arguments.Count != 0) - return false; - if (storeInstruction != ILCode.CallSetter) - return false; - if (!IsGetterSetterPair(exprInit.Operand, nextExpr.Operand)) - return false; - break; - default: - return false; - } - - ILExpression addExpr = nextExpr.Arguments[0]; - - int incrementAmount; - ILCode incrementCode = GetIncrementCode(addExpr, out incrementAmount); - if (!(incrementAmount != 0 && addExpr.Arguments[0].MatchLdloc(exprVar))) - return false; - - if (recombineVariable) { - // Split local variable, unsplit these two instances - // replace nextExpr.Operand with exprInit.Operand - ReplaceVariables(method, oldVar => oldVar == nextExpr.Operand ? (ILVariable)exprInit.Operand : oldVar); - } - - switch (loadInstruction) { - case ILCode.Ldloc: - exprInit.Code = ILCode.Ldloca; - break; - case ILCode.Ldsfld: - exprInit.Code = ILCode.Ldsflda; - break; - case ILCode.CallGetter: - exprInit = new ILExpression(ILCode.AddressOf, null, exprInit); - break; - } - expr.Arguments[0] = new ILExpression(incrementCode, incrementAmount, exprInit); - body.RemoveAt(pos + 1); // TODO ILRanges - return true; - } - - static bool IsGetterSetterPair(object getterOperand, object setterOperand) - { - MethodReference getter = getterOperand as MethodReference; - MethodReference setter = setterOperand as MethodReference; - if (getter == null || setter == null) - return false; - if (!TypeAnalysis.IsSameType(getter.DeclaringType, setter.DeclaringType)) - return false; - MethodDefinition getterDef = getter.Resolve(); - MethodDefinition setterDef = setter.Resolve(); - if (getterDef == null || setterDef == null) - return false; - foreach (PropertyDefinition prop in getterDef.DeclaringType.Properties) { - if (prop.GetMethod == getterDef) - return prop.SetMethod == setterDef; - } - return false; - } - - ILExpression IntroducePostIncrementForInstanceFields(ILExpression expr) - { - // stfld(field, ldloc(instance), add(stloc(helperVar, ldfld(field, ldloc(instance))), ldc.i4(1))) - // -> stloc(helperVar, postincrement(1, ldflda(field, ldloc(instance)))) - - // Also works for array elements and pointers: - - // stelem.any(T, ldloc(instance), ldloc(pos), add(stloc(helperVar, ldelem.any(T, ldloc(instance), ldloc(pos))), ldc.i4(1))) - // -> stloc(helperVar, postincrement(1, ldelema(ldloc(instance), ldloc(pos)))) - - // stobj(T, ldloc(ptr), add(stloc(helperVar, ldobj(T, ldloc(ptr)), ldc.i4(1)))) - // -> stloc(helperVar, postIncrement(1, ldloc(ptr))) - - // callsetter(set_P, ldloc(instance), add(stloc(helperVar, callgetter(get_P, ldloc(instance))), ldc.i4(1))) - // -> stloc(helperVar, postIncrement(1, propertyaddress. callgetter(get_P, ldloc(instance)))) - - if (!(expr.Code == ILCode.Stfld || expr.Code.IsStoreToArray() || expr.Code == ILCode.Stobj || expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter)) - return null; - - // Test that all arguments except the last are ldloc (1 arg for fields and pointers, 2 args for arrays) - for (int i = 0; i < expr.Arguments.Count - 1; i++) { - if (expr.Arguments[i].Code != ILCode.Ldloc) - return null; - } - - ILExpression addExpr = expr.Arguments[expr.Arguments.Count - 1]; - int incrementAmount; - ILCode incrementCode = GetIncrementCode(addExpr, out incrementAmount); - ILVariable helperVar; - ILExpression initialValue; - if (!(incrementAmount != 0 && addExpr.Arguments[0].Match(ILCode.Stloc, out helperVar, out initialValue))) - return null; - - if (expr.Code == ILCode.Stfld) { - if (initialValue.Code != ILCode.Ldfld) - return null; - // There might be two different FieldReference instances, so we compare the field's signatures: - FieldReference getField = (FieldReference)initialValue.Operand; - FieldReference setField = (FieldReference)expr.Operand; - if (!(TypeAnalysis.IsSameType(getField.DeclaringType, setField.DeclaringType) - && getField.Name == setField.Name && TypeAnalysis.IsSameType(getField.FieldType, setField.FieldType))) - { - return null; - } - } else if (expr.Code == ILCode.Stobj) { - if (!(initialValue.Code == ILCode.Ldobj && initialValue.Operand == expr.Operand)) - return null; - } else if (expr.Code == ILCode.CallSetter) { - if (!(initialValue.Code == ILCode.CallGetter && IsGetterSetterPair(initialValue.Operand, expr.Operand))) - return null; - } else if (expr.Code == ILCode.CallvirtSetter) { - if (!(initialValue.Code == ILCode.CallvirtGetter && IsGetterSetterPair(initialValue.Operand, expr.Operand))) - return null; - } else { - if (!initialValue.Code.IsLoadFromArray()) - return null; - } - Debug.Assert(expr.Arguments.Count - 1 == initialValue.Arguments.Count); - for (int i = 0; i < initialValue.Arguments.Count; i++) { - if (!initialValue.Arguments[i].MatchLdloc((ILVariable)expr.Arguments[i].Operand)) - return null; - } - - ILExpression stloc = addExpr.Arguments[0]; - if (expr.Code == ILCode.Stobj) { - stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue.Arguments[0]); - } else if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) { - initialValue = new ILExpression(ILCode.AddressOf, null, initialValue); - stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue); - } else { - stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue); - initialValue.Code = (expr.Code == ILCode.Stfld ? ILCode.Ldflda : ILCode.Ldelema); - } - // TODO: ILRanges? - - return stloc; - } - - ILCode GetIncrementCode(ILExpression addExpr, out int incrementAmount) - { - ILCode incrementCode; - bool decrement = false; - switch (addExpr.Code) { - case ILCode.Add: - incrementCode = ILCode.PostIncrement; - break; - case ILCode.Add_Ovf: - incrementCode = ILCode.PostIncrement_Ovf; - break; - case ILCode.Add_Ovf_Un: - incrementCode = ILCode.PostIncrement_Ovf_Un; - break; - case ILCode.Sub: - incrementCode = ILCode.PostIncrement; - decrement = true; - break; - case ILCode.Sub_Ovf: - incrementCode = ILCode.PostIncrement_Ovf; - decrement = true; - break; - case ILCode.Sub_Ovf_Un: - incrementCode = ILCode.PostIncrement_Ovf_Un; - decrement = true; - break; - default: - incrementAmount = 0; - return ILCode.Nop; - } - if (addExpr.Arguments[1].Match(ILCode.Ldc_I4, out incrementAmount)) { - if (incrementAmount == -1 || incrementAmount == 1) { // TODO pointer increment? - if (decrement) - incrementAmount = -incrementAmount; - return incrementCode; - } - } - incrementAmount = 0; - return ILCode.Nop; - } - #endregion - - #region IntroduceFixedStatements - bool IntroduceFixedStatements(List<ILNode> body, int i) - { - ILExpression initValue; - ILVariable pinnedVar; - int initEndPos; - if (!MatchFixedInitializer(body, i, out pinnedVar, out initValue, out initEndPos)) - return false; - - ILFixedStatement fixedStmt = body.ElementAtOrDefault(initEndPos) as ILFixedStatement; - if (fixedStmt != null) { - ILExpression expr = fixedStmt.BodyBlock.Body.LastOrDefault() as ILExpression; - if (expr != null && expr.Code == ILCode.Stloc && expr.Operand == pinnedVar && IsNullOrZero(expr.Arguments[0])) { - // we found a second initializer for the existing fixed statement - fixedStmt.Initializers.Insert(0, initValue); - body.RemoveRange(i, initEndPos - i); - fixedStmt.BodyBlock.Body.RemoveAt(fixedStmt.BodyBlock.Body.Count - 1); - if (pinnedVar.Type.IsByReference) - pinnedVar.Type = new PointerType(((ByReferenceType)pinnedVar.Type).ElementType); - return true; - } - } - - // find where pinnedVar is reset to 0: - int j; - for (j = initEndPos; j < body.Count; j++) { - ILVariable v2; - ILExpression storedVal; - // stloc(pinned_Var, conv.u(ldc.i4(0))) - if (body[j].Match(ILCode.Stloc, out v2, out storedVal) && v2 == pinnedVar) { - if (IsNullOrZero(storedVal)) { - break; - } - } - } - // Create fixed statement from i to j - fixedStmt = new ILFixedStatement(); - fixedStmt.Initializers.Add(initValue); - fixedStmt.BodyBlock = new ILBlock(body.GetRange(initEndPos, j - initEndPos)); // from initEndPos to j-1 (inclusive) - body.RemoveRange(i + 1, Math.Min(j, body.Count - 1) - i); // from i+1 to j (inclusive) - body[i] = fixedStmt; - if (pinnedVar.Type.IsByReference) - pinnedVar.Type = new PointerType(((ByReferenceType)pinnedVar.Type).ElementType); - - return true; - } - - bool IsNullOrZero(ILExpression expr) - { - if (expr.Code == ILCode.Conv_U || expr.Code == ILCode.Conv_I) - expr = expr.Arguments[0]; - return (expr.Code == ILCode.Ldc_I4 && (int)expr.Operand == 0) || expr.Code == ILCode.Ldnull; - } - - bool MatchFixedInitializer(List<ILNode> body, int i, out ILVariable pinnedVar, out ILExpression initValue, out int nextPos) - { - if (body[i].Match(ILCode.Stloc, out pinnedVar, out initValue) && pinnedVar.IsPinned && !IsNullOrZero(initValue)) { - initValue = (ILExpression)body[i]; - nextPos = i + 1; - HandleStringFixing(pinnedVar, body, ref nextPos, ref initValue); - return true; - } - ILCondition ifStmt = body[i] as ILCondition; - ILExpression arrayLoadingExpr; - if (ifStmt != null && MatchFixedArrayInitializerCondition(ifStmt.Condition, out arrayLoadingExpr)) { - ILVariable arrayVariable = (ILVariable)arrayLoadingExpr.Operand; - ILExpression trueValue; - if (ifStmt.TrueBlock != null && ifStmt.TrueBlock.Body.Count == 1 - && ifStmt.TrueBlock.Body[0].Match(ILCode.Stloc, out pinnedVar, out trueValue) - && pinnedVar.IsPinned && IsNullOrZero(trueValue)) - { - if (ifStmt.FalseBlock != null && ifStmt.FalseBlock.Body.Count == 1 && ifStmt.FalseBlock.Body[0] is ILFixedStatement) { - ILFixedStatement fixedStmt = (ILFixedStatement)ifStmt.FalseBlock.Body[0]; - ILVariable stlocVar; - ILExpression falseValue; - if (fixedStmt.Initializers.Count == 1 && fixedStmt.BodyBlock.Body.Count == 0 - && fixedStmt.Initializers[0].Match(ILCode.Stloc, out stlocVar, out falseValue) && stlocVar == pinnedVar) - { - ILVariable loadedVariable; - if (falseValue.Code == ILCode.Ldelema - && falseValue.Arguments[0].Match(ILCode.Ldloc, out loadedVariable) && loadedVariable == arrayVariable - && IsNullOrZero(falseValue.Arguments[1])) - { - // OK, we detected the pattern for fixing an array. - // Now check whether the loading expression was a store ot a temp. var - // that can be eliminated. - if (arrayLoadingExpr.Code == ILCode.Stloc) { - ILInlining inlining = new ILInlining(method); - if (inlining.numLdloc.GetOrDefault(arrayVariable) == 2 && - inlining.numStloc.GetOrDefault(arrayVariable) == 1 && inlining.numLdloca.GetOrDefault(arrayVariable) == 0) - { - arrayLoadingExpr = arrayLoadingExpr.Arguments[0]; - } - } - initValue = new ILExpression(ILCode.Stloc, pinnedVar, arrayLoadingExpr); - nextPos = i + 1; - return true; - } - } - } - } - } - initValue = null; - nextPos = -1; - return false; - } - - bool MatchFixedArrayInitializerCondition(ILExpression condition, out ILExpression initValue) - { - ILExpression logicAnd; - ILVariable arrayVar; - if (condition.Match(ILCode.LogicNot, out logicAnd) && logicAnd.Code == ILCode.LogicAnd) { - initValue = UnpackDoubleNegation(logicAnd.Arguments[0]); - ILExpression arrayVarInitializer; - if (initValue.Match(ILCode.Ldloc, out arrayVar) - || initValue.Match(ILCode.Stloc, out arrayVar, out arrayVarInitializer)) - { - ILExpression arrayLength = logicAnd.Arguments[1]; - if (arrayLength.Code == ILCode.Conv_I4) - arrayLength = arrayLength.Arguments[0]; - return arrayLength.Code == ILCode.Ldlen && arrayLength.Arguments[0].MatchLdloc(arrayVar); - } - } - initValue = null; - return false; - } - - ILExpression UnpackDoubleNegation(ILExpression expr) - { - ILExpression negated; - if (expr.Match(ILCode.LogicNot, out negated) && negated.Match(ILCode.LogicNot, out negated)) - return negated; - else - return expr; - } - - bool HandleStringFixing(ILVariable pinnedVar, List<ILNode> body, ref int pos, ref ILExpression fixedStmtInitializer) - { - // fixed (stloc(pinnedVar, ldloc(text))) { - // var1 = var2 = conv.i(ldloc(pinnedVar)) - // if (logicnot(logicnot(var1))) { - // var2 = add(var1, call(RuntimeHelpers::get_OffsetToStringData)) - // } - // stloc(ptrVar, var2) - // ... - - if (pos >= body.Count) - return false; - - ILVariable var1, var2; - ILExpression varAssignment, ptrInitialization; - if (!(body[pos].Match(ILCode.Stloc, out var1, out varAssignment) && varAssignment.Match(ILCode.Stloc, out var2, out ptrInitialization))) - return false; - if (!(var1.IsGenerated && var2.IsGenerated)) - return false; - if (ptrInitialization.Code == ILCode.Conv_I || ptrInitialization.Code == ILCode.Conv_U) - ptrInitialization = ptrInitialization.Arguments[0]; - if (!ptrInitialization.MatchLdloc(pinnedVar)) - return false; - - ILCondition ifStmt = body[pos + 1] as ILCondition; - if (!(ifStmt != null && ifStmt.TrueBlock != null && ifStmt.TrueBlock.Body.Count == 1 && (ifStmt.FalseBlock == null || ifStmt.FalseBlock.Body.Count == 0))) - return false; - if (!UnpackDoubleNegation(ifStmt.Condition).MatchLdloc(var1)) - return false; - ILVariable assignedVar; - ILExpression assignedExpr; - if (!(ifStmt.TrueBlock.Body[0].Match(ILCode.Stloc, out assignedVar, out assignedExpr) && assignedVar == var2 && assignedExpr.Code == ILCode.Add)) - return false; - MethodReference calledMethod; - if (!(assignedExpr.Arguments[0].MatchLdloc(var1))) - return false; - if (!(assignedExpr.Arguments[1].Match(ILCode.Call, out calledMethod) || assignedExpr.Arguments[1].Match(ILCode.CallGetter, out calledMethod))) - return false; - if (!(calledMethod.Name == "get_OffsetToStringData" && calledMethod.DeclaringType.FullName == "System.Runtime.CompilerServices.RuntimeHelpers")) - return false; - - ILVariable pointerVar; - if (body[pos + 2].Match(ILCode.Stloc, out pointerVar, out assignedExpr) && assignedExpr.MatchLdloc(var2)) { - pos += 3; - fixedStmtInitializer.Operand = pointerVar; - return true; - } - return false; - } - #endregion - - #region SimplifyLogicNot - static bool SimplifyLogicNot(List<ILNode> body, ILExpression expr, int pos) - { - bool modified = false; - expr = SimplifyLogicNot(expr, ref modified); - Debug.Assert(expr == null); - return modified; - } - - static ILExpression SimplifyLogicNot(ILExpression expr, ref bool modified) - { - ILExpression a; - // "ceq(a, ldc.i4.0)" becomes "logicnot(a)" if the inferred type for expression "a" is boolean - if (expr.Code == ILCode.Ceq && TypeAnalysis.IsBoolean(expr.Arguments[0].InferredType) && (a = expr.Arguments[1]).Code == ILCode.Ldc_I4 && (int)a.Operand == 0) { - expr.Code = ILCode.LogicNot; - expr.ILRanges.AddRange(a.ILRanges); - expr.Arguments.RemoveAt(1); - modified = true; - } - - ILExpression res = null; - while (expr.Code == ILCode.LogicNot) { - a = expr.Arguments[0]; - // remove double negation - if (a.Code == ILCode.LogicNot) { - res = a.Arguments[0]; - res.ILRanges.AddRange(expr.ILRanges); - res.ILRanges.AddRange(a.ILRanges); - expr = res; - } else { - if (SimplifyLogicNotArgument(expr)) res = expr = a; - break; - } - } - - for (int i = 0; i < expr.Arguments.Count; i++) { - a = SimplifyLogicNot(expr.Arguments[i], ref modified); - if (a != null) { - expr.Arguments[i] = a; - modified = true; - } - } - - return res; - } - - /// <summary> - /// If the argument is a binary comparison operation then the negation is pushed through it - /// </summary> - static bool SimplifyLogicNotArgument(ILExpression expr) - { - var a = expr.Arguments[0]; - ILCode c; - switch (a.Code) { - case ILCode.Ceq: c = ILCode.Cne; break; - case ILCode.Cne: c = ILCode.Ceq; break; - case ILCode.Cgt: c = ILCode.Cle; break; - case ILCode.Cgt_Un: c = ILCode.Cle_Un; break; - case ILCode.Cge: c = ILCode.Clt; break; - case ILCode.Cge_Un: c = ILCode.Clt_Un; break; - case ILCode.Clt: c = ILCode.Cge; break; - case ILCode.Clt_Un: c = ILCode.Cge_Un; break; - case ILCode.Cle: c = ILCode.Cgt; break; - case ILCode.Cle_Un: c = ILCode.Cgt_Un; break; - default: return false; - } - a.Code = c; - a.ILRanges.AddRange(expr.ILRanges); - return true; - } - #endregion - - #region SimplifyShiftOperators - static bool SimplifyShiftOperators(List<ILNode> body, ILExpression expr, int pos) - { - // C# compiles "a << b" to "a << (b & 31)", so we will remove the "& 31" if possible. - bool modified = false; - SimplifyShiftOperators(expr, ref modified); - return modified; - } - - static void SimplifyShiftOperators(ILExpression expr, ref bool modified) - { - for (int i = 0; i < expr.Arguments.Count; i++) - SimplifyShiftOperators(expr.Arguments[i], ref modified); - if (expr.Code != ILCode.Shl && expr.Code != ILCode.Shr && expr.Code != ILCode.Shr_Un) - return; - var a = expr.Arguments[1]; - if (a.Code != ILCode.And || a.Arguments[1].Code != ILCode.Ldc_I4 || expr.InferredType == null) - return; - int mask; - switch (expr.InferredType.MetadataType) { - case MetadataType.Int32: - case MetadataType.UInt32: mask = 31; break; - case MetadataType.Int64: - case MetadataType.UInt64: mask = 63; break; - default: return; - } - if ((int)a.Arguments[1].Operand != mask) return; - var res = a.Arguments[0]; - res.ILRanges.AddRange(a.ILRanges); - res.ILRanges.AddRange(a.Arguments[1].ILRanges); - expr.Arguments[1] = res; - modified = true; - } - #endregion - - #region InlineExpressionTreeParameterDeclarations - bool InlineExpressionTreeParameterDeclarations(List<ILNode> body, ILExpression expr, int pos) - { - // When there is a Expression.Lambda() call, and the parameters are declared in the - // IL statement immediately prior to the one containing the Lambda() call, - // using this code for the3 declaration: - // stloc(v, call(Expression::Parameter, call(Type::GetTypeFromHandle, ldtoken(...)), ldstr(...))) - // and the variables v are assigned only once (in that statements), and read only in a Expression::Lambda - // call that immediately follows the assignment statements, then we will inline those assignments - // into the Lambda call using ILCode.ExpressionTreeParameterDeclarations. - - // This is sufficient to allow inlining over the expression tree construction. The remaining translation - // of expression trees into C# will be performed by a C# AST transformer. - - for (int i = expr.Arguments.Count - 1; i >= 0; i--) { - if (InlineExpressionTreeParameterDeclarations(body, expr.Arguments[i], pos)) - return true; - } - - MethodReference mr; - ILExpression lambdaBodyExpr, parameterArray; - if (!(expr.Match(ILCode.Call, out mr, out lambdaBodyExpr, out parameterArray) && mr.Name == "Lambda")) - return false; - if (!(parameterArray.Code == ILCode.InitArray && mr.DeclaringType.FullName == "System.Linq.Expressions.Expression")) - return false; - int firstParameterPos = pos - parameterArray.Arguments.Count; - if (firstParameterPos < 0) - return false; - - ILExpression[] parameterInitExpressions = new ILExpression[parameterArray.Arguments.Count + 1]; - for (int i = 0; i < parameterArray.Arguments.Count; i++) { - parameterInitExpressions[i] = body[firstParameterPos + i] as ILExpression; - if (!MatchParameterVariableAssignment(parameterInitExpressions[i])) - return false; - ILVariable v = (ILVariable)parameterInitExpressions[i].Operand; - if (!parameterArray.Arguments[i].MatchLdloc(v)) - return false; - // TODO: validate that the variable is only used here and within 'body' - } - - parameterInitExpressions[parameterInitExpressions.Length - 1] = lambdaBodyExpr; - Debug.Assert(expr.Arguments[0] == lambdaBodyExpr); - expr.Arguments[0] = new ILExpression(ILCode.ExpressionTreeParameterDeclarations, null, parameterInitExpressions); - - body.RemoveRange(firstParameterPos, parameterArray.Arguments.Count); - - return true; - } - - bool MatchParameterVariableAssignment(ILExpression expr) - { - // stloc(v, call(Expression::Parameter, call(Type::GetTypeFromHandle, ldtoken(...)), ldstr(...))) - ILVariable v; - ILExpression init; - if (!expr.Match(ILCode.Stloc, out v, out init)) - return false; - if (v.IsGenerated || v.IsParameter || v.IsPinned) - return false; - if (v.Type == null || v.Type.FullName != "System.Linq.Expressions.ParameterExpression") - return false; - MethodReference parameterMethod; - ILExpression typeArg, nameArg; - if (!init.Match(ILCode.Call, out parameterMethod, out typeArg, out nameArg)) - return false; - if (!(parameterMethod.Name == "Parameter" && parameterMethod.DeclaringType.FullName == "System.Linq.Expressions.Expression")) - return false; - MethodReference getTypeFromHandle; - ILExpression typeToken; - if (!typeArg.Match(ILCode.Call, out getTypeFromHandle, out typeToken)) - return false; - if (!(getTypeFromHandle.Name == "GetTypeFromHandle" && getTypeFromHandle.DeclaringType.FullName == "System.Type")) - return false; - return typeToken.Code == ILCode.Ldtoken && nameArg.Code == ILCode.Ldstr; - } - #endregion - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs deleted file mode 100644 index e92e6abf89..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs +++ /dev/null @@ -1,376 +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.Collections.Generic; -using System.Diagnostics; -using System.Linq; - -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.ILAst -{ - public class SimpleControlFlow - { - Dictionary<ILLabel, int> labelGlobalRefCount = new Dictionary<ILLabel, int>(); - Dictionary<ILLabel, ILBasicBlock> labelToBasicBlock = new Dictionary<ILLabel, ILBasicBlock>(); - - DecompilerContext context; - TypeSystem typeSystem; - - public SimpleControlFlow(DecompilerContext context, ILBlock method) - { - this.context = context; - this.typeSystem = context.CurrentMethod.Module.TypeSystem; - - foreach(ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { - labelGlobalRefCount[target] = labelGlobalRefCount.GetOrDefault(target) + 1; - } - foreach(ILBasicBlock bb in method.GetSelfAndChildrenRecursive<ILBasicBlock>()) { - foreach(ILLabel label in bb.GetChildren().OfType<ILLabel>()) { - labelToBasicBlock[label] = bb; - } - } - } - - public bool SimplifyTernaryOperator(List<ILNode> body, ILBasicBlock head, int pos) - { - Debug.Assert(body.Contains(head)); - - ILExpression condExpr; - ILLabel trueLabel; - ILLabel falseLabel; - ILVariable trueLocVar = null; - ILExpression trueExpr; - ILLabel trueFall; - ILVariable falseLocVar = null; - ILExpression falseExpr; - ILLabel falseFall; - object unused; - - if (head.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) && - labelGlobalRefCount[trueLabel] == 1 && - labelGlobalRefCount[falseLabel] == 1 && - ((labelToBasicBlock[trueLabel].MatchSingleAndBr(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) && - labelToBasicBlock[falseLabel].MatchSingleAndBr(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall) && - trueLocVar == falseLocVar && trueFall == falseFall) || - (labelToBasicBlock[trueLabel].MatchSingle(ILCode.Ret, out unused, out trueExpr) && - labelToBasicBlock[falseLabel].MatchSingle(ILCode.Ret, out unused, out falseExpr))) && - body.Contains(labelToBasicBlock[trueLabel]) && - body.Contains(labelToBasicBlock[falseLabel]) - ) - { - bool isStloc = trueLocVar != null; - ILCode opCode = isStloc ? ILCode.Stloc : ILCode.Ret; - TypeReference retType = isStloc ? trueLocVar.Type : this.context.CurrentMethod.ReturnType; - bool retTypeIsBoolean = TypeAnalysis.IsBoolean(retType); - int leftBoolVal; - int rightBoolVal; - ILExpression newExpr; - // a ? true:false is equivalent to a - // a ? false:true is equivalent to !a - // a ? true : b is equivalent to a || b - // a ? b : true is equivalent to !a || b - // a ? b : false is equivalent to a && b - // a ? false : b is equivalent to !a && b - if (retTypeIsBoolean && - trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) && - falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) && - ((leftBoolVal != 0 && rightBoolVal == 0) || (leftBoolVal == 0 && rightBoolVal != 0)) - ) - { - // It can be expressed as trivilal expression - if (leftBoolVal != 0) { - newExpr = condExpr; - } else { - newExpr = new ILExpression(ILCode.LogicNot, null, condExpr) { InferredType = typeSystem.Boolean }; - } - } else if ((retTypeIsBoolean || TypeAnalysis.IsBoolean(falseExpr.InferredType)) && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) && (leftBoolVal == 0 || leftBoolVal == 1)) { - // It can be expressed as logical expression - if (leftBoolVal != 0) { - newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, condExpr, falseExpr); - } else { - newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, new ILExpression(ILCode.LogicNot, null, condExpr), falseExpr); - } - } else if ((retTypeIsBoolean || TypeAnalysis.IsBoolean(trueExpr.InferredType)) && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) && (rightBoolVal == 0 || rightBoolVal == 1)) { - // It can be expressed as logical expression - if (rightBoolVal != 0) { - newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, new ILExpression(ILCode.LogicNot, null, condExpr), trueExpr); - } else { - newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, condExpr, trueExpr); - } - } else { - // Ternary operator tends to create long complicated return statements - if (opCode == ILCode.Ret) - return false; - - // Only simplify generated variables - if (opCode == ILCode.Stloc && !trueLocVar.IsGenerated) - return false; - - // Create ternary expression - newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr); - } - - head.Body.RemoveTail(ILCode.Brtrue, ILCode.Br); - head.Body.Add(new ILExpression(opCode, trueLocVar, newExpr)); - if (isStloc) - head.Body.Add(new ILExpression(ILCode.Br, trueFall)); - - // Remove the old basic blocks - body.RemoveOrThrow(labelToBasicBlock[trueLabel]); - body.RemoveOrThrow(labelToBasicBlock[falseLabel]); - - return true; - } - return false; - } - - public bool SimplifyNullCoalescing(List<ILNode> body, ILBasicBlock head, int pos) - { - // ... - // v = ldloc(leftVar) - // brtrue(endBBLabel, ldloc(leftVar)) - // br(rightBBLabel) - // - // rightBBLabel: - // v = rightExpr - // br(endBBLabel) - // ... - // => - // ... - // v = NullCoalescing(ldloc(leftVar), rightExpr) - // br(endBBLabel) - - ILVariable v, v2; - ILExpression leftExpr, leftExpr2; - ILVariable leftVar; - ILLabel endBBLabel, endBBLabel2; - ILLabel rightBBLabel; - ILBasicBlock rightBB; - ILExpression rightExpr; - if (head.Body.Count >= 3 && - head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out v, out leftExpr) && - leftExpr.Match(ILCode.Ldloc, out leftVar) && - head.MatchLastAndBr(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) && - leftExpr2.MatchLdloc(leftVar) && - labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) && - rightBB.MatchSingleAndBr(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) && - v == v2 && - endBBLabel == endBBLabel2 && - labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 && - body.Contains(rightBB) - ) - { - head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br); - head.Body.Add(new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr))); - head.Body.Add(new ILExpression(ILCode.Br, endBBLabel)); - - body.RemoveOrThrow(labelToBasicBlock[rightBBLabel]); - return true; - } - return false; - } - - public bool SimplifyShortCircuit(List<ILNode> body, ILBasicBlock head, int pos) - { - Debug.Assert(body.Contains(head)); - - ILExpression condExpr; - ILLabel trueLabel; - ILLabel falseLabel; - if(head.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { - for (int pass = 0; pass < 2; pass++) { - - // On the second pass, swap labels and negate expression of the first branch - // It is slightly ugly, but much better then copy-pasting this whole block - ILLabel nextLabel = (pass == 0) ? trueLabel : falseLabel; - ILLabel otherLablel = (pass == 0) ? falseLabel : trueLabel; - bool negate = (pass == 1); - - ILBasicBlock nextBasicBlock = labelToBasicBlock[nextLabel]; - ILExpression nextCondExpr; - ILLabel nextTrueLablel; - ILLabel nextFalseLabel; - if (body.Contains(nextBasicBlock) && - nextBasicBlock != head && - labelGlobalRefCount[(ILLabel)nextBasicBlock.Body.First()] == 1 && - nextBasicBlock.MatchSingleAndBr(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) && - (otherLablel == nextFalseLabel || otherLablel == nextTrueLablel)) - { - // Create short cicuit branch - ILExpression logicExpr; - if (otherLablel == nextFalseLabel) { - logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr); - } else { - logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr); - } - head.Body.RemoveTail(ILCode.Brtrue, ILCode.Br); - head.Body.Add(new ILExpression(ILCode.Brtrue, nextTrueLablel, logicExpr)); - head.Body.Add(new ILExpression(ILCode.Br, nextFalseLabel)); - - // Remove the inlined branch from scope - body.RemoveOrThrow(nextBasicBlock); - - return true; - } - } - } - return false; - } - - public bool SimplifyCustomShortCircuit(List<ILNode> body, ILBasicBlock head, int pos) - { - Debug.Assert(body.Contains(head)); - - // --- looking for the following pattern --- - // stloc(targetVar, leftVar) - // brtrue(exitLabel, call(op_False, leftVar) - // br(followingBlock) - // - // FollowingBlock: - // stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression)) - // br(exitLabel) - // --- - - if (head.Body.Count < 3) - return false; - - // looking for: - // stloc(targetVar, leftVar) - ILVariable targetVar; - ILExpression targetVarInitExpr; - if (!head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out targetVar, out targetVarInitExpr)) - return false; - - ILVariable leftVar; - if (!targetVarInitExpr.Match(ILCode.Ldloc, out leftVar)) - return false; - - // looking for: - // brtrue(exitLabel, call(op_False, leftVar) - // br(followingBlock) - ILExpression callExpr; - ILLabel exitLabel; - ILLabel followingBlock; - if(!head.MatchLastAndBr(ILCode.Brtrue, out exitLabel, out callExpr, out followingBlock)) - return false; - - if (labelGlobalRefCount[followingBlock] > 1) - return false; - - MethodReference opFalse; - ILExpression opFalseArg; - if (!callExpr.Match(ILCode.Call, out opFalse, out opFalseArg)) - return false; - - // ignore operators other than op_False and op_True - if (opFalse.Name != "op_False" && opFalse.Name != "op_True") - return false; - - if (!opFalseArg.MatchLdloc(leftVar)) - return false; - - ILBasicBlock followingBasicBlock = labelToBasicBlock[followingBlock]; - - // FollowingBlock: - // stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression)) - // br(exitLabel) - ILVariable _targetVar; - ILExpression opBitwiseCallExpr; - ILLabel _exitLabel; - if (!followingBasicBlock.MatchSingleAndBr(ILCode.Stloc, out _targetVar, out opBitwiseCallExpr, out _exitLabel)) - return false; - - if (_targetVar != targetVar || exitLabel != _exitLabel) - return false; - - MethodReference opBitwise; - ILExpression leftVarExpression; - ILExpression rightExpression; - if (!opBitwiseCallExpr.Match(ILCode.Call, out opBitwise, out leftVarExpression, out rightExpression)) - return false; - - if (!leftVarExpression.MatchLdloc(leftVar)) - return false; - - // ignore operators other than op_BitwiseAnd and op_BitwiseOr - if (opBitwise.Name != "op_BitwiseAnd" && opBitwise.Name != "op_BitwiseOr") - return false; - - // insert: - // stloc(targetVar, LogicAnd(C::op_BitwiseAnd, leftVar, rightExpression) - // br(exitLabel) - ILCode op = opBitwise.Name == "op_BitwiseAnd" ? ILCode.LogicAnd : ILCode.LogicOr; - - if (op == ILCode.LogicAnd && opFalse.Name != "op_False") - return false; - - if (op == ILCode.LogicOr && opFalse.Name != "op_True") - return false; - - ILExpression shortCircuitExpr = MakeLeftAssociativeShortCircuit(op, opFalseArg, rightExpression); - shortCircuitExpr.Operand = opBitwise; - - head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br); - head.Body.Add(new ILExpression(ILCode.Stloc, targetVar, shortCircuitExpr)); - head.Body.Add(new ILExpression(ILCode.Br, exitLabel)); - body.Remove(followingBasicBlock); - - return true; - } - - ILExpression MakeLeftAssociativeShortCircuit(ILCode code, ILExpression left, ILExpression right) - { - // Assuming that the inputs are already left associative - if (right.Match(code)) { - // Find the leftmost logical expression - ILExpression current = right; - while(current.Arguments[0].Match(code)) - current = current.Arguments[0]; - current.Arguments[0] = new ILExpression(code, null, left, current.Arguments[0]) { InferredType = typeSystem.Boolean }; - return right; - } else { - return new ILExpression(code, null, left, right) { InferredType = typeSystem.Boolean }; - } - } - - public bool JoinBasicBlocks(List<ILNode> body, ILBasicBlock head, int pos) - { - ILLabel nextLabel; - ILBasicBlock nextBB; - if (!head.Body.ElementAtOrDefault(head.Body.Count - 2).IsConditionalControlFlow() && - head.Body.Last().Match(ILCode.Br, out nextLabel) && - labelGlobalRefCount[nextLabel] == 1 && - labelToBasicBlock.TryGetValue(nextLabel, out nextBB) && - body.Contains(nextBB) && - nextBB.Body.First() == nextLabel && - !nextBB.Body.OfType<ILTryCatchBlock>().Any() - ) - { - head.Body.RemoveTail(ILCode.Br); - nextBB.Body.RemoveAt(0); // Remove label - head.Body.AddRange(nextBB.Body); - - body.RemoveOrThrow(nextBB); - return true; - } - return false; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/StateRange.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/StateRange.cs deleted file mode 100644 index 3a3dfc2f34..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/StateRange.cs +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright (c) 2012 AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.ILAst -{ - struct Interval - { - public readonly int Start, End; - - public Interval(int start, int end) - { - Debug.Assert(start <= end || (start == 0 && end == -1)); - this.Start = start; - this.End = end; - } - - public override string ToString() - { - return string.Format("({0} to {1})", Start, End); - } - } - - class StateRange - { - readonly List<Interval> data = new List<Interval>(); - - public StateRange() - { - } - - public StateRange(int start, int end) - { - this.data.Add(new Interval(start, end)); - } - - public bool IsEmpty { - get { return data.Count == 0; } - } - - public bool Contains(int val) - { - foreach (Interval v in data) { - if (v.Start <= val && val <= v.End) - return true; - } - return false; - } - - public void UnionWith(StateRange other) - { - data.AddRange(other.data); - } - - /// <summary> - /// Unions this state range with (other intersect (minVal to maxVal)) - /// </summary> - public void UnionWith(StateRange other, int minVal, int maxVal) - { - foreach (Interval v in other.data) { - int start = Math.Max(v.Start, minVal); - int end = Math.Min(v.End, maxVal); - if (start <= end) - data.Add(new Interval(start, end)); - } - } - - /// <summary> - /// Merges overlapping interval ranges. - /// </summary> - public void Simplify() - { - if (data.Count < 2) - return; - data.Sort((a, b) => a.Start.CompareTo(b.Start)); - Interval prev = data[0]; - int prevIndex = 0; - for (int i = 1; i < data.Count; i++) { - Interval next = data[i]; - Debug.Assert(prev.Start <= next.Start); - if (next.Start <= prev.End + 1) { // intervals overlapping or touching - prev = new Interval(prev.Start, Math.Max(prev.End, next.End)); - data[prevIndex] = prev; - data[i] = new Interval(0, -1); // mark as deleted - } else { - prev = next; - prevIndex = i; - } - } - data.RemoveAll(i => i.Start > i.End); // remove all entries that were marked as deleted - } - - public override string ToString() - { - return string.Join(",", data); - } - - public Interval ToEnclosingInterval() - { - if (data.Count == 0) - throw new SymbolicAnalysisFailedException(); - return new Interval(data[0].Start, data[data.Count - 1].End); - } - } - - enum StateRangeAnalysisMode - { - IteratorMoveNext, - IteratorDispose, - AsyncMoveNext - } - - class StateRangeAnalysis - { - readonly StateRangeAnalysisMode mode; - readonly FieldDefinition stateField; - internal DefaultDictionary<ILNode, StateRange> ranges; - SymbolicEvaluationContext evalContext; - - internal Dictionary<MethodDefinition, StateRange> finallyMethodToStateRange; // used only for IteratorDispose - - /// <summary> - /// Initializes the state range logic: - /// Clears 'ranges' and sets 'ranges[entryPoint]' to the full range (int.MinValue to int.MaxValue) - /// </summary> - public StateRangeAnalysis(ILNode entryPoint, StateRangeAnalysisMode mode, FieldDefinition stateField, ILVariable cachedStateVar = null) - { - this.mode = mode; - this.stateField = stateField; - if (mode == StateRangeAnalysisMode.IteratorDispose) { - finallyMethodToStateRange = new Dictionary<MethodDefinition, StateRange>(); - } - - ranges = new DefaultDictionary<ILNode, StateRange>(n => new StateRange()); - ranges[entryPoint] = new StateRange(int.MinValue, int.MaxValue); - evalContext = new SymbolicEvaluationContext(stateField); - if (cachedStateVar != null) - evalContext.AddStateVariable(cachedStateVar); - } - - public int AssignStateRanges(List<ILNode> body, int bodyLength) - { - if (bodyLength == 0) - return 0; - for (int i = 0; i < bodyLength; i++) { - StateRange nodeRange = ranges[body[i]]; - nodeRange.Simplify(); - - ILLabel label = body[i] as ILLabel; - if (label != null) { - ranges[body[i + 1]].UnionWith(nodeRange); - continue; - } - - ILTryCatchBlock tryFinally = body[i] as ILTryCatchBlock; - if (tryFinally != null) { - if (mode == StateRangeAnalysisMode.IteratorDispose) { - if (tryFinally.CatchBlocks.Count != 0 || tryFinally.FaultBlock != null || tryFinally.FinallyBlock == null) - throw new SymbolicAnalysisFailedException(); - ranges[tryFinally.TryBlock].UnionWith(nodeRange); - if (tryFinally.TryBlock.Body.Count != 0) { - ranges[tryFinally.TryBlock.Body[0]].UnionWith(nodeRange); - AssignStateRanges(tryFinally.TryBlock.Body, tryFinally.TryBlock.Body.Count); - } - continue; - } else if (mode == StateRangeAnalysisMode.AsyncMoveNext) { - return i; - } else { - throw new SymbolicAnalysisFailedException(); - } - } - - ILExpression expr = body[i] as ILExpression; - if (expr == null) - throw new SymbolicAnalysisFailedException(); - switch (expr.Code) { - case ILCode.Switch: - { - SymbolicValue val = evalContext.Eval(expr.Arguments[0]); - if (val.Type != SymbolicValueType.State) - goto default; - ILLabel[] targetLabels = (ILLabel[])expr.Operand; - for (int j = 0; j < targetLabels.Length; j++) { - int state = j - val.Constant; - ranges[targetLabels[j]].UnionWith(nodeRange, state, state); - } - StateRange nextRange = ranges[body[i + 1]]; - nextRange.UnionWith(nodeRange, int.MinValue, -1 - val.Constant); - nextRange.UnionWith(nodeRange, targetLabels.Length - val.Constant, int.MaxValue); - break; - } - case ILCode.Br: - case ILCode.Leave: - ranges[(ILLabel)expr.Operand].UnionWith(nodeRange); - break; - case ILCode.Brtrue: - { - SymbolicValue val = evalContext.Eval(expr.Arguments[0]).AsBool(); - if (val.Type == SymbolicValueType.StateEquals) { - ranges[(ILLabel)expr.Operand].UnionWith(nodeRange, val.Constant, val.Constant); - StateRange nextRange = ranges[body[i + 1]]; - nextRange.UnionWith(nodeRange, int.MinValue, val.Constant - 1); - nextRange.UnionWith(nodeRange, val.Constant + 1, int.MaxValue); - break; - } else if (val.Type == SymbolicValueType.StateInEquals) { - ranges[body[i + 1]].UnionWith(nodeRange, val.Constant, val.Constant); - StateRange targetRange = ranges[(ILLabel)expr.Operand]; - targetRange.UnionWith(nodeRange, int.MinValue, val.Constant - 1); - targetRange.UnionWith(nodeRange, val.Constant + 1, int.MaxValue); - break; - } else { - goto default; - } - } - case ILCode.Nop: - ranges[body[i + 1]].UnionWith(nodeRange); - break; - case ILCode.Ret: - break; - case ILCode.Stloc: - { - SymbolicValue val = evalContext.Eval(expr.Arguments[0]); - if (val.Type == SymbolicValueType.State && val.Constant == 0) { - evalContext.AddStateVariable((ILVariable)expr.Operand); - goto case ILCode.Nop; - } else { - goto default; - } - } - case ILCode.Call: - // in some cases (e.g. foreach over array) the C# compiler produces a finally method outside of try-finally blocks - if (mode == StateRangeAnalysisMode.IteratorDispose) { - MethodDefinition mdef = (expr.Operand as MethodReference).ResolveWithinSameModule(); - if (mdef == null || finallyMethodToStateRange.ContainsKey(mdef)) - throw new SymbolicAnalysisFailedException(); - finallyMethodToStateRange.Add(mdef, nodeRange); - break; - } else { - goto default; - } - default: - if (mode == StateRangeAnalysisMode.IteratorDispose) { - throw new SymbolicAnalysisFailedException(); - } else { - return i; - } - } - } - return bodyLength; - } - - public void EnsureLabelAtPos(List<ILNode> body, ref int pos, ref int bodyLength) - { - if (pos > 0 && body[pos - 1] is ILLabel) { - pos--; - } else { - // ensure that the first element at body[pos] is a label: - ILLabel newLabel = new ILLabel(); - newLabel.Name = "YieldReturnEntryPoint"; - ranges[newLabel] = ranges[body[pos]]; // give the label the range of the instruction at body[pos] - body.Insert(pos, newLabel); - bodyLength++; - } - } - - public LabelRangeMapping CreateLabelRangeMapping(List<ILNode> body, int pos, int bodyLength) - { - LabelRangeMapping result = new LabelRangeMapping(); - CreateLabelRangeMapping(body, pos, bodyLength, result, false); - return result; - } - - void CreateLabelRangeMapping(List<ILNode> body, int pos, int bodyLength, LabelRangeMapping result, bool onlyInitialLabels) - { - for (int i = pos; i < bodyLength; i++) { - ILLabel label = body[i] as ILLabel; - if (label != null) { - result.Add(new KeyValuePair<ILLabel, StateRange>(label, ranges[label])); - } else { - ILTryCatchBlock tryCatchBlock = body[i] as ILTryCatchBlock; - if (tryCatchBlock != null) { - CreateLabelRangeMapping(tryCatchBlock.TryBlock.Body, 0, tryCatchBlock.TryBlock.Body.Count, result, true); - } else if (onlyInitialLabels) { - break; - } - } - } - } - } - - class LabelRangeMapping : List<KeyValuePair<ILLabel, StateRange>> {} -} diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/SymbolicExecution.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/SymbolicExecution.cs deleted file mode 100644 index 5fb40acf92..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/SymbolicExecution.cs +++ /dev/null @@ -1,157 +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.Collections.Generic; -using System.Diagnostics; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.ILAst -{ - /// <summary> - /// This exception is thrown when we find something else than we expect from the C# compiler. - /// This aborts the analysis and makes the whole transform fail. - /// </summary> - class SymbolicAnalysisFailedException : Exception {} - - enum SymbolicValueType - { - /// <summary> - /// Unknown value - /// </summary> - Unknown, - /// <summary> - /// int: Constant (result of ldc.i4) - /// </summary> - IntegerConstant, - /// <summary> - /// int: State + Constant - /// </summary> - State, - /// <summary> - /// This pointer (result of ldarg.0) - /// </summary> - This, - /// <summary> - /// bool: State == Constant - /// </summary> - StateEquals, - /// <summary> - /// bool: State != Constant - /// </summary> - StateInEquals - } - - struct SymbolicValue - { - public readonly int Constant; - public readonly SymbolicValueType Type; - - public SymbolicValue(SymbolicValueType type, int constant = 0) - { - this.Type = type; - this.Constant = constant; - } - - public SymbolicValue AsBool() - { - if (Type == SymbolicValueType.State) { - // convert state integer to bool: - // if (state + c) = if (state + c != 0) = if (state != -c) - return new SymbolicValue(SymbolicValueType.StateInEquals, unchecked(-Constant)); - } - return this; - } - public override string ToString() - { - return string.Format("[SymbolicValue {0}: {1}]", this.Type, this.Constant); - } - } - - class SymbolicEvaluationContext - { - readonly FieldDefinition stateField; - readonly List<ILVariable> stateVariables = new List<ILVariable>(); - - public SymbolicEvaluationContext(FieldDefinition stateField) - { - this.stateField = stateField; - } - - public void AddStateVariable(ILVariable v) - { - if (!stateVariables.Contains(v)) - stateVariables.Add(v); - } - - SymbolicValue Failed() - { - return new SymbolicValue(SymbolicValueType.Unknown); - } - - public SymbolicValue Eval(ILExpression expr) - { - SymbolicValue left, right; - switch (expr.Code) { - case ILCode.Sub: - left = Eval(expr.Arguments[0]); - right = Eval(expr.Arguments[1]); - if (left.Type != SymbolicValueType.State && left.Type != SymbolicValueType.IntegerConstant) - return Failed(); - if (right.Type != SymbolicValueType.IntegerConstant) - return Failed(); - return new SymbolicValue(left.Type, unchecked ( left.Constant - right.Constant )); - case ILCode.Ldfld: - if (Eval(expr.Arguments[0]).Type != SymbolicValueType.This) - return Failed(); - if (CecilExtensions.ResolveWithinSameModule(expr.Operand as FieldReference) != stateField) - return Failed(); - return new SymbolicValue(SymbolicValueType.State); - case ILCode.Ldloc: - ILVariable loadedVariable = (ILVariable)expr.Operand; - if (stateVariables.Contains(loadedVariable)) - return new SymbolicValue(SymbolicValueType.State); - else if (loadedVariable.IsParameter && loadedVariable.OriginalParameter.Index < 0) - return new SymbolicValue(SymbolicValueType.This); - else - return Failed(); - case ILCode.Ldc_I4: - return new SymbolicValue(SymbolicValueType.IntegerConstant, (int)expr.Operand); - case ILCode.Ceq: - case ILCode.Cne: - left = Eval(expr.Arguments[0]); - right = Eval(expr.Arguments[1]); - if (left.Type != SymbolicValueType.State || right.Type != SymbolicValueType.IntegerConstant) - return Failed(); - // bool: (state + left.Constant == right.Constant) - // bool: (state == right.Constant - left.Constant) - return new SymbolicValue(expr.Code == ILCode.Ceq ? SymbolicValueType.StateEquals : SymbolicValueType.StateInEquals, unchecked(right.Constant - left.Constant)); - case ILCode.LogicNot: - SymbolicValue val = Eval(expr.Arguments[0]).AsBool(); - if (val.Type == SymbolicValueType.StateEquals) - return new SymbolicValue(SymbolicValueType.StateInEquals, val.Constant); - else if (val.Type == SymbolicValueType.StateInEquals) - return new SymbolicValue(SymbolicValueType.StateEquals, val.Constant); - else - return Failed(); - default: - return Failed(); - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs deleted file mode 100644 index 6ed1f4dc05..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ /dev/null @@ -1,1297 +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.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace ICSharpCode.Decompiler.ILAst -{ - /// <summary> - /// Assigns C# types to IL expressions. - /// </summary> - /// <remarks> - /// Types are inferred in a bidirectional manner: - /// The expected type flows from the outside to the inside, the actual inferred type flows from the inside to the outside. - /// </remarks> - public class TypeAnalysis - { - public static void Run(DecompilerContext context, ILBlock method) - { - TypeAnalysis ta = new TypeAnalysis(); - ta.context = context; - ta.module = context.CurrentMethod.Module; - ta.typeSystem = ta.module.TypeSystem; - ta.method = method; - ta.CreateDependencyGraph(method); - ta.IdentifySingleLoadVariables(); - ta.RunInference(); - } - - sealed class ExpressionToInfer - { - public ILExpression Expression; - - public bool Done; - - /// <summary> - /// Set for assignment expressions that should wait until the variable type is available - /// from the context where the variable is used. - /// </summary> - public ILVariable DependsOnSingleLoad; - - /// <summary> - /// The list variables that are read by this expression. - /// </summary> - public List<ILVariable> Dependencies = new List<ILVariable>(); - - public override string ToString() - { - if (Done) - return "[Done] " + Expression.ToString(); - else - return Expression.ToString(); - } - - } - - DecompilerContext context; - TypeSystem typeSystem; - ILBlock method; - ModuleDefinition module; - List<ExpressionToInfer> allExpressions = new List<ExpressionToInfer>(); - DefaultDictionary<ILVariable, List<ExpressionToInfer>> assignmentExpressions = new DefaultDictionary<ILVariable, List<ExpressionToInfer>>(_ => new List<ExpressionToInfer>()); - HashSet<ILVariable> singleLoadVariables = new HashSet<ILVariable>(); - - #region CreateDependencyGraph - /// <summary> - /// Creates the "ExpressionToInfer" instances (=nodes in dependency graph) - /// </summary> - /// <remarks> - /// We are using a dependency graph to ensure that expressions are analyzed in the correct order. - /// </remarks> - void CreateDependencyGraph(ILNode node) - { - ILCondition cond = node as ILCondition; - if (cond != null) { - cond.Condition.ExpectedType = typeSystem.Boolean; - } - ILWhileLoop loop = node as ILWhileLoop; - if (loop != null && loop.Condition != null) { - loop.Condition.ExpectedType = typeSystem.Boolean; - } - ILTryCatchBlock.CatchBlock catchBlock = node as ILTryCatchBlock.CatchBlock; - if (catchBlock != null && catchBlock.ExceptionVariable != null && catchBlock.ExceptionType != null && catchBlock.ExceptionVariable.Type == null) { - catchBlock.ExceptionVariable.Type = catchBlock.ExceptionType; - } - ILExpression expr = node as ILExpression; - if (expr != null) { - ExpressionToInfer expressionToInfer = new ExpressionToInfer(); - expressionToInfer.Expression = expr; - allExpressions.Add(expressionToInfer); - FindNestedAssignments(expr, expressionToInfer); - - if (expr.Code == ILCode.Stloc && ((ILVariable)expr.Operand).Type == null) - assignmentExpressions[(ILVariable)expr.Operand].Add(expressionToInfer); - return; - } - foreach (ILNode child in node.GetChildren()) { - CreateDependencyGraph(child); - } - } - - void FindNestedAssignments(ILExpression expr, ExpressionToInfer parent) - { - foreach (ILExpression arg in expr.Arguments) { - if (arg.Code == ILCode.Stloc) { - ExpressionToInfer expressionToInfer = new ExpressionToInfer(); - expressionToInfer.Expression = arg; - allExpressions.Add(expressionToInfer); - FindNestedAssignments(arg, expressionToInfer); - ILVariable v = (ILVariable)arg.Operand; - if (v.Type == null) { - assignmentExpressions[v].Add(expressionToInfer); - // the instruction that consumes the stloc result is handled as if it was reading the variable - parent.Dependencies.Add(v); - } - } else { - ILVariable v; - if (arg.Match(ILCode.Ldloc, out v) && v.Type == null) { - parent.Dependencies.Add(v); - } - FindNestedAssignments(arg, parent); - } - } - } - #endregion - - void IdentifySingleLoadVariables() - { - // Find all variables that are assigned to exactly a single time: - var q = from expr in allExpressions - from v in expr.Dependencies - group expr by v; - foreach (var g in q.ToArray()) { - ILVariable v = g.Key; - if (g.Count() == 1 && g.Single().Expression.GetSelfAndChildrenRecursive<ILExpression>().Count(e => e.Operand == v) == 1) { - singleLoadVariables.Add(v); - // Mark the assignments as dependent on the type from the single load: - foreach (var assignment in assignmentExpressions[v]) { - assignment.DependsOnSingleLoad = v; - } - } - } - } - - void RunInference() - { - int numberOfExpressionsAlreadyInferred = 0; - // Two flags that allow resolving cycles: - bool ignoreSingleLoadDependencies = false; - bool assignVariableTypesBasedOnPartialInformation = false; - while (numberOfExpressionsAlreadyInferred < allExpressions.Count) { - int oldCount = numberOfExpressionsAlreadyInferred; - foreach (ExpressionToInfer expr in allExpressions) { - if (!expr.Done && expr.Dependencies.TrueForAll(v => v.Type != null || singleLoadVariables.Contains(v)) - && (expr.DependsOnSingleLoad == null || expr.DependsOnSingleLoad.Type != null || ignoreSingleLoadDependencies)) - { - RunInference(expr.Expression); - expr.Done = true; - numberOfExpressionsAlreadyInferred++; - } - } - if (numberOfExpressionsAlreadyInferred == oldCount) { - if (ignoreSingleLoadDependencies) { - if (assignVariableTypesBasedOnPartialInformation) - throw new InvalidOperationException("Could not infer any expression"); - else - assignVariableTypesBasedOnPartialInformation = true; - } else { - // We have a cyclic dependency; we'll try if we can resolve it by ignoring single-load dependencies. - // This can happen if the variable was not actually assigned an expected type by the single-load instruction. - ignoreSingleLoadDependencies = true; - continue; - } - } else { - assignVariableTypesBasedOnPartialInformation = false; - ignoreSingleLoadDependencies = false; - } - // Now infer types for variables: - foreach (var pair in assignmentExpressions) { - ILVariable v = pair.Key; - if (v.Type == null && (assignVariableTypesBasedOnPartialInformation ? pair.Value.Any(e => e.Done) : pair.Value.All(e => e.Done))) { - TypeReference inferredType = null; - foreach (ExpressionToInfer expr in pair.Value) { - Debug.Assert(expr.Expression.Code == ILCode.Stloc); - ILExpression assignedValue = expr.Expression.Arguments.Single(); - if (assignedValue.InferredType != null) { - if (inferredType == null) { - inferredType = assignedValue.InferredType; - } else { - // pick the common base type - inferredType = TypeWithMoreInformation(inferredType, assignedValue.InferredType); - } - } - } - if (inferredType == null) - inferredType = typeSystem.Object; - v.Type = inferredType; - // Assign inferred type to all the assignments (in case they used different inferred types): - foreach (ExpressionToInfer expr in pair.Value) { - expr.Expression.InferredType = inferredType; - // re-infer if the expected type has changed - InferTypeForExpression(expr.Expression.Arguments.Single(), inferredType); - } - } - } - } - } - - void RunInference(ILExpression expr) - { - bool anyArgumentIsMissingExpectedType = expr.Arguments.Any(a => a.ExpectedType == null); - if (expr.InferredType == null || anyArgumentIsMissingExpectedType) - InferTypeForExpression(expr, expr.ExpectedType, forceInferChildren: anyArgumentIsMissingExpectedType); - foreach (var arg in expr.Arguments) { - if (arg.Code != ILCode.Stloc) { - RunInference(arg); - } - } - } - - /// <summary> - /// Infers the C# type of <paramref name="expr"/>. - /// </summary> - /// <param name="expr">The expression</param> - /// <param name="expectedType">The expected type of the expression</param> - /// <param name="forceInferChildren">Whether direct children should be inferred even if its not necessary. (does not apply to nested children!)</param> - /// <returns>The inferred type</returns> - TypeReference InferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false) - { - if (expectedType != null && !IsSameType(expr.ExpectedType, expectedType)) { - expr.ExpectedType = expectedType; - if (expr.Code != ILCode.Stloc) // stloc is special case and never gets re-evaluated - forceInferChildren = true; - } - if (forceInferChildren || expr.InferredType == null) - expr.InferredType = DoInferTypeForExpression(expr, expectedType, forceInferChildren); - return expr.InferredType; - } - - TypeReference DoInferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false) - { - switch (expr.Code) { - #region Logical operators - case ILCode.LogicNot: - if (forceInferChildren) { - InferTypeForExpression(expr.Arguments.Single(), typeSystem.Boolean); - } - return typeSystem.Boolean; - case ILCode.LogicAnd: - case ILCode.LogicOr: - // if Operand is set the logic and/or expression is a custom operator - // we can deal with it the same as a normal invocation. - if (expr.Operand != null) - goto case ILCode.Call; - if (forceInferChildren) { - InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean); - InferTypeForExpression(expr.Arguments[1], typeSystem.Boolean); - } - return typeSystem.Boolean; - case ILCode.TernaryOp: - if (forceInferChildren) { - InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean); - } - return InferBinaryArguments(expr.Arguments[1], expr.Arguments[2], expectedType, forceInferChildren); - case ILCode.NullCoalescing: - return InferBinaryArguments(expr.Arguments[0], expr.Arguments[1], expectedType, forceInferChildren); - #endregion - #region Variable load/store - case ILCode.Stloc: - { - ILVariable v = (ILVariable)expr.Operand; - if (forceInferChildren) { - // do not use 'expectedType' in here! - InferTypeForExpression(expr.Arguments.Single(), v.Type); - } - return v.Type; - } - case ILCode.Ldloc: - { - ILVariable v = (ILVariable)expr.Operand; - if (v.Type == null && singleLoadVariables.Contains(v)) { - v.Type = expectedType; - } - return v.Type; - } - case ILCode.Ldloca: - { - ILVariable v = (ILVariable)expr.Operand; - if (v.Type != null) - return new ByReferenceType(v.Type); - else - return null; - } - #endregion - #region Call / NewObj - case ILCode.Call: - case ILCode.Callvirt: - case ILCode.CallGetter: - case ILCode.CallvirtGetter: - case ILCode.CallSetter: - case ILCode.CallvirtSetter: - { - MethodReference method = (MethodReference)expr.Operand; - if (forceInferChildren) { - for (int i = 0; i < expr.Arguments.Count; i++) { - if (i == 0 && method.HasThis) { - InferTypeForExpression(expr.Arguments[0], MakeRefIfValueType(method.DeclaringType, expr.GetPrefix(ILCode.Constrained))); - } else { - InferTypeForExpression(expr.Arguments[i], SubstituteTypeArgs(method.Parameters[method.HasThis ? i - 1 : i].ParameterType, method)); - } - } - } - if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) { - return SubstituteTypeArgs(method.Parameters.Last().ParameterType, method); - } else { - return SubstituteTypeArgs(method.ReturnType, method); - } - } - case ILCode.Newobj: - { - MethodReference ctor = (MethodReference)expr.Operand; - if (forceInferChildren) { - for (int i = 0; i < ctor.Parameters.Count; i++) { - InferTypeForExpression(expr.Arguments[i], SubstituteTypeArgs(ctor.Parameters[i].ParameterType, ctor)); - } - } - return ctor.DeclaringType; - } - case ILCode.InitObject: - case ILCode.InitCollection: - return InferTypeForExpression(expr.Arguments[0], expectedType); - case ILCode.InitializedObject: - // expectedType should always be known due to the parent method call / property setter - Debug.Assert(expectedType != null); - return expectedType; - #endregion - #region Load/Store Fields - case ILCode.Ldfld: - if (forceInferChildren) { - InferTypeForExpression(expr.Arguments[0], MakeRefIfValueType(((FieldReference)expr.Operand).DeclaringType, expr.GetPrefix(ILCode.Constrained))); - } - return GetFieldType((FieldReference)expr.Operand); - case ILCode.Ldsfld: - return GetFieldType((FieldReference)expr.Operand); - case ILCode.Ldflda: - if (forceInferChildren) { - InferTypeForExpression(expr.Arguments[0], MakeRefIfValueType(((FieldReference)expr.Operand).DeclaringType, expr.GetPrefix(ILCode.Constrained))); - } - return new ByReferenceType(GetFieldType((FieldReference)expr.Operand)); - case ILCode.Ldsflda: - return new ByReferenceType(GetFieldType((FieldReference)expr.Operand)); - case ILCode.Stfld: - if (forceInferChildren) { - InferTypeForExpression(expr.Arguments[0], MakeRefIfValueType(((FieldReference)expr.Operand).DeclaringType, expr.GetPrefix(ILCode.Constrained))); - InferTypeForExpression(expr.Arguments[1], GetFieldType((FieldReference)expr.Operand)); - } - return GetFieldType((FieldReference)expr.Operand); - case ILCode.Stsfld: - if (forceInferChildren) - InferTypeForExpression(expr.Arguments[0], GetFieldType((FieldReference)expr.Operand)); - return GetFieldType((FieldReference)expr.Operand); - #endregion - #region Reference/Pointer instructions - case ILCode.Ldind_Ref: - return UnpackPointer(InferTypeForExpression(expr.Arguments[0], null)); - case ILCode.Stind_Ref: - if (forceInferChildren) { - TypeReference elementType = UnpackPointer(InferTypeForExpression(expr.Arguments[0], null)); - InferTypeForExpression(expr.Arguments[1], elementType); - } - return null; - case ILCode.Ldobj: - { - TypeReference type = (TypeReference)expr.Operand; - var argType = InferTypeForExpression(expr.Arguments[0], null); - if (argType is PointerType || argType is ByReferenceType) { - var elementType = ((TypeSpecification)argType).ElementType; - int infoAmount = GetInformationAmount(elementType); - if (infoAmount == 1 && GetInformationAmount(type) == 8) { - // A bool can be loaded from both bytes and sbytes. - type = elementType; - } - if (infoAmount >= 8 && infoAmount <= 64 && infoAmount == GetInformationAmount(type)) { - // An integer can be loaded as another integer of the same size. - // For integers smaller than 32 bit, the signs must match (as loading performs sign extension) - bool? elementTypeIsSigned = IsSigned(elementType); - bool? typeIsSigned = IsSigned(type); - if (elementTypeIsSigned != null && typeIsSigned != null) { - if (infoAmount >= 32 || elementTypeIsSigned == typeIsSigned) - type = elementType; - } - } - } - if (argType is PointerType) - InferTypeForExpression(expr.Arguments[0], new PointerType(type)); - else - InferTypeForExpression(expr.Arguments[0], new ByReferenceType(type)); - return type; - } - case ILCode.Stobj: - { - TypeReference operandType = (TypeReference)expr.Operand; - TypeReference pointerType = InferTypeForExpression(expr.Arguments[0], new ByReferenceType(operandType)); - TypeReference elementType; - if (pointerType is PointerType) - elementType = ((PointerType)pointerType).ElementType; - else if (pointerType is ByReferenceType) - elementType = ((ByReferenceType)pointerType).ElementType; - else - elementType = null; - if (elementType != null) { - // An integer can be stored in any other integer of the same size. - int infoAmount = GetInformationAmount(elementType); - if (infoAmount == 1 && GetInformationAmount(operandType) == 8) - operandType = elementType; - else if (infoAmount == GetInformationAmount(operandType) && IsSigned(elementType) != null && IsSigned(operandType) != null) - operandType = elementType; - } - if (forceInferChildren) { - if (pointerType is PointerType) - InferTypeForExpression(expr.Arguments[0], new PointerType(operandType)); - else if (!IsSameType(operandType, expr.Operand as TypeReference)) - InferTypeForExpression(expr.Arguments[0], new ByReferenceType(operandType)); - InferTypeForExpression(expr.Arguments[1], operandType); - } - return operandType; - } - case ILCode.Initobj: - return null; - case ILCode.DefaultValue: - return (TypeReference)expr.Operand; - case ILCode.Localloc: - if (forceInferChildren) { - InferTypeForExpression(expr.Arguments[0], null); - } - if (expectedType is PointerType) - return expectedType; - else - return typeSystem.IntPtr; - case ILCode.Sizeof: - return typeSystem.Int32; - case ILCode.PostIncrement: - case ILCode.PostIncrement_Ovf: - case ILCode.PostIncrement_Ovf_Un: - { - TypeReference elementType = UnpackPointer(InferTypeForExpression(expr.Arguments[0], null)); - if (forceInferChildren && elementType != null) { - // Assign expected type to the child expression - InferTypeForExpression(expr.Arguments[0], new ByReferenceType(elementType)); - } - return elementType; - } - case ILCode.Mkrefany: - if (forceInferChildren) { - InferTypeForExpression(expr.Arguments[0], (TypeReference)expr.Operand); - } - return typeSystem.TypedReference; - case ILCode.Refanytype: - if (forceInferChildren) { - InferTypeForExpression(expr.Arguments[0], typeSystem.TypedReference); - } - return new TypeReference("System", "RuntimeTypeHandle", module, module.TypeSystem.Corlib, true); - case ILCode.Refanyval: - if (forceInferChildren) { - InferTypeForExpression(expr.Arguments[0], typeSystem.TypedReference); - } - return new ByReferenceType((TypeReference)expr.Operand); - case ILCode.AddressOf: - { - TypeReference t = InferTypeForExpression(expr.Arguments[0], UnpackPointer(expectedType)); - return t != null ? new ByReferenceType(t) : null; - } - case ILCode.ValueOf: - return GetNullableTypeArgument(InferTypeForExpression(expr.Arguments[0], CreateNullableType(expectedType))); - case ILCode.NullableOf: - return CreateNullableType(InferTypeForExpression(expr.Arguments[0], GetNullableTypeArgument(expectedType))); - #endregion - #region Arithmetic instructions - case ILCode.Not: // bitwise complement - case ILCode.Neg: - return InferTypeForExpression(expr.Arguments.Single(), expectedType); - case ILCode.Add: - return InferArgumentsInAddition(expr, null, expectedType); - case ILCode.Sub: - return InferArgumentsInSubtraction(expr, null, expectedType); - case ILCode.Mul: - case ILCode.Or: - case ILCode.And: - case ILCode.Xor: - return InferArgumentsInBinaryOperator(expr, null, expectedType); - case ILCode.Add_Ovf: - return InferArgumentsInAddition(expr, true, expectedType); - case ILCode.Sub_Ovf: - return InferArgumentsInSubtraction(expr, true, expectedType); - case ILCode.Mul_Ovf: - case ILCode.Div: - case ILCode.Rem: - return InferArgumentsInBinaryOperator(expr, true, expectedType); - case ILCode.Add_Ovf_Un: - return InferArgumentsInAddition(expr, false, expectedType); - case ILCode.Sub_Ovf_Un: - return InferArgumentsInSubtraction(expr, false, expectedType); - case ILCode.Mul_Ovf_Un: - case ILCode.Div_Un: - case ILCode.Rem_Un: - return InferArgumentsInBinaryOperator(expr, false, expectedType); - case ILCode.Shl: - if (forceInferChildren) - InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); - if (expectedType != null && ( - expectedType.MetadataType == MetadataType.Int32 || expectedType.MetadataType == MetadataType.UInt32 || - expectedType.MetadataType == MetadataType.Int64 || expectedType.MetadataType == MetadataType.UInt64) - ) - return NumericPromotion(InferTypeForExpression(expr.Arguments[0], expectedType)); - else - return NumericPromotion(InferTypeForExpression(expr.Arguments[0], null)); - case ILCode.Shr: - case ILCode.Shr_Un: - { - if (forceInferChildren) - InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); - TypeReference type = NumericPromotion(InferTypeForExpression(expr.Arguments[0], null)); - if (type == null) - return null; - TypeReference expectedInputType = null; - switch (type.MetadataType) { - case MetadataType.Int32: - if (expr.Code == ILCode.Shr_Un) - expectedInputType = typeSystem.UInt32; - break; - case MetadataType.UInt32: - if (expr.Code == ILCode.Shr) - expectedInputType = typeSystem.Int32; - break; - case MetadataType.Int64: - if (expr.Code == ILCode.Shr_Un) - expectedInputType = typeSystem.UInt64; - break; - case MetadataType.UInt64: - if (expr.Code == ILCode.Shr) - expectedInputType = typeSystem.UInt64; - break; - } - if (expectedInputType != null) { - InferTypeForExpression(expr.Arguments[0], expectedInputType); - return expectedInputType; - } else { - return type; - } - } - case ILCode.CompoundAssignment: - { - var op = expr.Arguments[0]; - if (op.Code == ILCode.NullableOf) op = op.Arguments[0].Arguments[0]; - var varType = InferTypeForExpression(op.Arguments[0], null); - if (forceInferChildren) { - InferTypeForExpression(expr.Arguments[0], varType); - } - return varType; - } - #endregion - #region Constant loading instructions - case ILCode.Ldnull: - return typeSystem.Object; - case ILCode.Ldstr: - return typeSystem.String; - case ILCode.Ldftn: - case ILCode.Ldvirtftn: - return typeSystem.IntPtr; - case ILCode.Ldc_I4: - if (IsBoolean(expectedType) && ((int)expr.Operand == 0 || (int)expr.Operand == 1)) - return typeSystem.Boolean; - if (expectedType is PointerType && (int)expr.Operand == 0) - return expectedType; - if (IsIntegerOrEnum(expectedType) && OperandFitsInType(expectedType, (int)expr.Operand)) - return expectedType; - else - return typeSystem.Int32; - case ILCode.Ldc_I8: - if (expectedType is PointerType && (long)expr.Operand == 0) - return expectedType; - if (IsIntegerOrEnum(expectedType) && GetInformationAmount(expectedType) >= NativeInt) - return expectedType; - else - return typeSystem.Int64; - case ILCode.Ldc_R4: - return typeSystem.Single; - case ILCode.Ldc_R8: - return typeSystem.Double; - case ILCode.Ldc_Decimal: - return new TypeReference("System", "Decimal", module, module.TypeSystem.Corlib, true); - case ILCode.Ldtoken: - if (expr.Operand is TypeReference) - return new TypeReference("System", "RuntimeTypeHandle", module, module.TypeSystem.Corlib, true); - else if (expr.Operand is FieldReference) - return new TypeReference("System", "RuntimeFieldHandle", module, module.TypeSystem.Corlib, true); - else - return new TypeReference("System", "RuntimeMethodHandle", module, module.TypeSystem.Corlib, true); - case ILCode.Arglist: - return new TypeReference("System", "RuntimeArgumentHandle", module, module.TypeSystem.Corlib, true); - #endregion - #region Array instructions - case ILCode.Newarr: - if (forceInferChildren) { - var lengthType = InferTypeForExpression(expr.Arguments.Single(), null); - if (lengthType == typeSystem.IntPtr) { - lengthType = typeSystem.Int64; - } else if (lengthType == typeSystem.UIntPtr) { - lengthType = typeSystem.UInt64; - } else if (lengthType != typeSystem.UInt32 && lengthType != typeSystem.Int64 && lengthType != typeSystem.UInt64) { - lengthType = typeSystem.Int32; - } - if (forceInferChildren) { - InferTypeForExpression(expr.Arguments.Single(), lengthType); - } - } - return new ArrayType((TypeReference)expr.Operand); - case ILCode.InitArray: - var operandAsArrayType = (ArrayType)expr.Operand; - if (forceInferChildren) - { - foreach (ILExpression arg in expr.Arguments) - InferTypeForExpression(arg, operandAsArrayType.ElementType); - } - return operandAsArrayType; - case ILCode.Ldlen: - return typeSystem.Int32; - case ILCode.Ldelem_U1: - case ILCode.Ldelem_U2: - case ILCode.Ldelem_U4: - case ILCode.Ldelem_I1: - case ILCode.Ldelem_I2: - case ILCode.Ldelem_I4: - case ILCode.Ldelem_I8: - case ILCode.Ldelem_R4: - case ILCode.Ldelem_R8: - case ILCode.Ldelem_I: - case ILCode.Ldelem_Ref: - { - ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType; - if (forceInferChildren) { - InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); - } - return arrayType != null ? arrayType.ElementType : null; - } - case ILCode.Ldelem_Any: - if (forceInferChildren) { - InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); - } - return (TypeReference)expr.Operand; - case ILCode.Ldelema: - { - ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType; - if (forceInferChildren) - InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); - return arrayType != null ? new ByReferenceType(arrayType.ElementType) : null; - } - case ILCode.Stelem_I: - case ILCode.Stelem_I1: - case ILCode.Stelem_I2: - case ILCode.Stelem_I4: - case ILCode.Stelem_I8: - case ILCode.Stelem_R4: - case ILCode.Stelem_R8: - case ILCode.Stelem_Ref: - case ILCode.Stelem_Any: - { - ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType; - if (forceInferChildren) { - InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); - if (arrayType != null) { - InferTypeForExpression(expr.Arguments[2], arrayType.ElementType); - } - } - return arrayType != null ? arrayType.ElementType : null; - } - #endregion - #region Conversion instructions - case ILCode.Conv_I1: - case ILCode.Conv_Ovf_I1: - case ILCode.Conv_Ovf_I1_Un: - return HandleConversion(8, true, expr.Arguments[0], expectedType, typeSystem.SByte); - case ILCode.Conv_I2: - case ILCode.Conv_Ovf_I2: - case ILCode.Conv_Ovf_I2_Un: - return HandleConversion(16, true, expr.Arguments[0], expectedType, typeSystem.Int16); - case ILCode.Conv_I4: - case ILCode.Conv_Ovf_I4: - case ILCode.Conv_Ovf_I4_Un: - return HandleConversion(32, true, expr.Arguments[0], expectedType, typeSystem.Int32); - case ILCode.Conv_I8: - case ILCode.Conv_Ovf_I8: - case ILCode.Conv_Ovf_I8_Un: - return HandleConversion(64, true, expr.Arguments[0], expectedType, typeSystem.Int64); - case ILCode.Conv_U1: - case ILCode.Conv_Ovf_U1: - case ILCode.Conv_Ovf_U1_Un: - return HandleConversion(8, false, expr.Arguments[0], expectedType, typeSystem.Byte); - case ILCode.Conv_U2: - case ILCode.Conv_Ovf_U2: - case ILCode.Conv_Ovf_U2_Un: - return HandleConversion(16, false, expr.Arguments[0], expectedType, typeSystem.UInt16); - case ILCode.Conv_U4: - case ILCode.Conv_Ovf_U4: - case ILCode.Conv_Ovf_U4_Un: - return HandleConversion(32, false, expr.Arguments[0], expectedType, typeSystem.UInt32); - case ILCode.Conv_U8: - case ILCode.Conv_Ovf_U8: - case ILCode.Conv_Ovf_U8_Un: - return HandleConversion(64, false, expr.Arguments[0], expectedType, typeSystem.UInt64); - case ILCode.Conv_I: - case ILCode.Conv_Ovf_I: - case ILCode.Conv_Ovf_I_Un: - return HandleConversion(NativeInt, true, expr.Arguments[0], expectedType, typeSystem.IntPtr); - case ILCode.Conv_U: - case ILCode.Conv_Ovf_U: - case ILCode.Conv_Ovf_U_Un: - return HandleConversion(NativeInt, false, expr.Arguments[0], expectedType, typeSystem.UIntPtr); - case ILCode.Conv_R4: - if (forceInferChildren) { - InferTypeForExpression(expr.Arguments[0], typeSystem.Single); - } - return typeSystem.Single; - case ILCode.Conv_R8: - if (forceInferChildren) { - InferTypeForExpression(expr.Arguments[0], typeSystem.Double); - } - return typeSystem.Double; - case ILCode.Conv_R_Un: - return (expectedType != null && expectedType.MetadataType == MetadataType.Single) ? typeSystem.Single : typeSystem.Double; - case ILCode.Castclass: - case ILCode.Unbox_Any: - return (TypeReference)expr.Operand; - case ILCode.Unbox: - return new ByReferenceType((TypeReference)expr.Operand); - case ILCode.Isinst: - { - // isinst performs the equivalent of a cast only for reference types; - // value types still need to be unboxed after an isinst instruction - TypeReference tr = (TypeReference)expr.Operand; - return tr.IsValueType ? typeSystem.Object : tr; - } - case ILCode.Box: - { - var tr = (TypeReference)expr.Operand; - if (forceInferChildren) - InferTypeForExpression(expr.Arguments.Single(), tr); - return tr.IsValueType ? typeSystem.Object : tr; - } - #endregion - #region Comparison instructions - case ILCode.Ceq: - case ILCode.Cne: - if (forceInferChildren) - InferArgumentsInBinaryOperator(expr, null, null); - return typeSystem.Boolean; - case ILCode.Clt: - case ILCode.Cgt: - case ILCode.Cle: - case ILCode.Cge: - if (forceInferChildren) - InferArgumentsInBinaryOperator(expr, true, null); - return typeSystem.Boolean; - case ILCode.Clt_Un: - case ILCode.Cgt_Un: - case ILCode.Cle_Un: - case ILCode.Cge_Un: - if (forceInferChildren) - InferArgumentsInBinaryOperator(expr, false, null); - return typeSystem.Boolean; - #endregion - #region Branch instructions - case ILCode.Brtrue: - if (forceInferChildren) - InferTypeForExpression(expr.Arguments.Single(), typeSystem.Boolean); - return null; - case ILCode.Br: - case ILCode.Leave: - case ILCode.Endfinally: - case ILCode.Switch: - case ILCode.Throw: - case ILCode.Rethrow: - case ILCode.LoopOrSwitchBreak: - case ILCode.LoopContinue: - case ILCode.YieldBreak: - return null; - case ILCode.Ret: - if (forceInferChildren && expr.Arguments.Count == 1) { - TypeReference returnType = context.CurrentMethod.ReturnType; - if (context.CurrentMethodIsAsync && returnType != null && returnType.Namespace == "System.Threading.Tasks") { - if (returnType.Name == "Task") { - returnType = typeSystem.Void; - } else if (returnType.Name == "Task`1" && returnType.IsGenericInstance) { - returnType = ((GenericInstanceType)returnType).GenericArguments[0]; - } - } - InferTypeForExpression(expr.Arguments[0], returnType); - } - return null; - case ILCode.YieldReturn: - if (forceInferChildren) { - GenericInstanceType genericType = context.CurrentMethod.ReturnType as GenericInstanceType; - if (genericType != null) { // IEnumerable<T> or IEnumerator<T> - InferTypeForExpression(expr.Arguments[0], genericType.GenericArguments[0]); - } else { // non-generic IEnumerable or IEnumerator - InferTypeForExpression(expr.Arguments[0], typeSystem.Object); - } - } - return null; - case ILCode.Await: - { - TypeReference taskType = InferTypeForExpression(expr.Arguments[0], null); - if (taskType != null && taskType.Name == "Task`1" && taskType.IsGenericInstance && taskType.Namespace == "System.Threading.Tasks") { - return ((GenericInstanceType)taskType).GenericArguments[0]; - } - return null; - } - #endregion - case ILCode.Pop: - return null; - case ILCode.Wrap: - case ILCode.Dup: - { - var arg = expr.Arguments.Single(); - return arg.ExpectedType = InferTypeForExpression(arg, expectedType); - } - default: - Debug.WriteLine("Type Inference: Can't handle " + expr.Code.GetName()); - return null; - } - } - - /// <summary> - /// Wraps 'type' in a ByReferenceType if it is a value type. If a constrained prefix is specified, - /// returns the constrained type wrapped in a ByReferenceType. - /// </summary> - TypeReference MakeRefIfValueType(TypeReference type, ILExpressionPrefix constrainedPrefix) - { - if (constrainedPrefix != null) - return new ByReferenceType((TypeReference)constrainedPrefix.Operand); - if (type.IsValueType) - return new ByReferenceType(type); - else - return type; - } - - /// <summary> - /// Promotes primitive types smaller than int32 to int32. - /// </summary> - /// <remarks> - /// Always promotes to signed int32. - /// </remarks> - TypeReference NumericPromotion(TypeReference type) - { - if (type == null) - return null; - switch (type.MetadataType) { - case MetadataType.SByte: - case MetadataType.Int16: - case MetadataType.Byte: - case MetadataType.UInt16: - return typeSystem.Int32; - default: - return type; - } - } - - TypeReference HandleConversion(int targetBitSize, bool targetSigned, ILExpression arg, TypeReference expectedType, TypeReference targetType) - { - if (targetBitSize >= NativeInt && expectedType is PointerType) { - InferTypeForExpression(arg, expectedType); - return expectedType; - } - TypeReference argType = InferTypeForExpression(arg, null); - if (targetBitSize >= NativeInt && argType is ByReferenceType) { - // conv instructions on managed references mean that the GC should stop tracking them, so they become pointers: - PointerType ptrType = new PointerType(((ByReferenceType)argType).ElementType); - InferTypeForExpression(arg, ptrType); - return ptrType; - } else if (targetBitSize >= NativeInt && argType is PointerType) { - return argType; - } - TypeReference resultType = (GetInformationAmount(expectedType) == targetBitSize && IsSigned(expectedType) == targetSigned) ? expectedType : targetType; - arg.ExpectedType = resultType; // store the expected type in the argument so that AstMethodBodyBuilder will insert a cast - return resultType; - } - - public static TypeReference GetFieldType(FieldReference fieldReference) - { - return SubstituteTypeArgs(UnpackModifiers(fieldReference.FieldType), fieldReference); - } - - public static TypeReference SubstituteTypeArgs(TypeReference type, MemberReference member) - { - if (type is TypeSpecification) { - ArrayType arrayType = type as ArrayType; - if (arrayType != null) { - TypeReference elementType = SubstituteTypeArgs(arrayType.ElementType, member); - if (elementType != arrayType.ElementType) { - ArrayType newArrayType = new ArrayType(elementType); - newArrayType.Dimensions.Clear(); // remove the single dimension that Cecil adds by default - foreach (ArrayDimension d in arrayType.Dimensions) - newArrayType.Dimensions.Add(d); - return newArrayType; - } else { - return type; - } - } - ByReferenceType refType = type as ByReferenceType; - if (refType != null) { - TypeReference elementType = SubstituteTypeArgs(refType.ElementType, member); - return elementType != refType.ElementType ? new ByReferenceType(elementType) : type; - } - GenericInstanceType giType = type as GenericInstanceType; - if (giType != null) { - GenericInstanceType newType = new GenericInstanceType(giType.ElementType); - bool isChanged = false; - for (int i = 0; i < giType.GenericArguments.Count; i++) { - newType.GenericArguments.Add(SubstituteTypeArgs(giType.GenericArguments[i], member)); - isChanged |= newType.GenericArguments[i] != giType.GenericArguments[i]; - } - return isChanged ? newType : type; - } - OptionalModifierType optmodType = type as OptionalModifierType; - if (optmodType != null) { - TypeReference elementType = SubstituteTypeArgs(optmodType.ElementType, member); - return elementType != optmodType.ElementType ? new OptionalModifierType(optmodType.ModifierType, elementType) : type; - } - RequiredModifierType reqmodType = type as RequiredModifierType; - if (reqmodType != null) { - TypeReference elementType = SubstituteTypeArgs(reqmodType.ElementType, member); - return elementType != reqmodType.ElementType ? new RequiredModifierType(reqmodType.ModifierType, elementType) : type; - } - PointerType ptrType = type as PointerType; - if (ptrType != null) { - TypeReference elementType = SubstituteTypeArgs(ptrType.ElementType, member); - return elementType != ptrType.ElementType ? new PointerType(elementType) : type; - } - } - GenericParameter gp = type as GenericParameter; - if (gp != null) { - if (member.DeclaringType is ArrayType) { - return ((ArrayType)member.DeclaringType).ElementType; - } else if (gp.Owner.GenericParameterType == GenericParameterType.Method) { - return ((GenericInstanceMethod)member).GenericArguments[gp.Position]; - } else { - return ((GenericInstanceType)member.DeclaringType).GenericArguments[gp.Position]; - } - } - return type; - } - - static TypeReference UnpackPointer(TypeReference pointerOrManagedReference) - { - ByReferenceType refType = pointerOrManagedReference as ByReferenceType; - if (refType != null) - return refType.ElementType; - PointerType ptrType = pointerOrManagedReference as PointerType; - if (ptrType != null) - return ptrType.ElementType; - return null; - } - - internal static TypeReference UnpackModifiers(TypeReference type) - { - while (type is OptionalModifierType || type is RequiredModifierType) - type = ((TypeSpecification)type).ElementType; - return type; - } - - static TypeReference GetNullableTypeArgument(TypeReference type) - { - var t = type as GenericInstanceType; - return IsNullableType(t) ? t.GenericArguments[0] : type; - } - - GenericInstanceType CreateNullableType(TypeReference type) - { - if (type == null) return null; - var t = new GenericInstanceType(new TypeReference("System", "Nullable`1", module, module.TypeSystem.Corlib, true)); - t.GenericArguments.Add(type); - return t; - } - - TypeReference InferArgumentsInBinaryOperator(ILExpression expr, bool? isSigned, TypeReference expectedType) - { - return InferBinaryArguments(expr.Arguments[0], expr.Arguments[1], expectedType); - } - - TypeReference InferArgumentsInAddition(ILExpression expr, bool? isSigned, TypeReference expectedType) - { - ILExpression left = expr.Arguments[0]; - ILExpression right = expr.Arguments[1]; - TypeReference leftPreferred = DoInferTypeForExpression(left, expectedType); - if (leftPreferred is PointerType) { - left.InferredType = left.ExpectedType = leftPreferred; - InferTypeForExpression(right, null); - return leftPreferred; - } - if (IsEnum(leftPreferred)) { - //E+U=E - left.InferredType = left.ExpectedType = leftPreferred; - InferTypeForExpression(right, GetEnumUnderlyingType(leftPreferred)); - return leftPreferred; - } - TypeReference rightPreferred = DoInferTypeForExpression(right, expectedType); - if (rightPreferred is PointerType) { - InferTypeForExpression(left, null); - right.InferredType = right.ExpectedType = rightPreferred; - return rightPreferred; - } - if (IsEnum(rightPreferred)) { - //U+E=E - right.InferredType = right.ExpectedType = rightPreferred; - InferTypeForExpression(left, GetEnumUnderlyingType(rightPreferred)); - return rightPreferred; - } - return InferBinaryArguments(left, right, expectedType, leftPreferred: leftPreferred, rightPreferred: rightPreferred); - } - - TypeReference InferArgumentsInSubtraction(ILExpression expr, bool? isSigned, TypeReference expectedType) - { - ILExpression left = expr.Arguments[0]; - ILExpression right = expr.Arguments[1]; - TypeReference leftPreferred = DoInferTypeForExpression(left, expectedType); - if (leftPreferred is PointerType) { - left.InferredType = left.ExpectedType = leftPreferred; - TypeReference rightPreferred = InferTypeForExpression(right, null); - // subtracting two pointers is not a pointer - if (rightPreferred is PointerType) - return typeSystem.IntPtr; - return leftPreferred; - } - if (IsEnum(leftPreferred)) { - if (expectedType != null && IsEnum(expectedType)) { - // E-U=E - left.InferredType = left.ExpectedType = leftPreferred; - InferTypeForExpression(right, GetEnumUnderlyingType(leftPreferred)); - return leftPreferred; - } else { - // E-E=U - left.InferredType = left.ExpectedType = leftPreferred; - InferTypeForExpression(right, leftPreferred); - return GetEnumUnderlyingType(leftPreferred); - } - } - return InferBinaryArguments(left, right, expectedType, leftPreferred: leftPreferred); - } - - TypeReference InferBinaryArguments(ILExpression left, ILExpression right, TypeReference expectedType, bool forceInferChildren = false, TypeReference leftPreferred = null, TypeReference rightPreferred = null) - { - if (leftPreferred == null) leftPreferred = DoInferTypeForExpression(left, expectedType, forceInferChildren); - if (rightPreferred == null) rightPreferred = DoInferTypeForExpression(right, expectedType, forceInferChildren); - if (IsSameType(leftPreferred, rightPreferred)) { - return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = leftPreferred; - } else if (IsSameType(rightPreferred, DoInferTypeForExpression(left, rightPreferred, forceInferChildren))) { - return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = rightPreferred; - } else if (IsSameType(leftPreferred, DoInferTypeForExpression(right, leftPreferred, forceInferChildren))) { - // re-infer the left expression with the preferred type to reset any conflicts caused by the rightPreferred type - DoInferTypeForExpression(left, leftPreferred, forceInferChildren); - return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = leftPreferred; - } else { - left.ExpectedType = right.ExpectedType = TypeWithMoreInformation(leftPreferred, rightPreferred); - left.InferredType = DoInferTypeForExpression(left, left.ExpectedType, forceInferChildren); - right.InferredType = DoInferTypeForExpression(right, right.ExpectedType, forceInferChildren); - return left.ExpectedType; - } - } - - TypeReference TypeWithMoreInformation(TypeReference leftPreferred, TypeReference rightPreferred) - { - int left = GetInformationAmount(leftPreferred); - int right = GetInformationAmount(rightPreferred); - if (left < right) { - return rightPreferred; - } else if (left > right) { - return leftPreferred; - } else { - // TODO - return leftPreferred; - } - } - - /// <summary> - /// Information amount used for IntPtr. - /// </summary> - public const int NativeInt = 33; // treat native int as between int32 and int64 - - /// <summary> - /// Gets the underlying type, if the specified type is an enum. - /// Otherwise, returns null. - /// </summary> - public static TypeReference GetEnumUnderlyingType(TypeReference enumType) - { - // unfortunately we cannot rely on enumType.IsValueType here - it's not set when the instruction operand is a typeref (as opposed to a typespec) - if (enumType != null && !IsArrayPointerOrReference(enumType)) { - // value type might be an enum - TypeDefinition typeDef = enumType.Resolve() as TypeDefinition; - if (typeDef != null && typeDef.IsEnum) { - return typeDef.Fields.Single(f => !f.IsStatic).FieldType; - } - } - return null; - } - - public static int GetInformationAmount(TypeReference type) - { - type = GetEnumUnderlyingType(type) ?? type; - if (type == null) - return 0; - switch (type.MetadataType) { - case MetadataType.Void: - return 0; - case MetadataType.Boolean: - return 1; - case MetadataType.SByte: - case MetadataType.Byte: - return 8; - case MetadataType.Char: - case MetadataType.Int16: - case MetadataType.UInt16: - return 16; - case MetadataType.Int32: - case MetadataType.UInt32: - case MetadataType.Single: - return 32; - case MetadataType.Int64: - case MetadataType.UInt64: - case MetadataType.Double: - return 64; - case MetadataType.IntPtr: - case MetadataType.UIntPtr: - return NativeInt; - default: - return 100; // we consider structs/objects to have more information than any primitives - } - } - - public static bool IsBoolean(TypeReference type) - { - return type != null && type.MetadataType == MetadataType.Boolean; - } - - public static bool IsIntegerOrEnum(TypeReference type) - { - return IsSigned(type) != null; - } - - public static bool IsEnum(TypeReference type) - { - // Arrays/Pointers/ByReference resolve to their element type, but we don't want to consider those to be enums - // However, GenericInstanceTypes, ModOpts etc. should be considered enums. - if (type == null || IsArrayPointerOrReference(type)) - return false; - // unfortunately we cannot rely on type.IsValueType here - it's not set when the instruction operand is a typeref (as opposed to a typespec) - TypeDefinition typeDef = type.Resolve() as TypeDefinition; - return typeDef != null && typeDef.IsEnum; - } - - static bool? IsSigned(TypeReference type) - { - type = GetEnumUnderlyingType(type) ?? type; - if (type == null) - return null; - switch (type.MetadataType) { - case MetadataType.SByte: - case MetadataType.Int16: - case MetadataType.Int32: - case MetadataType.Int64: - case MetadataType.IntPtr: - return true; - case MetadataType.Byte: - case MetadataType.Char: - case MetadataType.UInt16: - case MetadataType.UInt32: - case MetadataType.UInt64: - case MetadataType.UIntPtr: - return false; - default: - return null; - } - } - - static bool OperandFitsInType(TypeReference type, int num) - { - type = GetEnumUnderlyingType(type) ?? type; - switch (type.MetadataType) { - case MetadataType.SByte: - return sbyte.MinValue <= num && num <= sbyte.MaxValue; - case MetadataType.Int16: - return short.MinValue <= num && num <= short.MaxValue; - case MetadataType.Byte: - return byte.MinValue <= num && num <= byte.MaxValue; - case MetadataType.Char: - return char.MinValue <= num && num <= char.MaxValue; - case MetadataType.UInt16: - return ushort.MinValue <= num && num <= ushort.MaxValue; - default: - return true; - } - } - - static bool IsArrayPointerOrReference(TypeReference type) - { - TypeSpecification typeSpec = type as TypeSpecification; - while (typeSpec != null) { - if (typeSpec is ArrayType || typeSpec is PointerType || typeSpec is ByReferenceType) - return true; - typeSpec = typeSpec.ElementType as TypeSpecification; - } - return false; - } - - internal static bool IsNullableType(TypeReference type) - { - return type != null && type.Name == "Nullable`1" && type.Namespace == "System"; - } - - public static TypeCode GetTypeCode(TypeReference type) - { - if (type == null) - return TypeCode.Empty; - switch (type.MetadataType) { - case MetadataType.Boolean: - return TypeCode.Boolean; - case MetadataType.Char: - return TypeCode.Char; - case MetadataType.SByte: - return TypeCode.SByte; - case MetadataType.Byte: - return TypeCode.Byte; - case MetadataType.Int16: - return TypeCode.Int16; - case MetadataType.UInt16: - return TypeCode.UInt16; - case MetadataType.Int32: - return TypeCode.Int32; - case MetadataType.UInt32: - return TypeCode.UInt32; - case MetadataType.Int64: - return TypeCode.Int64; - case MetadataType.UInt64: - return TypeCode.UInt64; - case MetadataType.Single: - return TypeCode.Single; - case MetadataType.Double: - return TypeCode.Double; - case MetadataType.String: - return TypeCode.String; - default: - return TypeCode.Object; - } - } - - /// <summary> - /// Clears the type inference data on the method. - /// </summary> - public static void Reset(ILBlock method) - { - foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) { - expr.InferredType = null; - expr.ExpectedType = null; - ILVariable v = expr.Operand as ILVariable; - if (v != null && v.IsGenerated) - v.Type = null; - } - } - - public static bool IsSameType(TypeReference type1, TypeReference type2) - { - if (type1 == type2) - return true; - if (type1 == null || type2 == null) - return false; - return type1.FullName == type2.FullName; // TODO: implement this more efficiently? - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs deleted file mode 100644 index 0c5c045c1a..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs +++ /dev/null @@ -1,636 +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.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.ILAst -{ - class YieldReturnDecompiler - { - // For a description on the code generated by the C# compiler for yield return: - // http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx - - // The idea here is: - // - Figure out whether the current method is instanciating an enumerator - // - Figure out which of the fields is the state field - // - Construct an exception table based on states. This allows us to determine, for each state, what the parent try block is. - - // See http://community.sharpdevelop.net/blogs/danielgrunwald/archive/2011/03/06/ilspy-yield-return.aspx - // for a description of this step. - - DecompilerContext context; - TypeDefinition enumeratorType; - MethodDefinition enumeratorCtor; - MethodDefinition disposeMethod; - FieldDefinition stateField; - FieldDefinition currentField; - Dictionary<FieldDefinition, ILVariable> fieldToParameterMap = new Dictionary<FieldDefinition, ILVariable>(); - List<ILNode> newBody; - - #region Run() method - public static void Run(DecompilerContext context, ILBlock method) - { - if (!context.Settings.YieldReturn) - return; // abort if enumerator decompilation is disabled - var yrd = new YieldReturnDecompiler(); - yrd.context = context; - if (!yrd.MatchEnumeratorCreationPattern(method)) - return; - yrd.enumeratorType = yrd.enumeratorCtor.DeclaringType; - #if DEBUG - if (Debugger.IsAttached) { - yrd.Run(); - } else { - #endif - try { - yrd.Run(); - } catch (SymbolicAnalysisFailedException) { - return; - } - #if DEBUG - } - #endif - method.Body.Clear(); - method.EntryGoto = null; - method.Body.AddRange(yrd.newBody); - - // Repeat the inlining/copy propagation optimization because the conversion of field access - // to local variables can open up additional inlining possibilities. - ILInlining inlining = new ILInlining(method); - inlining.InlineAllVariables(); - inlining.CopyPropagation(); - } - - void Run() - { - AnalyzeCtor(); - AnalyzeCurrentProperty(); - ResolveIEnumerableIEnumeratorFieldMapping(); - ConstructExceptionTable(); - AnalyzeMoveNext(); - TranslateFieldsToLocalAccess(); - } - #endregion - - #region Match the enumerator creation pattern - bool MatchEnumeratorCreationPattern(ILBlock method) - { - if (method.Body.Count == 0) - return false; - ILExpression newObj; - if (method.Body.Count == 1) { - // ret(newobj(...)) - if (method.Body[0].Match(ILCode.Ret, out newObj)) - return MatchEnumeratorCreationNewObj(newObj, out enumeratorCtor); - else - return false; - } - // stloc(var_1, newobj(..) - ILVariable var1; - if (!method.Body[0].Match(ILCode.Stloc, out var1, out newObj)) - return false; - if (!MatchEnumeratorCreationNewObj(newObj, out enumeratorCtor)) - return false; - - int i; - for (i = 1; i < method.Body.Count; i++) { - // stfld(..., ldloc(var_1), ldloc(parameter)) - FieldReference storedField; - ILExpression ldloc, loadParameter; - if (!method.Body[i].Match(ILCode.Stfld, out storedField, out ldloc, out loadParameter)) - break; - ILVariable loadedVar, loadedArg; - if (!ldloc.Match(ILCode.Ldloc, out loadedVar) || !loadParameter.Match(ILCode.Ldloc, out loadedArg)) - return false; - storedField = GetFieldDefinition(storedField); - if (loadedVar != var1 || storedField == null || !loadedArg.IsParameter) - return false; - fieldToParameterMap[(FieldDefinition)storedField] = loadedArg; - } - ILVariable var2; - ILExpression ldlocForStloc2; - if (i < method.Body.Count && method.Body[i].Match(ILCode.Stloc, out var2, out ldlocForStloc2)) { - // stloc(var_2, ldloc(var_1)) - if (ldlocForStloc2.Code != ILCode.Ldloc || ldlocForStloc2.Operand != var1) - return false; - i++; - } else { - // the compiler might skip the above instruction in release builds; in that case, it directly returns stloc.Operand - var2 = var1; - } - ILExpression retArg; - if (i < method.Body.Count && method.Body[i].Match(ILCode.Ret, out retArg)) { - // ret(ldloc(var_2)) - if (retArg.Code == ILCode.Ldloc && retArg.Operand == var2) { - return true; - } - } - return false; - } - - static FieldDefinition GetFieldDefinition(FieldReference field) - { - return CecilExtensions.ResolveWithinSameModule(field); - } - - static MethodDefinition GetMethodDefinition(MethodReference method) - { - return CecilExtensions.ResolveWithinSameModule(method); - } - - bool MatchEnumeratorCreationNewObj(ILExpression expr, out MethodDefinition ctor) - { - // newobj(CurrentType/...::.ctor, ldc.i4(-2)) - ctor = null; - if (expr.Code != ILCode.Newobj || expr.Arguments.Count != 1) - return false; - if (expr.Arguments[0].Code != ILCode.Ldc_I4) - return false; - int initialState = (int)expr.Arguments[0].Operand; - if (!(initialState == -2 || initialState == 0)) - return false; - ctor = GetMethodDefinition(expr.Operand as MethodReference); - if (ctor == null || ctor.DeclaringType.DeclaringType != context.CurrentType) - return false; - return IsCompilerGeneratorEnumerator(ctor.DeclaringType); - } - - public static bool IsCompilerGeneratorEnumerator(TypeDefinition type) - { - if (!(type.DeclaringType != null && type.IsCompilerGenerated())) - return false; - foreach (InterfaceImplementation ii in type.Interfaces) { - var i = ii.InterfaceType; - if (i.Namespace == "System.Collections" && i.Name == "IEnumerator") - return true; - } - return false; - } - #endregion - - #region Figure out what the 'state' field is (analysis of .ctor()) - /// <summary> - /// Looks at the enumerator's ctor and figures out which of the fields holds the state. - /// </summary> - void AnalyzeCtor() - { - ILBlock method = CreateILAst(enumeratorCtor); - - foreach (ILNode node in method.Body) { - FieldReference field; - ILExpression instExpr; - ILExpression stExpr; - ILVariable arg; - if (node.Match(ILCode.Stfld, out field, out instExpr, out stExpr) && - instExpr.MatchThis() && - stExpr.Match(ILCode.Ldloc, out arg) && - arg.IsParameter && arg.OriginalParameter.Index == 0) - { - stateField = GetFieldDefinition(field); - } - } - if (stateField == null) - throw new SymbolicAnalysisFailedException(); - } - - /// <summary> - /// Creates ILAst for the specified method, optimized up to before the 'YieldReturn' step. - /// </summary> - ILBlock CreateILAst(MethodDefinition method) - { - if (method == null || !method.HasBody) - throw new SymbolicAnalysisFailedException(); - - ILBlock ilMethod = new ILBlock(); - ILAstBuilder astBuilder = new ILAstBuilder(); - ilMethod.Body = astBuilder.Build(method, true, context); - ILAstOptimizer optimizer = new ILAstOptimizer(); - optimizer.Optimize(context, ilMethod, ILAstOptimizationStep.YieldReturn); - return ilMethod; - } - #endregion - - #region Figure out what the 'current' field is (analysis of get_Current()) - /// <summary> - /// Looks at the enumerator's get_Current method and figures out which of the fields holds the current value. - /// </summary> - void AnalyzeCurrentProperty() - { - MethodDefinition getCurrentMethod = enumeratorType.Methods.FirstOrDefault( - m => m.Name.StartsWith("System.Collections.Generic.IEnumerator", StringComparison.Ordinal) - && m.Name.EndsWith(".get_Current", StringComparison.Ordinal)); - ILBlock method = CreateILAst(getCurrentMethod); - if (method.Body.Count == 1) { - // release builds directly return the current field - ILExpression retExpr; - FieldReference field; - ILExpression ldFromObj; - if (method.Body[0].Match(ILCode.Ret, out retExpr) && - retExpr.Match(ILCode.Ldfld, out field, out ldFromObj) && - ldFromObj.MatchThis()) - { - currentField = GetFieldDefinition(field); - } - } else if (method.Body.Count == 2) { - ILVariable v, v2; - ILExpression stExpr; - FieldReference field; - ILExpression ldFromObj; - ILExpression retExpr; - if (method.Body[0].Match(ILCode.Stloc, out v, out stExpr) && - stExpr.Match(ILCode.Ldfld, out field, out ldFromObj) && - ldFromObj.MatchThis() && - method.Body[1].Match(ILCode.Ret, out retExpr) && - retExpr.Match(ILCode.Ldloc, out v2) && - v == v2) - { - currentField = GetFieldDefinition(field); - } - } - if (currentField == null) - throw new SymbolicAnalysisFailedException(); - } - #endregion - - #region Figure out the mapping of IEnumerable fields to IEnumerator fields (analysis of GetEnumerator()) - void ResolveIEnumerableIEnumeratorFieldMapping() - { - MethodDefinition getEnumeratorMethod = enumeratorType.Methods.FirstOrDefault( - m => m.Name.StartsWith("System.Collections.Generic.IEnumerable", StringComparison.Ordinal) - && m.Name.EndsWith(".GetEnumerator", StringComparison.Ordinal)); - if (getEnumeratorMethod == null) - return; // no mappings (maybe it's just an IEnumerator implementation?) - - ILBlock method = CreateILAst(getEnumeratorMethod); - foreach (ILNode node in method.Body) { - FieldReference stField; - ILExpression stToObj; - ILExpression stExpr; - FieldReference ldField; - ILExpression ldFromObj; - if (node.Match(ILCode.Stfld, out stField, out stToObj, out stExpr) && - stExpr.Match(ILCode.Ldfld, out ldField, out ldFromObj) && - ldFromObj.MatchThis()) - { - FieldDefinition storedField = GetFieldDefinition(stField); - FieldDefinition loadedField = GetFieldDefinition(ldField); - if (storedField != null && loadedField != null) { - ILVariable mappedParameter; - if (fieldToParameterMap.TryGetValue(loadedField, out mappedParameter)) - fieldToParameterMap[storedField] = mappedParameter; - } - } - } - } - #endregion - - #region Construction of the exception table (analysis of Dispose()) - // We construct the exception table by analyzing the enumerator's Dispose() method. - - // Assumption: there are no loops/backward jumps - // We 'run' the code, with "state" being a symbolic variable - // so it can form expressions like "state + x" (when there's a sub instruction) - // For each instruction, we maintain a list of value ranges for state for which the instruction is reachable. - // This is (int.MinValue, int.MaxValue) for the first instruction. - // These ranges are propagated depending on the conditional jumps performed by the code. - - Dictionary<MethodDefinition, StateRange> finallyMethodToStateRange; - - void ConstructExceptionTable() - { - disposeMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "System.IDisposable.Dispose"); - ILBlock ilMethod = CreateILAst(disposeMethod); - - var rangeAnalysis = new StateRangeAnalysis(ilMethod.Body[0], StateRangeAnalysisMode.IteratorDispose, stateField); - rangeAnalysis.AssignStateRanges(ilMethod.Body, ilMethod.Body.Count); - finallyMethodToStateRange = rangeAnalysis.finallyMethodToStateRange; - - // Now look at the finally blocks: - foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive<ILTryCatchBlock>()) { - var range = rangeAnalysis.ranges[tryFinally.TryBlock.Body[0]]; - var finallyBody = tryFinally.FinallyBlock.Body; - if (finallyBody.Count != 2) - throw new SymbolicAnalysisFailedException(); - ILExpression call = finallyBody[0] as ILExpression; - if (call == null || call.Code != ILCode.Call || call.Arguments.Count != 1) - throw new SymbolicAnalysisFailedException(); - if (!call.Arguments[0].MatchThis()) - throw new SymbolicAnalysisFailedException(); - if (!finallyBody[1].Match(ILCode.Endfinally)) - throw new SymbolicAnalysisFailedException(); - - MethodDefinition mdef = GetMethodDefinition(call.Operand as MethodReference); - if (mdef == null || finallyMethodToStateRange.ContainsKey(mdef)) - throw new SymbolicAnalysisFailedException(); - finallyMethodToStateRange.Add(mdef, range); - } - rangeAnalysis = null; - } - #endregion - - #region Analysis of MoveNext() - ILVariable returnVariable; - ILLabel returnLabel; - ILLabel returnFalseLabel; - - void AnalyzeMoveNext() - { - MethodDefinition moveNextMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "MoveNext"); - ILBlock ilMethod = CreateILAst(moveNextMethod); - - if (ilMethod.Body.Count == 0) - throw new SymbolicAnalysisFailedException(); - ILExpression lastReturnArg; - if (!ilMethod.Body.Last().Match(ILCode.Ret, out lastReturnArg)) - throw new SymbolicAnalysisFailedException(); - - // There are two possibilities: - if (lastReturnArg.Code == ILCode.Ldloc) { - // a) the compiler uses a variable for returns (in debug builds, or when there are try-finally blocks) - returnVariable = (ILVariable)lastReturnArg.Operand; - returnLabel = ilMethod.Body.ElementAtOrDefault(ilMethod.Body.Count - 2) as ILLabel; - if (returnLabel == null) - throw new SymbolicAnalysisFailedException(); - } else { - // b) the compiler directly returns constants - returnVariable = null; - returnLabel = null; - // In this case, the last return must return false. - if (lastReturnArg.Code != ILCode.Ldc_I4 || (int)lastReturnArg.Operand != 0) - throw new SymbolicAnalysisFailedException(); - } - - ILTryCatchBlock tryFaultBlock = ilMethod.Body[0] as ILTryCatchBlock; - List<ILNode> body; - int bodyLength; - if (tryFaultBlock != null) { - // there are try-finally blocks - if (returnVariable == null) // in this case, we must use a return variable - throw new SymbolicAnalysisFailedException(); - // must be a try-fault block: - if (tryFaultBlock.CatchBlocks.Count != 0 || tryFaultBlock.FinallyBlock != null || tryFaultBlock.FaultBlock == null) - throw new SymbolicAnalysisFailedException(); - - ILBlock faultBlock = tryFaultBlock.FaultBlock; - // Ensure the fault block contains the call to Dispose(). - if (faultBlock.Body.Count != 2) - throw new SymbolicAnalysisFailedException(); - MethodReference disposeMethodRef; - ILExpression disposeArg; - if (!faultBlock.Body[0].Match(ILCode.Call, out disposeMethodRef, out disposeArg)) - throw new SymbolicAnalysisFailedException(); - if (GetMethodDefinition(disposeMethodRef) != disposeMethod || !disposeArg.MatchThis()) - throw new SymbolicAnalysisFailedException(); - if (!faultBlock.Body[1].Match(ILCode.Endfinally)) - throw new SymbolicAnalysisFailedException(); - - body = tryFaultBlock.TryBlock.Body; - bodyLength = body.Count; - } else { - // no try-finally blocks - body = ilMethod.Body; - if (returnVariable == null) - bodyLength = body.Count - 1; // all except for the return statement - else - bodyLength = body.Count - 2; // all except for the return label and statement - } - - // Now verify that the last instruction in the body is 'ret(false)' - if (returnVariable != null) { - // If we don't have a return variable, we already verified that above. - // If we do have one, check for 'stloc(returnVariable, ldc.i4(0))' - - // Maybe might be a jump to the return label after the stloc: - ILExpression leave = body.ElementAtOrDefault(bodyLength - 1) as ILExpression; - if (leave != null && (leave.Code == ILCode.Br || leave.Code == ILCode.Leave) && leave.Operand == returnLabel) - bodyLength--; - ILExpression store0 = body.ElementAtOrDefault(bodyLength - 1) as ILExpression; - if (store0 == null || store0.Code != ILCode.Stloc || store0.Operand != returnVariable) - throw new SymbolicAnalysisFailedException(); - if (store0.Arguments[0].Code != ILCode.Ldc_I4 || (int)store0.Arguments[0].Operand != 0) - throw new SymbolicAnalysisFailedException(); - - bodyLength--; // don't conside the stloc instruction to be part of the body - } - // The last element in the body usually is a label pointing to the 'ret(false)' - returnFalseLabel = body.ElementAtOrDefault(bodyLength - 1) as ILLabel; - // Note: in Roslyn-compiled code, returnFalseLabel may be null. - - var rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.IteratorMoveNext, stateField); - int pos = rangeAnalysis.AssignStateRanges(body, bodyLength); - rangeAnalysis.EnsureLabelAtPos(body, ref pos, ref bodyLength); - - var labels = rangeAnalysis.CreateLabelRangeMapping(body, pos, bodyLength); - ConvertBody(body, pos, bodyLength, labels); - } - #endregion - - #region ConvertBody - struct SetState - { - public readonly int NewBodyPos; - public readonly int NewState; - - public SetState(int newBodyPos, int newState) - { - this.NewBodyPos = newBodyPos; - this.NewState = newState; - } - } - - void ConvertBody(List<ILNode> body, int startPos, int bodyLength, List<KeyValuePair<ILLabel, StateRange>> labels) - { - newBody = new List<ILNode>(); - newBody.Add(MakeGoTo(labels, 0)); - List<SetState> stateChanges = new List<SetState>(); - int currentState = -1; - // Copy all instructions from the old body to newBody. - for (int pos = startPos; pos < bodyLength; pos++) { - ILExpression expr = body[pos] as ILExpression; - if (expr != null && expr.Code == ILCode.Stfld && expr.Arguments[0].MatchThis()) { - // Handle stores to 'state' or 'current' - if (GetFieldDefinition(expr.Operand as FieldReference) == stateField) { - if (expr.Arguments[1].Code != ILCode.Ldc_I4) - throw new SymbolicAnalysisFailedException(); - currentState = (int)expr.Arguments[1].Operand; - stateChanges.Add(new SetState(newBody.Count, currentState)); - } else if (GetFieldDefinition(expr.Operand as FieldReference) == currentField) { - newBody.Add(new ILExpression(ILCode.YieldReturn, null, expr.Arguments[1])); - } else { - newBody.Add(body[pos]); - } - } else if (returnVariable != null && expr != null && expr.Code == ILCode.Stloc && expr.Operand == returnVariable) { - // handle store+branch to the returnVariable - ILExpression br = body.ElementAtOrDefault(++pos) as ILExpression; - if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnLabel || expr.Arguments[0].Code != ILCode.Ldc_I4) - throw new SymbolicAnalysisFailedException(); - int val = (int)expr.Arguments[0].Operand; - if (val == 0) { - newBody.Add(new ILExpression(ILCode.YieldBreak, null)); - } else if (val == 1) { - newBody.Add(MakeGoTo(labels, currentState)); - } else { - throw new SymbolicAnalysisFailedException(); - } - } else if (expr != null && expr.Code == ILCode.Ret) { - if (expr.Arguments.Count != 1 || expr.Arguments[0].Code != ILCode.Ldc_I4) - throw new SymbolicAnalysisFailedException(); - // handle direct return (e.g. in release builds) - int val = (int)expr.Arguments[0].Operand; - if (val == 0) { - newBody.Add(new ILExpression(ILCode.YieldBreak, null)); - } else if (val == 1) { - newBody.Add(MakeGoTo(labels, currentState)); - } else { - throw new SymbolicAnalysisFailedException(); - } - } else if (expr != null && expr.Code == ILCode.Call && expr.Arguments.Count == 1 && expr.Arguments[0].MatchThis()) { - MethodDefinition method = GetMethodDefinition(expr.Operand as MethodReference); - if (method == null) - throw new SymbolicAnalysisFailedException(); - StateRange stateRange; - if (method == disposeMethod) { - // Explicit call to dispose is used for "yield break;" within the method. - ILExpression br = body.ElementAtOrDefault(++pos) as ILExpression; - if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnFalseLabel) - throw new SymbolicAnalysisFailedException(); - newBody.Add(new ILExpression(ILCode.YieldBreak, null)); - } else if (finallyMethodToStateRange.TryGetValue(method, out stateRange)) { - // Call to Finally-method - int index = stateChanges.FindIndex(ss => stateRange.Contains(ss.NewState)); - if (index < 0) - throw new SymbolicAnalysisFailedException(); - - ILLabel label = new ILLabel(); - label.Name = "JumpOutOfTryFinally" + stateChanges[index].NewState; - newBody.Add(new ILExpression(ILCode.Leave, label)); - - SetState stateChange = stateChanges[index]; - // Move all instructions from stateChange.Pos to newBody.Count into a try-block - stateChanges.RemoveRange(index, stateChanges.Count - index); // remove all state changes up to the one we found - ILTryCatchBlock tryFinally = new ILTryCatchBlock(); - tryFinally.TryBlock = new ILBlock(newBody.GetRange(stateChange.NewBodyPos, newBody.Count - stateChange.NewBodyPos)); - newBody.RemoveRange(stateChange.NewBodyPos, newBody.Count - stateChange.NewBodyPos); // remove all nodes that we just moved into the try block - tryFinally.CatchBlocks = new List<ILTryCatchBlock.CatchBlock>(); - tryFinally.FinallyBlock = ConvertFinallyBlock(method); - newBody.Add(tryFinally); - newBody.Add(label); - } - } else { - newBody.Add(body[pos]); - } - } - newBody.Add(new ILExpression(ILCode.YieldBreak, null)); - } - - ILExpression MakeGoTo(ILLabel targetLabel) - { - Debug.Assert(targetLabel != null); - if (targetLabel == returnFalseLabel) - return new ILExpression(ILCode.YieldBreak, null); - else - return new ILExpression(ILCode.Br, targetLabel); - } - - ILExpression MakeGoTo(List<KeyValuePair<ILLabel, StateRange>> labels, int state) - { - foreach (var pair in labels) { - if (pair.Value.Contains(state)) - return MakeGoTo(pair.Key); - } - throw new SymbolicAnalysisFailedException(); - } - - ILBlock ConvertFinallyBlock(MethodDefinition finallyMethod) - { - ILBlock block = CreateILAst(finallyMethod); - // Get rid of assignment to state - FieldReference stfld; - List<ILExpression> args; - if (block.Body.Count > 0 && block.Body[0].Match(ILCode.Stfld, out stfld, out args)) { - if (GetFieldDefinition(stfld) == stateField && args[0].MatchThis()) - block.Body.RemoveAt(0); - } - // Convert ret to endfinally - foreach (ILExpression expr in block.GetSelfAndChildrenRecursive<ILExpression>()) { - if (expr.Code == ILCode.Ret) - expr.Code = ILCode.Endfinally; - } - return block; - } - #endregion - - #region TranslateFieldsToLocalAccess - void TranslateFieldsToLocalAccess() - { - TranslateFieldsToLocalAccess(newBody, fieldToParameterMap); - } - - internal static void TranslateFieldsToLocalAccess(List<ILNode> newBody, Dictionary<FieldDefinition, ILVariable> fieldToParameterMap) - { - var fieldToLocalMap = new DefaultDictionary<FieldDefinition, ILVariable>(f => new ILVariable { Name = f.Name, Type = f.FieldType }); - foreach (ILNode node in newBody) { - foreach (ILExpression expr in node.GetSelfAndChildrenRecursive<ILExpression>()) { - FieldDefinition field = GetFieldDefinition(expr.Operand as FieldReference); - if (field != null) { - switch (expr.Code) { - case ILCode.Ldfld: - if (expr.Arguments[0].MatchThis()) { - expr.Code = ILCode.Ldloc; - if (fieldToParameterMap.ContainsKey(field)) { - expr.Operand = fieldToParameterMap[field]; - } else { - expr.Operand = fieldToLocalMap[field]; - } - expr.Arguments.Clear(); - } - break; - case ILCode.Stfld: - if (expr.Arguments[0].MatchThis()) { - expr.Code = ILCode.Stloc; - if (fieldToParameterMap.ContainsKey(field)) { - expr.Operand = fieldToParameterMap[field]; - } else { - expr.Operand = fieldToLocalMap[field]; - } - expr.Arguments.RemoveAt(0); - } - break; - case ILCode.Ldflda: - if (expr.Arguments[0].MatchThis()) { - expr.Code = ILCode.Ldloca; - if (fieldToParameterMap.ContainsKey(field)) { - expr.Operand = fieldToParameterMap[field]; - } else { - expr.Operand = fieldToLocalMap[field]; - } - expr.Arguments.Clear(); - } - break; - } - } - } - } - } - #endregion - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/ITextOutput.cs b/main/contrib/ICSharpCode.Decompiler/ITextOutput.cs deleted file mode 100644 index e459e36e70..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ITextOutput.cs +++ /dev/null @@ -1,63 +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.IO; -using ICSharpCode.NRefactory; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler -{ - public interface ITextOutput - { - TextLocation Location { get; } - - void Indent(); - void Unindent(); - void Write(char ch); - void Write(string text); - void WriteLine(); - void WriteDefinition(string text, object definition, bool isLocal = true); - void WriteReference(string text, object reference, bool isLocal = false); - void WritePrimitiveValue (object value, string literalValue = null); - - void AddDebugSymbols(MethodDebugSymbols methodDebugSymbols); - - void MarkFoldStart(string collapsedText = "...", bool defaultCollapsed = false); - void MarkFoldEnd(); - } - - public static class TextOutputExtensions - { - public static void Write(this ITextOutput output, string format, params object[] args) - { - output.Write(string.Format(format, args)); - } - - public static void WriteLine(this ITextOutput output, string text) - { - output.Write(text); - output.WriteLine(); - } - - public static void WriteLine(this ITextOutput output, string format, params object[] args) - { - output.WriteLine(string.Format(format, args)); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/PlainTextOutput.cs b/main/contrib/ICSharpCode.Decompiler/PlainTextOutput.cs deleted file mode 100644 index 6ed760ad39..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/PlainTextOutput.cs +++ /dev/null @@ -1,127 +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.IO; -using ICSharpCode.NRefactory; - -namespace ICSharpCode.Decompiler -{ - public sealed class PlainTextOutput : ITextOutput - { - readonly TextWriter writer; - int indent; - bool needsIndent; - - int line = 1; - int column = 1; - - public PlainTextOutput(TextWriter writer) - { - if (writer == null) - throw new ArgumentNullException("writer"); - this.writer = writer; - } - - public PlainTextOutput() - { - this.writer = new StringWriter(); - } - - public TextLocation Location { - get { - return new TextLocation(line, column + (needsIndent ? indent : 0)); - } - } - - public override string ToString() - { - return writer.ToString(); - } - - public void Indent() - { - indent++; - } - - public void Unindent() - { - indent--; - } - - void WriteIndent() - { - if (needsIndent) { - needsIndent = false; - for (int i = 0; i < indent; i++) { - writer.Write('\t'); - } - column += indent; - } - } - - public void Write(char ch) - { - WriteIndent(); - writer.Write(ch); - column++; - } - - public void Write(string text) - { - WriteIndent(); - writer.Write(text); - column += text.Length; - } - - public void WriteLine() - { - writer.WriteLine(); - needsIndent = true; - line++; - column = 1; - } - - public void WriteDefinition(string text, object definition, bool isLocal) - { - Write(text); - } - - public void WriteReference(string text, object reference, bool isLocal) - { - Write(text); - } - - public void WritePrimitiveValue (object value, string literalValue) - { - Write (value.ToString ()); - } - - void ITextOutput.MarkFoldStart(string collapsedText, bool defaultCollapsed) - { - } - - void ITextOutput.MarkFoldEnd() - { - } - - void ITextOutput.AddDebugSymbols(MethodDebugSymbols methodDebugSymbols) - { - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs b/main/contrib/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs deleted file mode 100644 index 5fac809c0e..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,26 +0,0 @@ -#region Using directives - -using System; -using System.Resources; -using System.Reflection; -using System.Runtime.InteropServices; - -#endregion - -[assembly: AssemblyTitle("ICSharpCode.Decompiler")] -[assembly: AssemblyDescription("IL decompiler engine")] -[assembly: AssemblyCompany("ic#code")] -[assembly: AssemblyProduct("ILSpy")] -[assembly: AssemblyCopyright("Copyright 2011-2014 AlphaSierraPapa for the SharpDevelop Team")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: NeutralResourcesLanguage("en-US")] - -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly", - Justification = "AssemblyInformationalVersion does not need to be a parsable version")] diff --git a/main/contrib/ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs b/main/contrib/ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs deleted file mode 100644 index 3dba481ae1..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs +++ /dev/null @@ -1,27 +0,0 @@ -#region Using directives - -using System; -using System.Resources; -using System.Reflection; -using System.Runtime.InteropServices; - -#endregion - -[assembly: AssemblyTitle("ICSharpCode.Decompiler")] -[assembly: AssemblyDescription("IL decompiler engine")] -[assembly: AssemblyCompany("ic#code")] -[assembly: AssemblyProduct("ILSpy")] -[assembly: AssemblyCopyright("Copyright 2011-2014 AlphaSierraPapa for the SharpDevelop Team")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -[assembly: AssemblyVersion("$INSERTVERSION$")] -[assembly: AssemblyInformationalVersion("$INSERTVERSION$$INSERTBRANCHPOSTFIX$$INSERTVERSIONNAMEPOSTFIX$-$INSERTSHORTCOMMITHASH$")] -[assembly: NeutralResourcesLanguage("en-US")] - -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly", - Justification = "AssemblyInformationalVersion does not need to be a parsable version")] diff --git a/main/contrib/ICSharpCode.Decompiler/ReferenceResolvingException.cs b/main/contrib/ICSharpCode.Decompiler/ReferenceResolvingException.cs deleted file mode 100644 index dbceb44b3a..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/ReferenceResolvingException.cs +++ /dev/null @@ -1,68 +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.Collections.Generic; -using System.Linq; -using System.Text; - -namespace ICSharpCode.Decompiler -{ - /// <summary> - /// Represents an error while resolving a reference to a type or a member. - /// </summary> - [Serializable] - public class ReferenceResolvingException : Exception - { - /// <summary> - /// Initializes a new instance of the <see cref="T:ResolveException"/> class - /// </summary> - public ReferenceResolvingException() - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:ResolveException"/> class - /// </summary> - /// <param name="message">A <see cref="T:System.String"/> that describes the error. The content of message is intended to be understood by humans. The caller of this constructor is required to ensure that this string has been localized for the current system culture.</param> - public ReferenceResolvingException(string message) - : base(message) - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:ResolveException"/> class - /// </summary> - /// <param name="message">A <see cref="T:System.String"/> that describes the error. The content of message is intended to be understood by humans. The caller of this constructor is required to ensure that this string has been localized for the current system culture.</param> - /// <param name="inner">The exception that is the cause of the current exception. If the innerException parameter is not a null reference, the current exception is raised in a catch block that handles the inner exception.</param> - public ReferenceResolvingException(string message, Exception inner) - : base(message, inner) - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:ResolveException"/> class - /// </summary> - /// <param name="info">The object that holds the serialized object data.</param> - /// <param name="context">The contextual information about the source or destination.</param> - protected ReferenceResolvingException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) - : base(info, context) - { - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Async.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Async.cs deleted file mode 100644 index 7629dd5e62..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/Async.cs +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) 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. - -#pragma warning disable 1998 -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -public class Async -{ - public async void SimpleVoidMethod() - { - Console.WriteLine("Before"); - await Task.Delay(TimeSpan.FromSeconds(1.0)); - Console.WriteLine("After"); - } - - public async void VoidMethodWithoutAwait() - { - Console.WriteLine("No Await"); - } - - public async void AwaitYield() - { - await Task.Yield(); - } - - public async void AwaitDefaultYieldAwaitable() - { - await default(YieldAwaitable); - } - - public async Task SimpleVoidTaskMethod() - { - Console.WriteLine("Before"); - await Task.Delay(TimeSpan.FromSeconds(1.0)); - Console.WriteLine("After"); - } - - public async Task TaskMethodWithoutAwait() - { - Console.WriteLine("No Await"); - } - - public async Task<bool> SimpleBoolTaskMethod() - { - Console.WriteLine("Before"); - await Task.Delay(TimeSpan.FromSeconds(1.0)); - Console.WriteLine("After"); - return true; - } - - public async void TwoAwaitsWithDifferentAwaiterTypes() - { - Console.WriteLine("Before"); - if (await this.SimpleBoolTaskMethod()) - { - await Task.Delay(TimeSpan.FromSeconds(1.0)); - } - Console.WriteLine("After"); - } - - public async void StreamCopyTo(Stream destination, int bufferSize) - { - byte[] array = new byte[bufferSize]; - int count; - while ((count = await destination.ReadAsync(array, 0, array.Length)) != 0) - { - await destination.WriteAsync(array, 0, count); - } - } - - public async void StreamCopyToWithConfigureAwait(Stream destination, int bufferSize) - { - byte[] array = new byte[bufferSize]; - int count; - while ((count = await destination.ReadAsync(array, 0, array.Length).ConfigureAwait(false)) != 0) - { - await destination.WriteAsync(array, 0, count).ConfigureAwait(false); - } - } - - public async void AwaitInLoopCondition() - { - while (await this.SimpleBoolTaskMethod()) - { - Console.WriteLine("Body"); - } - } - - public async Task<int> AwaitInForEach(IEnumerable<Task<int>> elements) - { - int num = 0; - foreach (Task<int> current in elements) - { - num += await current; - } - return num; - } - - public async Task TaskMethodWithoutAwaitButWithExceptionHandling() - { - try - { - using (new StringWriter()) - { - Console.WriteLine("No Await"); - } - } - catch (Exception) - { - Console.WriteLine("Crash"); - } - } - - public async Task<int> NestedAwait(Task<Task<int>> task) - { - return await(await task); - } - - public async Task AwaitWithStack(Task<int> task) - { - Console.WriteLine("A", 1, await task); - } - - public async Task AwaitWithStack2(Task<int> task) - { - if (await this.SimpleBoolTaskMethod()) - { - Console.WriteLine("A", 1, await task); - } - else - { - int num = 1; - Console.WriteLine("A", 1, num); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/BooleanConsumedAsInteger.il b/main/contrib/ICSharpCode.Decompiler/Tests/BooleanConsumedAsInteger.il deleted file mode 100644 index 9e30002790..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/BooleanConsumedAsInteger.il +++ /dev/null @@ -1,59 +0,0 @@ -.assembly extern mscorlib -{ - .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) - .ver 4:0:0:0 -} -.assembly BooleanConsumedAsInteger -{ - .hash algorithm 0x00008004 - .ver 1:0:0:0 -} -.module BooleanConsumedAsInteger.exe -.imagebase 0x00400000 -.file alignment 0x00000200 -.stackreserve 0x00100000 -.subsystem 0x0003 // WINDOWS_CUI -.corflags 0x00000003 // ILONLY 32BITREQUIRED - -.class private auto ansi beforefieldinit BooleanConsumedAsInteger.Program extends [mscorlib]System.Object -{ - .method public hidebysig static void Main(string[] args) cil managed - { - .entrypoint - .maxstack 8 - - ret - } - - .method public hidebysig static int32 ReturnBoolAsInt() cil managed - { - ldnull - ldnull - call bool [mscorlib] System.Object::Equals(object, object) - ret - } - - .method public hidebysig static int32 BitwiseOperationOnBool() cil managed - { - ldnull - ldnull - call bool [mscorlib] System.Object::Equals(object, object) - ldc.i4 255 - and - ret - } - - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - // Code size 7 (0x7) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } // end of method Program::.ctor - -} // end of class StackTests.Program - - -// ============================================================= diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/CallOverloadedMethod.cs b/main/contrib/ICSharpCode.Decompiler/Tests/CallOverloadedMethod.cs deleted file mode 100644 index 4f493ef20f..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/CallOverloadedMethod.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Collections.Generic; - -public class CallOverloadedMethod -{ - public void OverloadedMethod(object a) - { - } - - public void OverloadedMethod(int? a) - { - } - - public void OverloadedMethod(string a) - { - } - - public void Call() - { - this.OverloadedMethod("(string)"); - this.OverloadedMethod((object)"(object)"); - this.OverloadedMethod(5); - this.OverloadedMethod((object)5); - this.OverloadedMethod(5L); - this.OverloadedMethod((object)null); - this.OverloadedMethod((string)null); - this.OverloadedMethod((int?)null); - } - - public void CallMethodUsingInterface(List<int> list) - { - ((ICollection<int>)list).Clear(); - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/CheckedUnchecked.cs b/main/contrib/ICSharpCode.Decompiler/Tests/CheckedUnchecked.cs deleted file mode 100644 index fd1c4d89f3..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/CheckedUnchecked.cs +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (c) 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; - -public class CheckedUnchecked -{ - public int Operators(int a, int b) - { - int num = checked(a + b); - int num2 = a + b; - int num3 = checked(a - b); - int num4 = a - b; - int num5 = checked(a * b); - int num6 = a * b; - int num7 = a / b; - int num8 = a % b; - // The division operators / and % only exist in one form (checked vs. unchecked doesn't matter for them) - return num * num2 * num3 * num4 * num5 * num6 * num7 * num8; - } - - public int Cast(int a) - { - short num = checked((short)a); - short num2 = (short)a; - byte b = checked((byte)a); - byte b2 = (byte)a; - return num * num2 * b * b2; - } - - public void ForWithCheckedIteratorAndUncheckedBody(int n) - { - checked - { - for (int i = n + 1; i < n + 1; i++) - { - n = unchecked(i * i); - } - } - } - - public void ForWithCheckedInitializerAndUncheckedIterator(int n) - { - checked - { - int i = n; - for (i -= 10; i < n; i = unchecked(i + 1)) - { - n--; - } - } - } - public void ObjectCreationInitializerChecked() - { - this.TestHelp(new - { - x = 0, - l = 0 - }, n => checked(new - { - x = n.x + 1, - l = n.l + 1 - })); - } - - public void ObjectCreationWithOneFieldChecked() - { - this.TestHelp(new - { - x = 0, - l = 0 - }, n => new - { - x = checked(n.x + 1), - l = n.l + 1 - }); - } - - - public void ArrayInitializerChecked() - { - this.TestHelp<int[]>(new int[] - { - 1, - 2 - }, (int[] n) => checked(new int[] - { - n[0] + 1, - n[1] + 1 - })); - } - - public T TestHelp<T>(T t, Func<T, T> f) - { - return f(t); - } - - public void CheckedInArrayCreationArgument(int a, int b) - { - Console.WriteLine(new int[checked(a + b)]); - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/CodeSampleFileParser.cs b/main/contrib/ICSharpCode.Decompiler/Tests/CodeSampleFileParser.cs deleted file mode 100644 index 5cb49aad73..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/CodeSampleFileParser.cs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.IO; - -namespace ICSharpCode.Decompiler.Tests -{ - static class CodeSampleFileParser - { - public static IEnumerable<string> ListSections(string s) - { - var query = from line in ToLines(s) - let sectionName = ReadSectionName(line) - where sectionName != null - select sectionName; - return query; - } - - public static string GetSection(string sectionName, string s) - { - var lines = ToLines(s); - - bool sectionFound = false; - var sectionText = new StringBuilder(); - - Action<string> parser = null; - - Action<string> commonSectionReader = line => - { - if (IsCommonSectionEnd(line)) - parser = null; - else - sectionText.AppendLine(line); - }; - - Action<string> namedSectionReader = line => - { - string name = ReadSectionName(line); - if (name == null) - sectionText.AppendLine(line); - else if (name != sectionName) - parser = null; - }; - - Action<string> defaultReader = line => - { - if (IsCommonSectionStart(line)) - parser = commonSectionReader; - else if (ReadSectionName(line) == sectionName) - { - parser = namedSectionReader; - sectionFound = true; - } - }; - - foreach(var line in lines) - { - (parser ?? defaultReader)(line); - } - - if (sectionFound) - return sectionText.ToString(); - else - return ""; - } - - public static bool IsCommentOrBlank(string s) - { - if(String.IsNullOrWhiteSpace(s)) - return true; - s = s.Trim(); - return s.StartsWith("//") || s.StartsWith("#"); // Also ignore #pragmas for warning suppression - } - - public static string ConcatLines(IEnumerable<string> lines) - { - var buffer = new StringBuilder(); - foreach (var line in lines) - { - buffer.AppendLine(line); - } - return buffer.ToString(); - } - - static string ReadSectionName(string line) - { - line = line.TrimStart(); - if (line.StartsWith("//$$")) - return line.Substring(4).Trim(); - else - return null; - } - - static bool IsCommonSectionStart(string line) - { - return line.Trim() == "//$CS"; - } - - static bool IsCommonSectionEnd(string line) - { - return line.Trim() == "//$CE"; - } - - static IEnumerable<string> ToLines(string s) - { - var reader = new StringReader(s); - string line; - while ((line = reader.ReadLine()) != null) - { - yield return line; - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/ControlFlow.cs b/main/contrib/ICSharpCode.Decompiler/Tests/ControlFlow.cs deleted file mode 100644 index d83f04a2b1..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/ControlFlow.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Collections.Generic; - -public static class ControlFlow -{ - public static void EmptyIf(string input, List<string> value, Dictionary<int, string> _headers) - { - if (value.Contains("test")) - { - } - _headers.Add(2, "result"); - } - - public static void NormalIf(string input, List<string> value, Dictionary<int, string> _headers) - { - if (value.Contains("test")) - { - _headers.Add(1, "result"); - } - else - { - _headers.Add(1, "else"); - } - _headers.Add(2, "end"); - } - - public static void NormalIf2(string input, List<string> value, Dictionary<int, string> _headers) - { - if (value.Contains("test")) - { - _headers.Add(1, "result"); - } - _headers.Add(2, "end"); - } - - public static void NormalIf3(string input, List<string> value, Dictionary<int, string> _headers) - { - if (value.Contains("test")) - { - _headers.Add(1, "result"); - } - else - { - _headers.Add(1, "else"); - } - } - - public static void Test(string input, List<string> value, Dictionary<int, string> _headers) - { - foreach (string current in value) - { - _headers.Add(0, current); - } - if (value.Contains("test")) - { - _headers.Add(1, "result"); - } - else - { - _headers.Add(1, "else"); - } - } - - public static void CascadingIfElse(bool condition, string input, int index) - { - if (condition) - { - Console.WriteLine("condition"); - } - else if (input == null) - { - Console.WriteLine("condition2"); - } - else if (index > 1) - { - Console.WriteLine("condition3"); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/CustomAttributes.code.cs b/main/contrib/ICSharpCode.Decompiler/Tests/CustomAttributes.code.cs deleted file mode 100644 index 4130b0b0f7..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/CustomAttributes.code.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -namespace aa -{ - public static class CustomAtributes - { - [Flags] - public enum EnumWithFlag - { - All = 15, - None = 0, - Item1 = 1, - Item2 = 2, - Item3 = 4, - Item4 = 8 - } - [AttributeUsage(AttributeTargets.All)] - public class MyAttribute : Attribute - { - public MyAttribute(CustomAtributes.EnumWithFlag en) - { - } - } - [CustomAtributes.MyAttribute(CustomAtributes.EnumWithFlag.Item1 | CustomAtributes.EnumWithFlag.Item2)] - private static int field; - [CustomAtributes.MyAttribute(CustomAtributes.EnumWithFlag.All)] - public static string Property - { - get - { - return "aa"; - } - } - [Obsolete("some message")] - public static void ObsoletedMethod() - { - Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, AttributeTargets.Property | AttributeTargets.Field); - AttributeTargets attributeTargets = AttributeTargets.Property | AttributeTargets.Field; - Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, attributeTargets); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/CustomAttributes/CustomAttributeTests.cs b/main/contrib/ICSharpCode.Decompiler/Tests/CustomAttributes/CustomAttributeTests.cs deleted file mode 100644 index 892f33bf95..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/CustomAttributes/CustomAttributeTests.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using NUnit.Framework; - -namespace ICSharpCode.Decompiler.Tests.CustomAttributes -{ - [TestFixture] - public class CustomAttributeTests : DecompilerTestBase - { - [Test] - public void CustomAttributeSamples() - { - ValidateFileRoundtrip(@"CustomAttributes\S_CustomAttributeSamples.cs"); - } - - [Test] - public void CustomAttributesMultiTest() - { - ValidateFileRoundtrip(@"CustomAttributes\S_CustomAttributes.cs"); - } - - [Test] - public void AssemblyCustomAttributesMultiTest() - { - ValidateFileRoundtrip(@"CustomAttributes\S_AssemblyCustomAttribute.cs"); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/CustomAttributes/S_AssemblyCustomAttribute.cs b/main/contrib/ICSharpCode.Decompiler/Tests/CustomAttributes/S_AssemblyCustomAttribute.cs deleted file mode 100644 index feaa04a680..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/CustomAttributes/S_AssemblyCustomAttribute.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2014 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; - -[assembly: CLSCompliant(false)] diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs b/main/contrib/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs deleted file mode 100644 index 7154b2c3b5..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs +++ /dev/null @@ -1,508 +0,0 @@ -// Copyright (c) 2014 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. - -//$CS -using System; -//$CE - -//$$ TargetModule (ignored) -//[module: CLSCompliantAttribute(false)] -//$$ ParameterlessAttributeUsage -namespace ParameterLessAttributeUsage -{ - [Flags] - public enum EnumWithFlagsAttribute - { - None = 0 - } -} -//$$ AttributeWithEnumArgument -namespace AttributeWithEnumArgument -{ - [AttributeUsage(AttributeTargets.All)] - public class MyAttributeAttribute : Attribute - { - } -} -//$$ AttributeWithEnumExpressionArgument -namespace AttributeWithEnumExpressionArgument -{ - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Interface)] - public class MyAttributeAttribute : Attribute - { - } -} -//$$ AttributeWithStringExpressionArgument -namespace AttributeWithStringExpressionArgument -{ - [Obsolete("message")] - public class ObsoletedClass - { - } -} -//$$ AttributeWithTypeArgument -namespace AttributeWithTypeArgument -{ - [AttributeUsage(AttributeTargets.All)] - public class MyTypeAttribute : Attribute - { - public MyTypeAttribute(Type t) - { - } - } - - [MyType(typeof(Attribute))] - public class SomeClass - { - } -} -//$$ AppliedToEvent -namespace AppliedToEvent -{ - [AttributeUsage(AttributeTargets.Event)] - public class MyAttributeAttribute : Attribute - { - } - public class TestClass - { - [MyAttribute] - public event EventHandler MyEvent; - } -} -//$$ AppliedToEventMethods -namespace AppliedToEventMethods -{ - [AttributeUsage(AttributeTargets.Method)] - public class MyAttributeAttribute : Attribute - { - } - public class TestClass - { - [method: MyAttribute] - public event EventHandler MyEvent; - } -} -//$$ AppliedToField -namespace AppliedToField -{ - [AttributeUsage(AttributeTargets.Field)] - public class MyAttributeAttribute : Attribute - { - } - public class TestClass - { - [MyAttribute] - public int Field; - } -} -//$$ AppliedToProperty -namespace AppliedToProperty -{ - public class TestClass - { - [Obsolete("reason")] - public int Property - { - get - { - return 0; - } - } - } -} -//$$ AppliedToPropertyGet -namespace AppliedToPropertyGet -{ - [AttributeUsage(AttributeTargets.All)] - public class MyAttributeAttribute : Attribute - { - } - public class TestClass - { - public int Property - { - [MyAttribute] - get - { - return 0; - } - } - } -} -//$$ AppliedToPropertySet -namespace AppliedToPropertySet -{ - [AttributeUsage(AttributeTargets.All)] - public class MyAttributeAttribute : Attribute - { - } - public class TestClass - { - public int Property - { - get - { - return 3; - } - [MyAttribute] - set - { - } - } - } -} -//$$ AppliedToIndexer -namespace AppliedToIndexer -{ - public class TestClass - { - [Obsolete("reason")] - public int this[int i] - { - get - { - return 0; - } - } - } -} -//$$ AppliedToDelegate -[Obsolete("reason")] -public delegate int AppliedToDelegate(); -//$$ AppliedToMethod -namespace AppliedToMethod -{ - [AttributeUsage(AttributeTargets.Method)] - public class MyAttributeAttribute : Attribute - { - } - public class TestClass - { - [MyAttribute] - public void Method() - { - } - } -} -//$$ AppliedToInterface -[Obsolete("reason")] -public interface AppliedToInterface -{ -} -//$$ AppliedToStruct -[Obsolete("reason")] -public struct AppliedToStruct -{ - public int Field; -} -//$$ AppliedToParameter -namespace AppliedToParameter -{ - [AttributeUsage(AttributeTargets.Parameter)] - public class MyAttributeAttribute : Attribute - { - } - public class MyClass - { - public void Method([MyAttribute] int val) - { - } - } -} -//$$ NamedInitializerProperty -namespace NamedInitializerProperty -{ - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - public class MyAttributeAttribute : Attribute - { - } -} -//$$ NamedInitializerPropertyString -namespace NamedInitializerPropertyString -{ - [AttributeUsage(AttributeTargets.All)] - public class MyAttributeAttribute : Attribute - { - public string Prop - { - get - { - return ""; - } - set - { - } - } - } - [MyAttribute(Prop = "value")] - public class MyClass - { - } -} -//$$ NamedInitializerPropertyType -namespace NamedInitializerPropertyType -{ - [AttributeUsage(AttributeTargets.All)] - public class MyAttributeAttribute : Attribute - { - public Type Prop - { - get - { - return null; - } - set - { - } - } - } - [MyAttribute(Prop = typeof(Enum))] - public class MyClass - { - } -} -//$$ NamedInitializerPropertyEnum -namespace NamedInitializerPropertyEnum -{ - [AttributeUsage(AttributeTargets.All)] - public class MyAttributeAttribute : Attribute - { - public AttributeTargets Prop - { - get - { - return AttributeTargets.All; - } - set - { - } - } - } - [MyAttribute(Prop = (AttributeTargets.Class | AttributeTargets.Method))] - public class MyClass - { - } -} -//$$ NamedInitializerFieldEnum -namespace NamedInitializerFieldEnum -{ - [AttributeUsage(AttributeTargets.All)] - public class MyAttributeAttribute : Attribute - { - public AttributeTargets Field; - } - [MyAttribute(Field = (AttributeTargets.Class | AttributeTargets.Method))] - public class MyClass - { - } -} -//$$ TargetReturn -namespace TargetReturn -{ - [AttributeUsage(AttributeTargets.All)] - public class MyAttributeAttribute : Attribute - { - } - public class MyClass - { - [return: MyAttribute] - public int MyMethod() - { - return 5; - } - } -} -//$$ TargetPropertyGetReturn -namespace TargetPropertyGetReturn -{ - [AttributeUsage(AttributeTargets.All)] - public class MyAttributeAttribute : Attribute - { - } - public class MyClass - { - public int Prop - { - [return: MyAttribute] - get - { - return 3; - } - } - } -} -//$$ TargetPropertySetParam -namespace TargetPropertySetParam -{ - [AttributeUsage(AttributeTargets.All)] - public class MyAttributeAttribute : Attribute - { - } - public class MyClass - { - public int Prop - { - [param: MyAttribute] - set - { - } - } - } -} -//$$ TargetPropertySetReturn -namespace TargetPropertySetReturn -{ - [AttributeUsage(AttributeTargets.All)] - public class MyAttributeAttribute : Attribute - { - } - public class MyClass - { - public int Prop - { - get - { - return 3; - } - [return: MyAttribute] - set - { - } - } - } -} -//$$ TargetPropertyIndexGetReturn -namespace TargetPropertyIndexGetReturn -{ - [AttributeUsage(AttributeTargets.All)] - public class MyAttributeAttribute : Attribute - { - } - public class MyClass - { - public int this[string s] - { - [return: MyAttribute] - get - { - return 3; - } - } - } -} -//$$ TargetPropertyIndexParamOnlySet -namespace TargetPropertyIndexParamOnlySet -{ - [AttributeUsage(AttributeTargets.All)] - public class MyAttributeAttribute : Attribute - { - } - public class MyClass - { - public int this[[MyAttribute] string s] - { - set - { - } - } - } -} -//$$ TargetPropertyIndexParamOnlyGet -namespace TargetPropertyIndexParamOnlyGet -{ - [AttributeUsage(AttributeTargets.All)] - public class MyAttributeAttribute : Attribute - { - } - public class MyClass - { - public int this[[MyAttribute] string s] - { - get - { - return 3; - } - } - } -} -//$$ TargetPropertyIndexSetReturn -namespace TargetPropertyIndexSetReturn -{ - [AttributeUsage(AttributeTargets.All)] - public class MyAttributeAttribute : Attribute - { - } - public class MyClass - { - public string this[int index] - { - get - { - return ""; - } - [return: MyAttribute] - set - { - } - } - } -} -//$$ TargetPropertyIndexSetMultiParam -namespace TargetPropertyIndexSetMultiParam -{ - [AttributeUsage(AttributeTargets.All)] - public class MyAttributeAttribute : Attribute - { - public int Field; - } - public class MyClass - { - public string this[[MyAttribute(Field = 2)] int index1, [MyAttribute(Field = 3)] int index2] - { - get - { - return ""; - } - [param: MyAttribute] - set - { - } - } - } -} -//$$ ClassAttributeOnTypeParameter -namespace ClassAttributeOnTypeParameter -{ - [AttributeUsage(AttributeTargets.All)] - public class MyAttributeAttribute : Attribute - { - } - public class MyClass<[MyAttribute] T> - { - } -} -//$$ AttributeOnReturnTypeOfDelegate -namespace AttributeOnReturnTypeOfDelegate -{ - [AttributeUsage(AttributeTargets.All)] - public class MyAttributeAttribute : Attribute - { - } - [return: MyAttribute] - public delegate void Test(); -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributes.cs b/main/contrib/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributes.cs deleted file mode 100644 index 37c1c0d71b..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributes.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) 2014 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; - -namespace aa -{ - public static class CustomAttributes - { - [Flags] - public enum EnumWithFlag - { - All = 15, - None = 0, - Item1 = 1, - Item2 = 2, - Item3 = 4, - Item4 = 8 - } - [AttributeUsage(AttributeTargets.All)] - public class MyAttribute : Attribute - { - public MyAttribute(object val) - { - } - } - [CustomAttributes.MyAttribute(CustomAttributes.EnumWithFlag.Item1 | CustomAttributes.EnumWithFlag.Item2)] - private static int field; - [CustomAttributes.MyAttribute(CustomAttributes.EnumWithFlag.All)] - public static string Property - { - get - { - return "aa"; - } - } - [Obsolete("some message")] - public static void ObsoletedMethod() - { - //Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, (AttributeTargets)(AttributeTargets.Property | AttributeTargets.Field)); - Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, AttributeTargets.Property | AttributeTargets.Field); - AttributeTargets attributeTargets = AttributeTargets.Property | AttributeTargets.Field; - Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, attributeTargets); - } - // No Boxing - [CustomAttributes.MyAttribute(new StringComparison[] - { - StringComparison.Ordinal, - StringComparison.CurrentCulture - })] - public static void ArrayAsAttribute1() - { - } - // Boxing of each array element - [CustomAttributes.MyAttribute(new object[] - { - StringComparison.Ordinal, - StringComparison.CurrentCulture - })] - public static void ArrayAsAttribute2() - { - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs b/main/contrib/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs deleted file mode 100644 index 5bd0ec516b..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) 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; - -public static class CustomShortCircuitOperators -{ - // TODO: Restore base class after https://roslyn.codeplex.com/workitem/358 is fixed. - private class C - { - public static bool operator true(CustomShortCircuitOperators.C x) - { - return true; - } - - public static bool operator false(CustomShortCircuitOperators.C x) - { - return false; - } - - public static CustomShortCircuitOperators.C operator &(CustomShortCircuitOperators.C x, CustomShortCircuitOperators.C y) - { - return null; - } - - public static CustomShortCircuitOperators.C operator |(CustomShortCircuitOperators.C x, CustomShortCircuitOperators.C y) - { - return null; - } - - public static bool operator !(CustomShortCircuitOperators.C x) - { - return false; - } - - private static void Main() - { - CustomShortCircuitOperators.C c = new CustomShortCircuitOperators.C(); - CustomShortCircuitOperators.C c2 = new CustomShortCircuitOperators.C(); - CustomShortCircuitOperators.C c3 = c && c2; - CustomShortCircuitOperators.C c4 = c || c2; - Console.WriteLine(c3.ToString()); - Console.WriteLine(c4.ToString()); - } - - private static void Test2() - { - CustomShortCircuitOperators.C c = new CustomShortCircuitOperators.C(); - if (c && c) - { - Console.WriteLine(c.ToString()); - } - - if (!(c && c)) - { - Console.WriteLine(c.ToString()); - } - } - - private static void Test3() - { - CustomShortCircuitOperators.C c = new CustomShortCircuitOperators.C(); - if (c) - { - Console.WriteLine(c.ToString()); - } - if (!c) - { - Console.WriteLine(c.ToString()); - } - } - } -}
\ No newline at end of file diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs b/main/contrib/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs deleted file mode 100644 index 659bb2be68..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 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.CodeDom.Compiler; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -using ICSharpCode.Decompiler.Ast; -using ICSharpCode.Decompiler.Tests.Helpers; -using Microsoft.CSharp; -using Mono.Cecil; -using NUnit.Framework; - -namespace ICSharpCode.Decompiler.Tests -{ - public abstract class DecompilerTestBase - { - protected static void ValidateFileRoundtrip(string samplesFileName) - { - var fullPath = Path.Combine(@"..\..\Tests", samplesFileName); - AssertRoundtripCode(fullPath); - } - - static string RemoveIgnorableLines(IEnumerable<string> lines) - { - return CodeSampleFileParser.ConcatLines(lines.Where(l => !CodeSampleFileParser.IsCommentOrBlank(l))); - } - - protected static void AssertRoundtripCode(string fileName, bool optimize = false, bool useDebug = false, int compilerVersion = 4) - { - var code = RemoveIgnorableLines(File.ReadLines(fileName)); - AssemblyDefinition assembly = CompileLegacy(code, optimize, useDebug, compilerVersion); - - AstBuilder decompiler = new AstBuilder(new DecompilerContext(assembly.MainModule)); - decompiler.AddAssembly(assembly); - new Helpers.RemoveCompilerAttribute().Run(decompiler.SyntaxTree); - - StringWriter output = new StringWriter(); - decompiler.GenerateCode(new PlainTextOutput(output)); - CodeAssert.AreEqual(code, output.ToString()); - } - - protected static AssemblyDefinition CompileLegacy(string code, bool optimize, bool useDebug, int compilerVersion) - { - CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v" + new Version(compilerVersion, 0) } }); - CompilerParameters options = new CompilerParameters(); - options.CompilerOptions = "/unsafe /o" + (optimize ? "+" : "-") + (useDebug ? " /debug" : ""); - if (compilerVersion >= 4) - options.ReferencedAssemblies.Add("System.Core.dll"); - CompilerResults results = provider.CompileAssemblyFromSource(options, code); - try - { - if (results.Errors.Count > 0) - { - StringBuilder b = new StringBuilder("Compiler error:"); - foreach (var error in results.Errors) - { - b.AppendLine(error.ToString()); - } - throw new Exception(b.ToString()); - } - return AssemblyDefinition.ReadAssembly(results.PathToAssembly); - } - finally - { - File.Delete(results.PathToAssembly); - results.TempFiles.Delete(); - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs b/main/contrib/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs deleted file mode 100644 index 70c00fc655..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; - -public static class DelegateConstruction -{ - class InstanceTests - { - public Action CaptureOfThis() - { - return delegate { - CaptureOfThis(); - }; - } - - public Action CaptureOfThisAndParameter(int a) - { - return delegate { - CaptureOfThisAndParameter(a); - }; - } - - public Action CaptureOfThisAndParameterInForEach(int a) - { - foreach (int item in Enumerable.Empty<int>()) { - if (item > 0) { - return delegate { - CaptureOfThisAndParameter(item + a); - }; - } - } - return null; - } - - public Action CaptureOfThisAndParameterInForEachWithItemCopy(int a) - { - foreach (int item in Enumerable.Empty<int>()) { - int copyOfItem = item; - if (item > 0) { - return delegate { - CaptureOfThisAndParameter(item + a + copyOfItem); - }; - } - } - return null; - } - - public void LambdaInForLoop() - { - for (int i = 0; i < 100000; i++) { - Bar(() => Foo()); - } - } - - public int Foo() - { - return 0; - } - - public void Bar(Func<int> f) - { - } - } - - public static void Test(this string a) - { - } - - public static Action<string> ExtensionMethodUnbound() - { - return new Action<string>(DelegateConstruction.Test); - } - - public static Action ExtensionMethodBound() - { - return new Action("abc".Test); - } - - public static Action ExtensionMethodBoundOnNull() - { - return new Action(((string)null).Test); - } - - public static object StaticMethod() - { - return new Func<Action>(DelegateConstruction.ExtensionMethodBound); - } - - public static object InstanceMethod() - { - return new Func<string>("hello".ToUpper); - } - - public static object InstanceMethodOnNull() - { - return new Func<string>(((string)null).ToUpper); - } - - public static List<Action<int>> AnonymousMethodStoreWithinLoop() - { - List<Action<int>> list = new List<Action<int>>(); - for (int i = 0; i < 10; i++) - { - int counter; - list.Add(delegate(int x) - { - counter = x; - } - ); - } - return list; - } - - public static List<Action<int>> AnonymousMethodStoreOutsideLoop() - { - List<Action<int>> list = new List<Action<int>>(); - int counter; - for (int i = 0; i < 10; i++) - { - list.Add(delegate(int x) - { - counter = x; - } - ); - } - return list; - } - - public static Action StaticAnonymousMethodNoClosure() - { - return delegate - { - Console.WriteLine(); - }; - } - - public static void NameConflict() - { - // i is captured variable, - // j is parameter in anonymous method - // k is local in anonymous method, - // l is local in main method - // Ensure that the decompiler doesn't introduce name conflicts - List<Action<int>> list = new List<Action<int>>(); - for (int l = 0; l < 10; l++) { - int i; - for (i = 0; i < 10; i++) { - list.Add( - delegate (int j) { - for (int k = 0; k < i; k += j) { - Console.WriteLine(); - } - }); - } - } - } - - public static void NameConflict2(int j) - { - List<Action<int>> list = new List<Action<int>>(); - for (int k = 0; k < 10; k++) { - list.Add( - delegate(int i) { - Console.WriteLine(i); - }); - } - } - - public static Action<int> NameConflict3(int i) - { - return delegate(int j) { - for (int k = 0; k < j; k++) { - Console.WriteLine(k); - } - }; - } - - public static Func<int, Func<int, int>> CurriedAddition(int a) - { - return b => c => a + b + c; - } - - public static Func<int, Func<int, Func<int, int>>> CurriedAddition2(int a) - { - return b => c => d => a + b + c + d; - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/DoubleConstants.cs b/main/contrib/ICSharpCode.Decompiler/Tests/DoubleConstants.cs deleted file mode 100644 index 2bc20a7730..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/DoubleConstants.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 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; - -public class DoubleConstants -{ - public const double Zero = 0.0; - public const double MinusZero = -0.0; - public const double NaN = double.NaN; - public const double PositiveInfinity = double.PositiveInfinity; - public const double NegativeInfinity = double.NegativeInfinity; -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs b/main/contrib/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs deleted file mode 100644 index 0db35e3573..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) 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.Threading; - -public class ExceptionHandling -{ - public void MethodEndingWithEndFinally() - { - try - { - throw null; - } - finally - { - Console.WriteLine(); - } - } - - public void MethodEndingWithRethrow() - { - try - { - throw null; - } - catch - { - throw; - } - } - - public void TryCatchFinally() - { - try - { - Console.WriteLine("Try"); - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - } - finally - { - Console.WriteLine("Finally"); - } - } - - public void TryCatchMultipleHandlers() - { - try - { - Console.WriteLine("Try"); - } - catch (InvalidOperationException ex) - { - Console.WriteLine(ex.Message); - } - catch (Exception ex2) - { - Console.WriteLine(ex2.Message); - } - catch - { - Console.WriteLine("other"); - } - } - - public void NoUsingStatementBecauseTheVariableIsAssignedTo() - { - CancellationTokenSource cancellationTokenSource = null; - try - { - cancellationTokenSource = new CancellationTokenSource(); - } - finally - { - if (cancellationTokenSource != null) - { - cancellationTokenSource.Dispose(); - } - } - } - - public void UsingStatementThatChangesTheVariable() - { - CancellationTokenSource cancellationTokenSource = null; - using (cancellationTokenSource) - { - cancellationTokenSource = new CancellationTokenSource(); - } - } - - public void TwoCatchBlocksWithSameVariable() - { - try - { - Console.WriteLine("Try1"); - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - } - try - { - Console.WriteLine("Try2"); - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/ExpressionTrees.cs b/main/contrib/ICSharpCode.Decompiler/Tests/ExpressionTrees.cs deleted file mode 100644 index 9d325e9e9d..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/ExpressionTrees.cs +++ /dev/null @@ -1,370 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Xml; - -public class ExpressionTrees -{ - class GenericClass<X> - { - public static X StaticField; - public X InstanceField; - public static X StaticProperty { get; set; } - public X InstanceProperty { get; set; } - - public static bool GenericMethod<Y>() - { - return false; - } - } - - int field; - - static object ToCode<R>(object x, Expression<Func<R>> expr) - { - return expr; - } - - static object ToCode<T, R>(object x, Expression<Func<T, R>> expr) - { - return expr; - } - - static object X() - { - return null; - } - - public void Parameter(bool a) - { - ToCode(X(), () => a); - } - - public void LocalVariable() - { - bool a = true; - ToCode(X(), () => a); - } - - public void LambdaParameter() - { - ToCode(X(), (bool a) => a); - } - - public void AddOperator(int x) - { - ToCode(X(), () => 1 + x + 2); - } - - public void AnonymousClasses() - { - ToCode(X(), () => new { X = 3, A = "a" }); - } - - public void ArrayIndex() - { - ToCode(X(), () => new[] { 3, 4, 5 }[0 + (int)(DateTime.Now.Ticks % 3)]); - } - - public void ArrayLengthAndDoubles() - { - ToCode(X(), () => new[] { 1.0, 2.01, 3.5 }.Concat(new[] { 1.0, 2.0 }).ToArray().Length); - } - - public void AsOperator() - { - ToCode(X(), () => new object() as string); - } - - public void ComplexGenericName() - { - ToCode(X(), () => ((Func<int, bool>)(x => x > 0))(0)); - } - - public void DefaultValue() - { - ToCode(X(), () => new TimeSpan(1, 2, 3) == default(TimeSpan)); - } - - public void EnumConstant() - { - ToCode(X(), () => new object().Equals(MidpointRounding.ToEven)); - } - - public void IndexerAccess() - { - var dict = Enumerable.Range(1, 20).ToDictionary(n => n.ToString()); - ToCode(X(), () => dict["3"] == 3); - } - - public void IsOperator() - { - ToCode(X(), () => new object() is string); - } - - public void ListInitializer() - { - ToCode(X(), () => new Dictionary<int, int> { { 1, 1 }, { 2, 2 }, { 3, 4 } }.Count == 3); - } - - public void ListInitializer2() - { - ToCode(X(), () => new List<int>(50) { 1, 2, 3 }.Count == 3); - } - - public void ListInitializer3() - { - ToCode(X(), () => new List<int> { 1, 2, 3 }.Count == 3); - } - - public void LiteralCharAndProperty() - { - ToCode(X(), () => new string(' ', 3).Length == 1); - } - - public void CharNoCast() - { - ToCode(X(), () => "abc"[1] == 'b'); - } - - public void StringsImplicitCast() - { - int i = 1; - string x = "X"; - ToCode(X(), () => (("a\n\\b" ?? x) + x).Length == 2 ? false : true && (1m + -i > 0 || false)); - } - - public void NotImplicitCast() - { - byte z = 42; - ToCode(X(), () => ~z == 0); - } - - public void MembersBuiltin() - { - ToCode(X(), () => 1.23m.ToString()); - ToCode(X(), () => AttributeTargets.All.HasFlag((Enum)AttributeTargets.Assembly)); - ToCode(X(), () => "abc".Length == 3); - ToCode(X(), () => 'a'.CompareTo('b') < 0); - } - - public void MembersDefault() - { - ToCode(X(), () => default(DateTime).Ticks == 0); - ToCode(X(), () => default(int[]).Length == 0); - ToCode(X(), () => default(Type).IsLayoutSequential); - ToCode(X(), () => default(List<int>).Count); - ToCode(X(), () => default(int[]).Clone() == null); - ToCode(X(), () => default(Type).IsInstanceOfType(new object())); - ToCode(X(), () => default(List<int>).AsReadOnly()); - } - - public void DoAssert() - { - field = 37; - ToCode(X(), () => field != C()); - ToCode(X(), () => !ReferenceEquals(this, new ExpressionTrees())); - ToCode(X(), () => MyEquals(this) && !MyEquals(default(ExpressionTrees))); - } - - int C() - { - return field + 5; - } - - bool MyEquals(ExpressionTrees other) - { - return other != null && field == other.field; - } - - public void MethodGroupAsExtensionMethod() - { - ToCode(X(), () => (Func<bool>)new[] { 2000, 2004, 2008, 2012 }.Any); - } - - public void MethodGroupConstant() - { - ToCode(X(), () => Array.TrueForAll(new[] { 2000, 2004, 2008, 2012 }, DateTime.IsLeapYear)); - - HashSet<int> set = new HashSet<int>(); - ToCode(X(), () => new[] { 2000, 2004, 2008, 2012 }.All(set.Add)); - - Func<Func<object, object, bool>, bool> sink = f => f(null, null); - ToCode(X(), () => sink(int.Equals)); - } - - public void MultipleCasts() - { - ToCode(X(), () => 1 == (int)(object)1); - } - - public void MultipleDots() - { - ToCode(X(), () => 3.ToString().ToString().Length > 0); - } - - public void NestedLambda() - { - Func<Func<int>, int> call = f => f(); - //no params - ToCode(X(), () => call(() => 42)); - //one param - ToCode(X(), () => new[] { 37, 42 }.Select(x => x * 2)); - //two params - ToCode(X(), () => new[] { 37, 42 }.Select((x, i) => x * 2)); - } - - public void CurriedLambda() - { - ToCode<int, Func<int, Func<int, int>>>(X(), a => b => c => a + b + c); - } - - bool Fizz(Func<int, bool> a) - { - return a(42); - } - - bool Buzz(Func<int, bool> a) - { - return a(42); - } - - bool Fizz(Func<string, bool> a) - { - return a("42"); - } - - public void NestedLambda2() - { - ToCode(X(), () => Fizz(x => x == "a")); - ToCode(X(), () => Fizz(x => x == 37)); - - ToCode(X(), () => Fizz((int x) => true)); - ToCode(X(), () => Buzz(x => true)); - } - - public void NewArrayAndExtensionMethod() - { - ToCode(X(), () => new[] { 1.0, 2.01, 3.5 }.SequenceEqual(new[] { 1.0, 2.01, 3.5 })); - } - - public void NewMultiDimArray() - { - ToCode(X(), () => new int[3, 4].Length == 1); - } - - public void NewObject() - { - ToCode(X(), () => new object() != new object()); - } - - public void NotOperator() - { - bool x = true; - int y = 3; - byte z = 42; - ToCode(X(), () => ~(int)z == 0); - ToCode(X(), () => ~y == 0); - ToCode(X(), () => !x); - } - - public void ObjectInitializers() - { - XmlReaderSettings s = new XmlReaderSettings { - CloseInput = false, - CheckCharacters = false - }; - ToCode(X(), () => new XmlReaderSettings { CloseInput = s.CloseInput, CheckCharacters = s.CheckCharacters }.Equals(s)); - } - - public void Quoted() - { - ToCode(X(), () => (Expression<Func<int, string, string>>)((n, s) => s + n.ToString()) != null); - } - - public void Quoted2() - { - ToCode(X(), () => ToCode(X(), () => true).Equals(null)); - } - - public void QuotedWithAnonymous() - { - ToCode(X(), () => new[] { new { X = "a", Y = "b" } }.Select(o => o.X + o.Y).Single()); - } - - public void StaticCall() - { - ToCode(X(), () => Equals(3, 0)); - } - - public void ThisCall() - { - ToCode(X(), () => !Equals(3)); - } - - public void ThisExplicit() - { - ToCode(X(), () => object.Equals(this, 3)); - } - - public void TypedConstant() - { - ToCode(X(), () => new[] { typeof(int), typeof(string) }); - } - - public void StaticCallImplicitCast() - { - ToCode(X(), () => Equals(3, 0)); - } - - public void StaticMembers() - { - ToCode(X(), () => (DateTime.Now > DateTime.Now + TimeSpan.FromMilliseconds(10.001)).ToString() == "False"); - } - - public void Strings() - { - int i = 1; - string x = "X"; - ToCode(X(), () => (("a\n\\b" ?? x) + x).Length == 2 ? false : true && (1m + (decimal)-i > 0m || false)); - } - - public void StringAccessor() - { - ToCode(X(), () => (int)"abc"[1] == 98); - } - - public void GenericClassInstance() - { - ToCode(X(), () => new GenericClass<int>().InstanceField + new GenericClass<double>().InstanceProperty); - } - - public void GenericClassStatic() - { - ToCode(X(), () => GenericClass<int>.StaticField + GenericClass<double>.StaticProperty); - } - - public void InvokeGenericMethod() - { - ToCode(X(), () => GenericClass<int>.GenericMethod<double>()); - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Generics.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Generics.cs deleted file mode 100644 index 9776d42f34..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/Generics.cs +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Collections.Generic; - -public static class Generics -{ - public class MyArray<T> - { - public class NestedClass<Y> - { - public T Item1; - public Y Item2; - } - - public enum NestedEnum - { - A, - B - } - - private T[] arr; - - public MyArray(int capacity) - { - this.arr = new T[capacity]; - } - - public void Size(int capacity) - { - Array.Resize<T>(ref this.arr, capacity); - } - - public void Grow(int capacity) - { - if (capacity >= this.arr.Length) - { - this.Size(capacity); - } - } - } - - public interface IInterface - { - void Method1<T>() where T : class; - void Method2<T>() where T : class; - } - - public abstract class Base : Generics.IInterface - { - // constraints must be repeated on implicit interface implementation - public abstract void Method1<T>() where T : class; - - // constraints must not be specified on explicit interface implementation - void Generics.IInterface.Method2<T>() - { - } - } - - public class Derived : Generics.Base - { - // constraints are inherited automatically and must not be specified - public override void Method1<T>() - { - } - } - - private const Generics.MyArray<string>.NestedEnum enumVal = Generics.MyArray<string>.NestedEnum.A; - private static Type type1 = typeof(List<>); - private static Type type2 = typeof(Generics.MyArray<>); - private static Type type3 = typeof(List<>.Enumerator); - private static Type type4 = typeof(Generics.MyArray<>.NestedClass<>); - private static Type type5 = typeof(List<int>[]); - private static Type type6 = typeof(Generics.MyArray<>.NestedEnum); - - public static void MethodWithConstraint<T, S>() where T : class, S where S : ICloneable, new() - { - } - - public static void MethodWithStructConstraint<T>() where T : struct - { - } - - private static void MultidimensionalArray<T>(T[,] array) - { - array[0, 0] = array[0, 1]; - } - - public static Dictionary<string, string>.KeyCollection.Enumerator GetEnumerator(Dictionary<string, string> d, Generics.MyArray<string>.NestedClass<int> nc) - { - // Tests references to inner classes in generic classes - return d.Keys.GetEnumerator(); - } - - public static bool IsString<T>(T input) - { - return input is string; - } - - public static string AsString<T>(T input) - { - return input as string; - } - - public static string CastToString<T>(T input) - { - return (string)((object)input); - } - - public static T CastFromString<T>(string input) - { - return (T)((object)input); - } - - public static bool IsInt<T>(T input) - { - return input is int; - } - - public static int CastToInt<T>(T input) - { - return (int)((object)input); - } - - public static T CastFromInt<T>(int input) - { - return (T)((object)input); - } - - public static bool IsNullableInt<T>(T input) - { - return input is int?; - } - - public static int? AsNullableInt<T>(T input) - { - return input as int?; - } - - public static int? CastToNullableInt<T>(T input) - { - return (int?)((object)input); - } - - public static T CastFromNullableInt<T>(int? input) - { - return (T)((object)input); - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Helpers/CodeAssert.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Helpers/CodeAssert.cs deleted file mode 100644 index d06e2db29d..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/Helpers/CodeAssert.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using DiffLib; -using NUnit.Framework; - -namespace ICSharpCode.Decompiler.Tests.Helpers -{ - public class CodeAssert - { - public static void AreEqual(string input1, string input2) - { - var diff = new StringWriter(); - if (!Compare(input1, input2, diff)) { - Assert.Fail(diff.ToString()); - } - } - - static bool Compare(string input1, string input2, StringWriter diff) - { - var differ = new AlignedDiff<string>( - NormalizeAndSplitCode(input1), - NormalizeAndSplitCode(input2), - new CodeLineEqualityComparer(), - new StringSimilarityComparer(), - new StringAlignmentFilter()); - - bool result = true, ignoreChange; - - int line1 = 0, line2 = 0; - - foreach (var change in differ.Generate()) { - switch (change.Change) { - case ChangeType.Same: - diff.Write("{0,4} {1,4} ", ++line1, ++line2); - diff.Write(" "); - diff.WriteLine(change.Element1); - break; - case ChangeType.Added: - diff.Write(" {1,4} ", line1, ++line2); - result &= ignoreChange = ShouldIgnoreChange(change.Element2); - diff.Write(ignoreChange ? " " : " + "); - diff.WriteLine(change.Element2); - break; - case ChangeType.Deleted: - diff.Write("{0,4} ", ++line1, line2); - result &= ignoreChange = ShouldIgnoreChange(change.Element1); - diff.Write(ignoreChange ? " " : " - "); - diff.WriteLine(change.Element1); - break; - case ChangeType.Changed: - diff.Write("{0,4} ", ++line1, line2); - result = false; - diff.Write("(-) "); - diff.WriteLine(change.Element1); - diff.Write(" {1,4} ", line1, ++line2); - diff.Write("(+) "); - diff.WriteLine(change.Element2); - break; - } - } - - return result; - } - - class CodeLineEqualityComparer : IEqualityComparer<string> - { - private IEqualityComparer<string> baseComparer = EqualityComparer<string>.Default; - - public bool Equals(string x, string y) - { - return baseComparer.Equals( - NormalizeLine(x), - NormalizeLine(y) - ); - } - - public int GetHashCode(string obj) - { - return baseComparer.GetHashCode(NormalizeLine(obj)); - } - } - - private static string NormalizeLine(string line) - { - line = line.Trim(); - var index = line.IndexOf("//"); - if (index >= 0) { - return line.Substring(0, index); - } else if (line.StartsWith("#")) { - return string.Empty; - } else { - return line; - } - } - - private static bool ShouldIgnoreChange(string line) - { - // for the result, we should ignore blank lines and added comments - return NormalizeLine(line) == string.Empty; - } - - private static IEnumerable<string> NormalizeAndSplitCode(string input) - { - return input.Split(new[] { "\r\n", "\n\r", "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs deleted file mode 100644 index cb65620242..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using ICSharpCode.Decompiler.Ast.Transforms; -using ICSharpCode.NRefactory.CSharp; - -namespace ICSharpCode.Decompiler.Tests.Helpers -{ - class RemoveCompilerAttribute : DepthFirstAstVisitor<object, object>, IAstTransform - { - public override object VisitAttribute(NRefactory.CSharp.Attribute attribute, object data) - { - var section = (AttributeSection)attribute.Parent; - SimpleType type = attribute.Type as SimpleType; - if (section.AttributeTarget == "assembly" && - (type.Identifier == "CompilationRelaxations" || type.Identifier == "RuntimeCompatibility" || type.Identifier == "SecurityPermission" || type.Identifier == "AssemblyVersion" || type.Identifier == "Debuggable")) - { - attribute.Remove(); - if (section.Attributes.Count == 0) - section.Remove(); - } - if (section.AttributeTarget == "module" && type.Identifier == "UnverifiableCode") - { - attribute.Remove(); - if (section.Attributes.Count == 0) - section.Remove(); - } - return null; - } - - public void Run(AstNode node) - { - node.AcceptVisitor(this, null); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/main/contrib/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj deleted file mode 100644 index 8bb4934e78..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj +++ /dev/null @@ -1,139 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build"> - <PropertyGroup> - <ProjectGuid>{FEC0DA52-C4A6-4710-BE36-B484A20C5E22}</ProjectGuid> - <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> - <Platform Condition=" '$(Platform)' == '' ">x86</Platform> - <OutputType>Library</OutputType> - <RootNamespace>ICSharpCode.Decompiler.Tests</RootNamespace> - <AssemblyName>ICSharpCode.Decompiler.Tests</AssemblyName> - <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> - <AppDesignerFolder>Properties</AppDesignerFolder> - <AllowUnsafeBlocks>True</AllowUnsafeBlocks> - <NoStdLib>False</NoStdLib> - <WarningLevel>4</WarningLevel> - <TreatWarningsAsErrors>false</TreatWarningsAsErrors> - <CheckForOverflowUnderflow>False</CheckForOverflowUnderflow> - <NoWarn>67,169,1058,728,1720,649</NoWarn> - <TargetFrameworkProfile /> - </PropertyGroup> - <PropertyGroup Condition=" '$(Platform)' == 'x86' "> - <PlatformTarget>x86</PlatformTarget> - <RegisterForComInterop>False</RegisterForComInterop> - <GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies> - <BaseAddress>4194304</BaseAddress> - <FileAlignment>4096</FileAlignment> - </PropertyGroup> - <PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' "> - <PlatformTarget>AnyCPU</PlatformTarget> - <RegisterForComInterop>False</RegisterForComInterop> - <GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies> - <BaseAddress>4194304</BaseAddress> - <FileAlignment>4096</FileAlignment> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> - <OutputPath>..\bin\Debug\</OutputPath> - <DebugSymbols>true</DebugSymbols> - <DebugType>Full</DebugType> - <Optimize>False</Optimize> - <DefineConstants>DEBUG;TRACE</DefineConstants> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)' == 'Release' "> - <OutputPath>..\bin\Release\</OutputPath> - <DebugSymbols>false</DebugSymbols> - <DebugType>None</DebugType> - <Optimize>True</Optimize> - <DefineConstants>TRACE</DefineConstants> - </PropertyGroup> - <ItemGroup> - <Reference Include="DiffLib"> - <HintPath>..\..\packages\DiffLib.1.0.0.55\lib\net35-Client\DiffLib.dll</HintPath> - </Reference> - <Reference Include="nunit.framework"> - <SpecificVersion>False</SpecificVersion> - <HintPath>.\nunit.framework.dll</HintPath> - </Reference> - <Reference Include="System" /> - <Reference Include="System.Core"> - <RequiredTargetFramework>3.5</RequiredTargetFramework> - </Reference> - <Reference Include="System.Xml" /> - <Reference Include="System.Xml.Linq"> - <RequiredTargetFramework>3.5</RequiredTargetFramework> - </Reference> - </ItemGroup> - <ItemGroup> - <Compile Include="Async.cs" /> - <Compile Include="CallOverloadedMethod.cs" /> - <Compile Include="CheckedUnchecked.cs" /> - <Compile Include="ControlFlow.cs" /> - <Compile Include="DoubleConstants.cs" /> - <Compile Include="ExpressionTrees.cs" /> - <None Include="IL\SequenceOfNestedIfs.Output.cs" /> - <Compile Include="IL\ILTests.cs" /> - <Compile Include="LiftedOperators.cs" /> - <Compile Include="CustomShortCircuitOperators.cs" /> - <Compile Include="Helpers\CodeAssert.cs" /> - <Compile Include="IncrementDecrement.cs" /> - <Compile Include="Lock.cs" /> - <Compile Include="PInvoke.cs" /> - <Compile Include="QueryExpressions.cs" /> - <Compile Include="Switch.cs" /> - <Compile Include="TypeAnalysisTests.cs" /> - <Compile Include="UndocumentedExpressions.cs" /> - <Compile Include="UnsafeCode.cs" /> - <Compile Include="Types\S_TypeDeclarations.cs" /> - <Compile Include="YieldReturn.cs" /> - <None Include="IL\SequenceOfNestedIfs.dll" /> - <None Include="IL\SequenceOfNestedIfs.il" /> - <None Include="IL\StackTests.exe" /> - <None Include="IL\StackTests.il" /> - <None Include="packages.config" /> - <None Include="Types\S_EnumSamples.cs" /> - <None Include="CustomAttributes\S_AssemblyCustomAttribute.cs" /> - <Compile Include="Helpers\RemoveCompilerAttribute.cs" /> - <Compile Include="Types\S_TypeMemberDeclarations.cs" /> - <Compile Include="Types\EnumTests.cs" /> - <Compile Include="Types\TypeTests.cs" /> - <Compile Include="DelegateConstruction.cs" /> - <None Include="CustomAttributes\S_CustomAttributes.cs" /> - <Compile Include="Loops.cs" /> - <Compile Include="PropertiesAndEvents.cs" /> - <None Include="CustomAttributes\S_CustomAttributeSamples.cs" /> - <Compile Include="CodeSampleFileParser.cs" /> - <Compile Include="CustomAttributes\CustomAttributeTests.cs" /> - <Compile Include="DecompilerTestBase.cs" /> - <Compile Include="InitializerTests.cs" /> - <Compile Include="ExceptionHandling.cs" /> - <Compile Include="Generics.cs" /> - <Compile Include="MultidimensionalArray.cs" /> - <Compile Include="TestRunner.cs" /> - <Compile Include="ValueTypes.cs" /> - </ItemGroup> - <ItemGroup> - <ProjectReference Include="..\..\Mono.Cecil\Mono.Cecil.csproj"> - <Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project> - <Name>Mono.Cecil</Name> - </ProjectReference> - <ProjectReference Include="..\..\NRefactory\ICSharpCode.NRefactory.CSharp\ICSharpCode.NRefactory.CSharp.csproj"> - <Project>{53DCA265-3C3C-42F9-B647-F72BA678122B}</Project> - <Name>ICSharpCode.NRefactory.CSharp</Name> - </ProjectReference> - <ProjectReference Include="..\..\NRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj"> - <Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project> - <Name>ICSharpCode.NRefactory</Name> - </ProjectReference> - <ProjectReference Include="..\ICSharpCode.Decompiler.csproj"> - <Project>{984CC812-9470-4A13-AFF9-CC44068D666C}</Project> - <Name>ICSharpCode.Decompiler</Name> - </ProjectReference> - </ItemGroup> - <ItemGroup /> - <ItemGroup> - <None Include="BooleanConsumedAsInteger.il" /> - </ItemGroup> - <ItemGroup> - <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> - </ItemGroup> - <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" /> -</Project>
\ No newline at end of file diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IL/ILTests.cs b/main/contrib/ICSharpCode.Decompiler/Tests/IL/ILTests.cs deleted file mode 100644 index f06f3108c5..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/IL/ILTests.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 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.IO; -using ICSharpCode.Decompiler.Ast; -using ICSharpCode.Decompiler.Tests.Helpers; -using Mono.Cecil; -using NUnit.Framework; - -namespace ICSharpCode.Decompiler.Tests -{ - [TestFixture] - public class ILTests - { - const string path = "../../Tests/IL"; - - [Test] - public void SequenceOfNestedIfs() - { - Run("SequenceOfNestedIfs.dll", "SequenceOfNestedIfs.Output.cs"); - } - - void Run(string compiledFile, string expectedOutputFile) - { - string expectedOutput = File.ReadAllText(Path.Combine(path, expectedOutputFile)); - var assembly = AssemblyDefinition.ReadAssembly(Path.Combine(path, compiledFile)); - AstBuilder decompiler = new AstBuilder(new DecompilerContext(assembly.MainModule)); - decompiler.AddAssembly(assembly); - new Helpers.RemoveCompilerAttribute().Run(decompiler.SyntaxTree); - StringWriter output = new StringWriter(); - decompiler.GenerateCode(new PlainTextOutput(output)); - CodeAssert.AreEqual(expectedOutput, output.ToString()); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.Output.cs b/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.Output.cs deleted file mode 100644 index 754d7dafdc..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.Output.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -[Serializable] -public class Material -{ - public static implicit operator bool(Material m) - { - return m == null; - } -} -[Serializable] -public class SequenceOfNestedIfs -{ - public bool _clear; - public Material _material; - public override bool CheckShader() - { - return false; - } - public override void CreateMaterials() - { - if (!this._clear) - { - if (!this.CheckShader()) - { - return; - } - this._material = new Material(); - } - if (!this._material) - { - if (!this.CheckShader()) - { - return; - } - this._material = new Material(); - } - if (!this._material) - { - if (!this.CheckShader()) - { - return; - } - this._material = new Material(); - } - if (!this._material) - { - if (this.CheckShader()) - { - this._material = new Material(); - } - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.dll b/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.dll Binary files differdeleted file mode 100644 index f517d4546d..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.dll +++ /dev/null diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.il b/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.il deleted file mode 100644 index 9c9b749ce4..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.il +++ /dev/null @@ -1,140 +0,0 @@ -// Metadata version: v2.0.50727 -.assembly extern mscorlib -{ - .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. - .ver 2:0:0:0 -} - -.assembly SequenceOfNestedIfs -{ - .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx - 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. - .hash algorithm 0x00008004 - .ver 0:0:0:0 -} -.module SequenceOfNestedIfs -// MVID: {DCEC8A87-5679-4EBE-89A3-51274D8B5446} -.imagebase 0x00400000 -.file alignment 0x00000200 -.stackreserve 0x00100000 -.subsystem 0x0003 // WINDOWS_CUI -.corflags 0x00000001 // ILONLY -// Image base: 0x01D60000 - - -// =============== CLASS MEMBERS DECLARATION =================== - -.class public auto ansi serializable beforefieldinit Material - extends [mscorlib]System.Object -{ - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - // Code size 7 (0x7) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } // end of method Material::.ctor - - .method public hidebysig specialname static - bool op_Implicit(class Material m) cil managed - { - // Code size 11 (0xb) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: ldnull - IL_0008: ceq - IL_000a: ret - } // end of method Material::op_Implicit - -} // end of class Material - -.class public auto ansi serializable beforefieldinit SequenceOfNestedIfs - extends [mscorlib]System.Object -{ - .field public bool _clear - .field public class Material _material - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - // Code size 7 (0x7) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } // end of method SequenceOfNestedIfs::.ctor - - .method public hidebysig virtual instance bool - CheckShader() cil managed - { - // Code size 2 (0x2) - .maxstack 8 - IL_0000: ldc.i4.0 - IL_0001: ret - } // end of method SequenceOfNestedIfs::CheckShader - - .method public hidebysig virtual instance void - CreateMaterials() cil managed - { - // Code size 168 (0xa8) - .maxstack 13 - IL_0000: ldarg.0 - IL_0001: ldfld bool SequenceOfNestedIfs::_clear - IL_0006: brtrue IL_0026 - - IL_000b: ldarg.0 - IL_000c: callvirt instance bool SequenceOfNestedIfs::CheckShader() - IL_0011: brtrue IL_001b - - IL_0016: br IL_00a7 - - IL_001b: ldarg.0 - IL_001c: newobj instance void Material::.ctor() - IL_0021: stfld class Material SequenceOfNestedIfs::_material - IL_0026: ldarg.0 - IL_0027: ldfld class Material SequenceOfNestedIfs::_material - IL_002c: call bool Material::op_Implicit(class Material) - IL_0031: brtrue IL_0051 - - IL_0036: ldarg.0 - IL_0037: callvirt instance bool SequenceOfNestedIfs::CheckShader() - IL_003c: brtrue IL_0046 - - IL_0041: br IL_00a7 - - IL_0046: ldarg.0 - IL_0047: newobj instance void Material::.ctor() - IL_004c: stfld class Material SequenceOfNestedIfs::_material - IL_0051: ldarg.0 - IL_0052: ldfld class Material SequenceOfNestedIfs::_material - IL_0057: call bool Material::op_Implicit(class Material) - IL_005c: brtrue IL_007c - - IL_0061: ldarg.0 - IL_0062: callvirt instance bool SequenceOfNestedIfs::CheckShader() - IL_0067: brtrue IL_0071 - - IL_006c: br IL_00a7 - - IL_0071: ldarg.0 - IL_0072: newobj instance void Material::.ctor() - IL_0077: stfld class Material SequenceOfNestedIfs::_material - IL_007c: ldarg.0 - IL_007d: ldfld class Material SequenceOfNestedIfs::_material - IL_0082: call bool Material::op_Implicit(class Material) - IL_0087: brtrue IL_00a7 - - IL_008c: ldarg.0 - IL_008d: callvirt instance bool SequenceOfNestedIfs::CheckShader() - IL_0092: brtrue IL_009c - - IL_0097: br IL_00a7 - - IL_009c: ldarg.0 - IL_009d: newobj instance void Material::.ctor() - IL_00a2: stfld class Material SequenceOfNestedIfs::_material - IL_00a7: ret - } // end of method SequenceOfNestedIfs::CreateMaterials - -} // end of class SequenceOfNestedIfs
\ No newline at end of file diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.exe b/main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.exe Binary files differdeleted file mode 100644 index db6194ad68..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.exe +++ /dev/null diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.il b/main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.il deleted file mode 100644 index 51cee37549..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.il +++ /dev/null @@ -1,132 +0,0 @@ -.assembly extern mscorlib -{ - .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) - .ver 4:0:0:0 -} -.assembly StackTests -{ - .hash algorithm 0x00008004 - .ver 1:0:4059:39717 -} -.module StackTests.exe -.imagebase 0x00400000 -.file alignment 0x00000200 -.stackreserve 0x00100000 -.subsystem 0x0003 // WINDOWS_CUI -.corflags 0x00000003 // ILONLY 32BITREQUIRED - -.class private auto ansi beforefieldinit StackTests.Program extends [mscorlib]System.Object -{ - .method public hidebysig static void Main(string[] args) cil managed - { - .entrypoint - .maxstack 8 - - ldc.i4.0 - call string StackTests.Program::Test1(bool cond) - call void [mscorlib]System.Console::WriteLine(string) // false - - ldc.i4.1 - call string StackTests.Program::Test1(bool cond) - call void [mscorlib]System.Console::WriteLine(string) // true - - ldc.i4.0 - ldc.i4.0 - ldc.i4.0 - call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2) - call void [mscorlib]System.Console::WriteLine(int32) // 11 - - ldc.i4.0 - ldc.i4.1 - ldc.i4.0 - call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2) - call void [mscorlib]System.Console::WriteLine(int32) // 21 - - ldc.i4.1 - ldc.i4.1 - ldc.i4.1 - call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2) - call void [mscorlib]System.Console::WriteLine(int32) // 32 - - ldc.i4.2 - ldc.i4.1 - ldc.i4.0 - call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2) - call void [mscorlib]System.Console::WriteLine(int32) // 23 - - ret - } - - .method public hidebysig static string Test1(bool cond) cil managed - { - ldarg.0 - brtrue TRUE - - FALSE: - ldstr "false" - br EXIT - - TRUE: - ldstr "true" - - EXIT: - ret - } - - .method public hidebysig static int32 Test2(int32 switch1, int32 br1, int32 br2) cil managed - { - ldarg.0 - switch (ENTRY1, ENTRY2, ENTRY3) - ldc.i4.0 - ret - - ENTRY1: - ldc.i4.1 - br BRANCH1 - - ENTRY2: - ldc.i4.2 - br BRANCH1 - - ENTRY3: - ldc.i4.3 - br BRANCH2 - - BRANCH1: - ldarg.1 - brtrue BRANCH2 - - EXIT1: - ldc.i4 10 - add - ret - - BRANCH2: - ldarg.2 - brtrue.s EXIT3 - - EXIT2: - ldc.i4 20 - add - ret - - EXIT3: - ldc.i4 30 - add - ret - } - - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - // Code size 7 (0x7) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } // end of method Program::.ctor - -} // end of class StackTests.Program - - -// ============================================================= diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IncrementDecrement.cs b/main/contrib/ICSharpCode.Decompiler/Tests/IncrementDecrement.cs deleted file mode 100644 index e06b680d68..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/IncrementDecrement.cs +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright (c) 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; - -public class IncrementDecrement -{ - [Flags] - private enum MyEnum - { - None = 0, - One = 1, - Two = 2, - Four = 4 - } - - public class MutableClass - { - public int Field; - - public int Property - { - get; - set; - } - - public uint this[string name] - { - get - { - return 0u; - } - set - { - } - } - } - - private IncrementDecrement.MyEnum enumField; - public static int StaticField; - - public static int StaticProperty - { - get; - set; - } - - private IncrementDecrement.MutableClass M() - { - return new IncrementDecrement.MutableClass(); - } - - private int[,] Array() - { - return null; - } - - private unsafe int* GetPointer() - { - return null; - } - - public int PreIncrementInAddition(int i, int j) - { - return i + ++j; - } - - public int PreIncrementArrayElement(int[] array, int pos) - { - return --array[pos]; - } - - public int PreIncrementInstanceField() - { - return ++this.M().Field; - } - - public int PreIncrementInstanceField2(IncrementDecrement.MutableClass m) - { - return ++m.Field; - } - - public int PreIncrementInstanceProperty() - { - return ++this.M().Property; - } - - public int PreIncrementStaticField() - { - return ++IncrementDecrement.StaticField; - } - - public int PreIncrementStaticProperty() - { - return ++IncrementDecrement.StaticProperty; - } - -// public uint PreIncrementIndexer(string name) -// { -// return ++this.M()[name]; -// } - - public int PreIncrementByRef(ref int i) - { - return ++i; - } - - public unsafe int PreIncrementByPointer() - { - return ++(*this.GetPointer()); - } - - public int PreIncrement2DArray() - { - return ++this.Array()[1, 2]; - } - - public int CompoundAssignInstanceField() - { - return this.M().Field *= 10; - } - - public int CompoundAssignInstanceProperty() - { - return this.M().Property *= 10; - } - - public int CompoundAssignStaticField() - { - return IncrementDecrement.StaticField ^= 100; - } - - public int CompoundAssignStaticProperty() - { - return IncrementDecrement.StaticProperty &= 10; - } - - public int CompoundAssignArrayElement1(int[] array, int pos) - { - return array[pos] *= 10; - } - - public int CompoundAssignArrayElement2(int[] array) - { - return array[Environment.TickCount] *= 10; - } - -// public uint CompoundAssignIndexer(string name) -// { -// return this.M()[name] -= 2; -// } - - public int CompoundAssignIncrement2DArray() - { - return this.Array()[1, 2] %= 10; - } - - public int CompoundAssignByRef(ref int i) - { - return i <<= 2; - } - - public unsafe double CompoundAssignByPointer(double* ptr) - { - return *ptr /= 1.5; - } - - public void CompoundAssignEnum() - { - this.enumField |= IncrementDecrement.MyEnum.Two; - this.enumField &= ~IncrementDecrement.MyEnum.Four; - } - - public int PostIncrementInAddition(int i, int j) - { - return i++ + j; - } - - public void PostIncrementInlineLocalVariable(Func<int, int> f) - { - int num = 0; - f(num++); - } - - public int PostIncrementArrayElement(int[] array, int pos) - { - return array[pos]--; - } - - public int PostIncrementStaticField() - { - return IncrementDecrement.StaticField++; - } - - public int PostIncrementStaticProperty() - { - return IncrementDecrement.StaticProperty++; - } - - public int PostIncrementInstanceField(IncrementDecrement.MutableClass m) - { - return m.Field++; - } - -// public uint PostIncrementIndexer(string name) -// { -// return this.M()[name]++; -// } - -// public unsafe int PostIncrementOfPointer(int* ptr) -// { -// return *(ptr++); -// } - - public int PostIncrementInstanceField() - { - return this.M().Field--; - } - - public int PostIncrementInstanceProperty() - { - return this.M().Property--; - } - - public int PostIncrement2DArray() - { - return this.Array()[IncrementDecrement.StaticField, IncrementDecrement.StaticProperty]++; - } - - public int PostIncrementByRef(ref int i) - { - return i++; - } - - public unsafe int PostIncrementByPointer() - { - return (*this.GetPointer())++; - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/InitializerTests.cs b/main/contrib/ICSharpCode.Decompiler/Tests/InitializerTests.cs deleted file mode 100644 index 29e3d1d595..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/InitializerTests.cs +++ /dev/null @@ -1,885 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Collections.Generic; - -public class InitializerTests -{ - private enum MyEnum - { - a, - b - } - - private enum MyEnum2 - { - c, - d - } - - private class Data - { - public List<InitializerTests.MyEnum2> FieldList = new List<InitializerTests.MyEnum2>(); - public InitializerTests.MyEnum a - { - get; - set; - } - public List<InitializerTests.MyEnum2> PropertyList - { - get; - set; - } - - public InitializerTests.Data MoreData - { - get; - set; - } - - public InitializerTests.StructData NestedStruct - { - get; - set; - } - } - - private struct StructData - { - public int Field; - public int Property - { - get; - set; - } - - public InitializerTests.Data MoreData - { - get; - set; - } - - public StructData(int initialValue) - { - this = default(InitializerTests.StructData); - this.Field = initialValue; - this.Property = initialValue; - } - } - - // Helper methods used to ensure initializers used within expressions work correctly - private static void X(object a, object b) - { - } - - private static object Y() - { - return null; - } - - #region Array Initializers - public static void Array1() - { - InitializerTests.X(InitializerTests.Y(), new int[] - { - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10 - }); - } - - public static void Array2(int a, int b, int c) - { - InitializerTests.X(InitializerTests.Y(), new int[] - { - a, - 0, - b, - 0, - c - }); - } - - public static void NestedArray(int a, int b, int c) - { - InitializerTests.X(InitializerTests.Y(), new int[][] - { - new int[] - { - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10 - }, - new int[] - { - a, - b, - c - }, - new int[] - { - 1, - 2, - 3, - 4, - 5, - 6 - } - }); - } - - public static void ArrayBoolean() - { - InitializerTests.X(InitializerTests.Y(), new bool[] - { - true, - false, - true, - false, - false, - false, - true, - true - }); - } - - public static void ArrayByte() - { - InitializerTests.X(InitializerTests.Y(), new byte[] - { - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 254, - 255 - }); - } - - public static void ArraySByte() - { - InitializerTests.X(InitializerTests.Y(), new sbyte[] - { - -128, - -127, - 0, - 1, - 2, - 3, - 4, - 127 - }); - } - - public static void ArrayShort() - { - InitializerTests.X(InitializerTests.Y(), new short[] - { - -32768, - -1, - 0, - 1, - 32767 - }); - } - - public static void ArrayUShort() - { - InitializerTests.X(InitializerTests.Y(), new ushort[] - { - 0, - 1, - 32767, - 32768, - 65534, - 65535 - }); - } - - public static void ArrayInt() - { - InitializerTests.X(InitializerTests.Y(), new int[] - { - 1, - -2, - 2000000000, - 4, - 5, - -6, - 7, - 8, - 9, - 10 - }); - } - - public static void ArrayUInt() - { - InitializerTests.X(InitializerTests.Y(), new uint[] - { - 1u, - 2000000000u, - 3000000000u, - 4u, - 5u, - 6u, - 7u, - 8u, - 9u, - 10u - }); - } - - public static void ArrayLong() - { - InitializerTests.X(InitializerTests.Y(), new long[] - { - -4999999999999999999L, - -1L, - 0L, - 1L, - 4999999999999999999L - }); - } - - public static void ArrayULong() - { - InitializerTests.X(InitializerTests.Y(), new ulong[] - { - 1uL, - 2000000000uL, - 3000000000uL, - 4uL, - 5uL, - 6uL, - 7uL, - 8uL, - 4999999999999999999uL, - 9999999999999999999uL - }); - } - - public static void ArrayFloat() - { - InitializerTests.X(InitializerTests.Y(), new float[] - { - -1.5f, - 0f, - 1.5f, - float.NegativeInfinity, - float.PositiveInfinity, - float.NaN - }); - } - - public static void ArrayDouble() - { - InitializerTests.X(InitializerTests.Y(), new double[] - { - -1.5, - 0.0, - 1.5, - double.NegativeInfinity, - double.PositiveInfinity, - double.NaN - }); - } - - public static void ArrayDecimal() - { - InitializerTests.X(InitializerTests.Y(), new decimal[] - { - -100m, - 0m, - 100m, - -79228162514264337593543950335m, - 79228162514264337593543950335m, - 0.0000001m - }); - } - - public static void ArrayString() - { - InitializerTests.X(InitializerTests.Y(), new string[] - { - "", - null, - "Hello", - "World" - }); - } - - public static void ArrayEnum() - { - InitializerTests.X(InitializerTests.Y(), new InitializerTests.MyEnum[] - { - InitializerTests.MyEnum.a, - InitializerTests.MyEnum.b, - InitializerTests.MyEnum.a, - InitializerTests.MyEnum.b - }); - } - - public static void RecursiveArrayInitializer() - { - int[] array = new int[3]; - array[0] = 1; - array[1] = 2; - array[2] = array[1] + 1; - array[0] = 0; - } - #endregion - - public static void CollectionInitializerList() - { - InitializerTests.X(InitializerTests.Y(), new List<int> - { - 1, - 2, - 3 - }); - } - - public static object RecursiveCollectionInitializer() - { - List<object> list = new List<object>(); - list.Add(list); - return list; - } - - public static void CollectionInitializerDictionary() - { - InitializerTests.X(InitializerTests.Y(), new Dictionary<string, int> - { - { - "First", - 1 - }, - { - "Second", - 2 - }, - { - "Third", - 3 - } - }); - } - - public static void CollectionInitializerDictionaryWithEnumTypes() - { - InitializerTests.X(InitializerTests.Y(), new Dictionary<InitializerTests.MyEnum, InitializerTests.MyEnum2> - { - { - InitializerTests.MyEnum.a, - InitializerTests.MyEnum2.c - }, - { - InitializerTests.MyEnum.b, - InitializerTests.MyEnum2.d - } - }); - } - - public static void NotACollectionInitializer() - { - List<int> list = new List<int>(); - list.Add(1); - list.Add(2); - list.Add(3); - InitializerTests.X(InitializerTests.Y(), list); - } - - public static void ObjectInitializer() - { - InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data - { - a = InitializerTests.MyEnum.a - }); - } - - public static void NotAObjectInitializer() - { - InitializerTests.Data data = new InitializerTests.Data(); - data.a = InitializerTests.MyEnum.a; - InitializerTests.X(InitializerTests.Y(), data); - } - - public static void ObjectInitializerAssignCollectionToField() - { - InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data - { - a = InitializerTests.MyEnum.a, - FieldList = new List<InitializerTests.MyEnum2> - { - InitializerTests.MyEnum2.c, - InitializerTests.MyEnum2.d - } - }); - } - - public static void ObjectInitializerAddToCollectionInField() - { - InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data - { - a = InitializerTests.MyEnum.a, - FieldList = - { - InitializerTests.MyEnum2.c, - InitializerTests.MyEnum2.d - } - }); - } - - public static void ObjectInitializerAssignCollectionToProperty() - { - InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data - { - a = InitializerTests.MyEnum.a, - PropertyList = new List<InitializerTests.MyEnum2> - { - InitializerTests.MyEnum2.c, - InitializerTests.MyEnum2.d - } - }); - } - - public static void ObjectInitializerAddToCollectionInProperty() - { - InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data - { - a = InitializerTests.MyEnum.a, - PropertyList = - { - InitializerTests.MyEnum2.c, - InitializerTests.MyEnum2.d - } - }); - } - - public static void ObjectInitializerWithInitializationOfNestedObjects() - { - InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data - { - MoreData = - { - a = InitializerTests.MyEnum.a - } - }); - } - - public static void StructInitializer_DefaultConstructor() - { - InitializerTests.X(InitializerTests.Y(), new InitializerTests.StructData - { - Field = 1, - Property = 2 - }); - } - - public static void StructInitializer_ExplicitConstructor() - { - InitializerTests.X(InitializerTests.Y(), new InitializerTests.StructData(0) - { - Field = 1, - Property = 2 - }); - } - - public static void StructInitializerWithInitializationOfNestedObjects() - { - InitializerTests.X(InitializerTests.Y(), new InitializerTests.StructData - { - MoreData = - { - a = InitializerTests.MyEnum.a, - FieldList = - { - InitializerTests.MyEnum2.c, - InitializerTests.MyEnum2.d - } - } - }); - } - - public static void StructInitializerWithinObjectInitializer() - { - InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data - { - NestedStruct = new InitializerTests.StructData(2) - { - Field = 1, - Property = 2 - } - }); - } - - public int[,] MultidimensionalInit() - { - return new int[,] - { - - { - 0, - 0, - 0, - 0 - }, - - { - 1, - 1, - 1, - 1 - }, - - { - 0, - 0, - 0, - 0 - }, - - { - 0, - 0, - 0, - 0 - }, - - { - 0, - 0, - 1, - 0 - }, - - { - 0, - 0, - 1, - 0 - }, - - { - 0, - 0, - 1, - 0 - }, - - { - 0, - 0, - 1, - 0 - }, - - { - 0, - 0, - 0, - 0 - }, - - { - 1, - 1, - 1, - 1 - }, - - { - 0, - 0, - 0, - 0 - }, - - { - 0, - 0, - 0, - 0 - }, - - { - 0, - 0, - 1, - 0 - }, - - { - 0, - 0, - 1, - 0 - }, - - { - 0, - 0, - 1, - 0 - }, - - { - 0, - 0, - 1, - 0 - } - }; - } - - public int[][,] MultidimensionalInit2() - { - return new int[][,] - { - new int[,] - { - - { - 0, - 0, - 0, - 0 - }, - - { - 1, - 1, - 1, - 1 - }, - - { - 0, - 0, - 0, - 0 - }, - - { - 0, - 0, - 0, - 0 - } - - }, - new int[,] - { - - { - 0, - 0, - 1, - 0 - }, - - { - 0, - 0, - 1, - 0 - }, - - { - 0, - 0, - 1, - 0 - }, - - { - 0, - 0, - 1, - 0 - } - - }, - new int[,] - { - - { - 0, - 0, - 0, - 0 - }, - - { - 1, - 1, - 1, - 1 - }, - - { - 0, - 0, - 0, - 0 - }, - - { - 0, - 0, - 0, - 0 - } - }, - new int[,] - { - - { - 0, - 0, - 1, - 0 - }, - - { - 0, - 0, - 1, - 0 - }, - - { - 0, - 0, - 1, - 0 - }, - - { - 0, - 0, - 1, - 0 - } - - } - }; - } - - public int[][,,] ArrayOfArrayOfArrayInit() - { - return new int[][,,] - { - new int[,,] - { - { - { - 1, - 2, - 3 - }, - { - 4, - 5, - 6 - }, - { - 7, - 8, - 9 - } - }, - { - { - 11, - 12, - 13 - }, - { - 14, - 15, - 16 - }, - { - 17, - 18, - 19 - } - } - }, - - new int[,,] - { - { - { - 21, - 22, - 23 - }, - { - 24, - 25, - 26 - }, - { - 27, - 28, - 29 - } - }, - { - { - 31, - 32, - 33 - }, - { - 34, - 35, - 36 - }, - { - 37, - 38, - 39 - } - } - } - }; - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/LiftedOperators.cs b/main/contrib/ICSharpCode.Decompiler/Tests/LiftedOperators.cs deleted file mode 100644 index 426276ab0a..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/LiftedOperators.cs +++ /dev/null @@ -1,830 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Runtime.InteropServices; - -public static class LiftedOperators -{ - // C# uses 4 different patterns of IL for lifted operators: bool, other primitive types, decimal, other structs. - // Different patterns are used depending on whether both of the operands are nullable or only the left/right operand is nullable. - // Negation must not be pushed through such comparisons because it would change the semantics. - // A comparison used in a condition differs somewhat from a comparison used as a simple value. - - public static void BoolBasic(bool? a, bool? b) - { - if (a == b) - { - Console.WriteLine(); - } - if (a != b) - { - Console.WriteLine(); - } - - if (!(a == b)) - { - Console.WriteLine(); - } - if (!(a != b)) - { - Console.WriteLine(); - } - } - - public static void BoolComplex(bool? a, Func<bool> x) - { - if (a == x()) - { - Console.WriteLine(); - } - if (a != x()) - { - Console.WriteLine(); - } - - if (x() == a) - { - Console.WriteLine(); - } - if (x() != a) - { - Console.WriteLine(); - } - - if (!(a == x())) - { - Console.WriteLine(); - } - if (!(a != x())) - { - Console.WriteLine(); - } - if (!(x() == a)) - { - Console.WriteLine(); - } - if (!(x() != a)) - { - Console.WriteLine(); - } - } - - public static void BoolConst(bool? a) - { - if (a == true) - { - Console.WriteLine(); - } - if (a != true) - { - Console.WriteLine(); - } - if (a == false) - { - Console.WriteLine(); - } - if (a != false) - { - Console.WriteLine(); - } - if (a ?? true) - { - Console.WriteLine(); - } - if (a ?? false) - { - Console.WriteLine(); - } - } - - public static void BoolValueBasic(bool? a, bool? b) - { - Console.WriteLine(a == b); - Console.WriteLine(a != b); - - Console.WriteLine(!(a == b)); - Console.WriteLine(!(a != b)); - - Console.WriteLine(a & b); - Console.WriteLine(a | b); - Console.WriteLine(a ^ b); - Console.WriteLine(a ?? b); - Console.WriteLine(!a); - a &= b; - a |= b; - a ^= b; - } - - public static void BoolValueComplex(bool? a, Func<bool> x) - { - Console.WriteLine(a == x()); - Console.WriteLine(a != x()); - - Console.WriteLine(x() == a); - Console.WriteLine(x() != a); - - Console.WriteLine(!(a == x())); - Console.WriteLine(!(a != x())); - - Console.WriteLine(a & x()); - Console.WriteLine(a | x()); - Console.WriteLine(a ^ x()); - Console.WriteLine(a ?? x()); - a &= x(); - a |= x(); - a ^= x(); - - Console.WriteLine(x() ^ a); - (new bool?[0])[0] ^= x(); - } - - public static void BoolValueConst(bool? a) - { - Console.WriteLine(a == true); - Console.WriteLine(a != true); - Console.WriteLine(a == false); - Console.WriteLine(a != false); - Console.WriteLine(a ?? true); - Console.WriteLine(a ?? false); - } - - public static void IntBasic(int? a, int? b) - { - if (a == b) - { - Console.WriteLine(); - } - if (a != b) - { - Console.WriteLine(); - } - if (a > b) - { - Console.WriteLine(); - } - if (a < b) - { - Console.WriteLine(); - } - if (a >= b) - { - Console.WriteLine(); - } - if (a <= b) - { - Console.WriteLine(); - } - - if (!(a == b)) - { - Console.WriteLine(); - } - if (!(a != b)) - { - Console.WriteLine(); - } - if (!(a > b)) - { - Console.WriteLine(); - } - } - - public static void IntComplex(int? a, Func<int> x) - { - if (a == x()) - { - Console.WriteLine(); - } - if (a != x()) - { - Console.WriteLine(); - } - if (a > x()) - { - Console.WriteLine(); - } - - if (x() == a) - { - Console.WriteLine(); - } - if (x() != a) - { - Console.WriteLine(); - } - if (x() > a) - { - Console.WriteLine(); - } - - if (!(a == x())) - { - Console.WriteLine(); - } - if (!(a != x())) - { - Console.WriteLine(); - } - if (!(a > x())) - { - Console.WriteLine(); - } - } - - public static void IntConst(int? a) - { - if (a == 2) - { - Console.WriteLine(); - } - if (a != 2) - { - Console.WriteLine(); - } - if (a > 2) - { - Console.WriteLine(); - } - - if (2 == a) - { - Console.WriteLine(); - } - if (2 != a) - { - Console.WriteLine(); - } - if (2 > a) - { - Console.WriteLine(); - } - } - - public static void IntValueBasic(int? a, int? b) - { - Console.WriteLine(a == b); - Console.WriteLine(a != b); - Console.WriteLine(a > b); - - Console.WriteLine(!(a == b)); - Console.WriteLine(!(a != b)); - Console.WriteLine(!(a > b)); - - Console.WriteLine(a + b); - Console.WriteLine(a - b); - Console.WriteLine(a * b); - Console.WriteLine(a / b); - Console.WriteLine(a % b); - Console.WriteLine(a & b); - Console.WriteLine(a | b); - Console.WriteLine(a ^ b); - Console.WriteLine(a << b); - Console.WriteLine(a >> b); - Console.WriteLine(a ?? b); - Console.WriteLine(-a); - Console.WriteLine(~a); - // TODO: - //Console.WriteLine(a++); - //Console.WriteLine(a--); - Console.WriteLine(++a); - Console.WriteLine(--a); - a += b; - a -= b; - a *= b; - a /= b; - a %= b; - a &= b; - a |= b; - a ^= b; - a <<= b; - a >>= b; - } - - public static void IntValueComplex(int? a, Func<int> x) - { - Console.WriteLine(a == x()); - Console.WriteLine(a != x()); - Console.WriteLine(a > x()); - - Console.WriteLine(x() == a); - Console.WriteLine(x() != a); - Console.WriteLine(x() > a); - - Console.WriteLine(a + x()); - Console.WriteLine(a - x()); - Console.WriteLine(a * x()); - Console.WriteLine(a / x()); - Console.WriteLine(a % x()); - Console.WriteLine(a & x()); - Console.WriteLine(a | x()); - Console.WriteLine(a ^ x()); - Console.WriteLine(a << x()); - Console.WriteLine(a >> x()); - Console.WriteLine(a ?? x()); - a += x(); - a -= x(); - a *= x(); - a /= x(); - a %= x(); - a &= x(); - a |= x(); - a ^= x(); - a <<= x(); - a >>= x(); - - Console.WriteLine(x() + a); - (new int?[0])[0] += x(); - } - - public static void IntValueConst(int? a) - { - Console.WriteLine(a == 2); - Console.WriteLine(a != 2); - Console.WriteLine(a > 2); - - Console.WriteLine(2 == a); - Console.WriteLine(2 != a); - Console.WriteLine(2 > a); - - Console.WriteLine(a + 2); - Console.WriteLine(a - 2); - Console.WriteLine(a * 2); - Console.WriteLine(a / 2); - Console.WriteLine(a % 2); - Console.WriteLine(a & 2); - Console.WriteLine(a | 2); - Console.WriteLine(a ^ 2); - Console.WriteLine(a << 2); - Console.WriteLine(a >> 2); - Console.WriteLine(a ?? 2); - a += 2; - a -= 2; - a *= 2; - a /= 2; - a %= 2; - a &= 2; - a |= 2; - a ^= 2; - a <<= 2; - a >>= 2; - - Console.WriteLine(2 + a); - } - - public static void NumberBasic(decimal? a, decimal? b) - { - if (a == b) - { - Console.WriteLine(); - } - if (a != b) - { - Console.WriteLine(); - } - if (a > b) - { - Console.WriteLine(); - } - if (a < b) - { - Console.WriteLine(); - } - if (a >= b) - { - Console.WriteLine(); - } - if (a <= b) - { - Console.WriteLine(); - } - - if (!(a == b)) - { - Console.WriteLine(); - } - if (!(a != b)) - { - Console.WriteLine(); - } - if (!(a > b)) - { - Console.WriteLine(); - } - } - - public static void NumberComplex(decimal? a, Func<decimal> x) - { - if (a == x()) - { - Console.WriteLine(); - } - if (a != x()) - { - Console.WriteLine(); - } - if (a > x()) - { - Console.WriteLine(); - } - - if (x() == a) - { - Console.WriteLine(); - } - if (x() != a) - { - Console.WriteLine(); - } - if (x() > a) - { - Console.WriteLine(); - } - } - - public static void NumberConst(decimal? a) - { - if (a == 2m) - { - Console.WriteLine(); - } - if (a != 2m) - { - Console.WriteLine(); - } - if (a > 2m) - { - Console.WriteLine(); - } - - if (2m == a) - { - Console.WriteLine(); - } - if (2m != a) - { - Console.WriteLine(); - } - if (2m > a) - { - Console.WriteLine(); - } - } - - public static void NumberValueBasic(decimal? a, decimal? b) - { - Console.WriteLine(a == b); - Console.WriteLine(a != b); - Console.WriteLine(a > b); - - Console.WriteLine(!(a == b)); - Console.WriteLine(!(a != b)); - Console.WriteLine(!(a > b)); - - Console.WriteLine(a + b); - Console.WriteLine(a - b); - Console.WriteLine(a * b); - Console.WriteLine(a / b); - Console.WriteLine(a % b); - Console.WriteLine(a ?? b); - Console.WriteLine(-a); - // TODO: - //Console.WriteLine(a++); - //Console.WriteLine(a--); - //Console.WriteLine(++a); - //Console.WriteLine(--a); - a += b; - a -= b; - a *= b; - a /= b; - a %= b; - } - - public static void NumberValueComplex(decimal? a, Func<decimal> x) - { - Console.WriteLine(a == x()); - Console.WriteLine(a != x()); - Console.WriteLine(a > x()); - - Console.WriteLine(x() == a); - Console.WriteLine(x() != a); - Console.WriteLine(x() > a); - - Console.WriteLine(a + x()); - Console.WriteLine(a - x()); - Console.WriteLine(a * x()); - Console.WriteLine(a / x()); - Console.WriteLine(a % x()); - Console.WriteLine(a ?? x()); - a += x(); - a -= x(); - a *= x(); - a /= x(); - a %= x(); - - Console.WriteLine(x() + a); - (new decimal?[0])[0] += x(); - } - - public static void NumberValueConst(decimal? a) - { - Console.WriteLine(a == 2m); - Console.WriteLine(a != 2m); - Console.WriteLine(a > 2m); - - Console.WriteLine(2m == a); - Console.WriteLine(2m != a); - Console.WriteLine(2m > a); - - Console.WriteLine(a + 2m); - Console.WriteLine(a - 2m); - Console.WriteLine(a * 2m); - Console.WriteLine(a / 2m); - Console.WriteLine(a % 2m); - Console.WriteLine(a ?? 2m); - a += 2m; - a -= 2m; - a *= 2m; - a /= 2m; - a %= 2m; - - Console.WriteLine(2m + a); - } - - public static void StructBasic(TS? a, TS? b) - { - if (a == b) - { - Console.WriteLine(); - } - if (a != b) - { - Console.WriteLine(); - } - if (a > b) - { - Console.WriteLine(); - } - if (a < b) - { - Console.WriteLine(); - } - if (a >= b) - { - Console.WriteLine(); - } - if (a <= b) - { - Console.WriteLine(); - } - - if (!(a == b)) - { - Console.WriteLine(); - } - if (!(a != b)) - { - Console.WriteLine(); - } - if (!(a > b)) - { - Console.WriteLine(); - } - } - - public static void StructComplex(TS? a, Func<TS> x) - { - if (a == x()) - { - Console.WriteLine(); - } - if (a != x()) - { - Console.WriteLine(); - } - if (a > x()) - { - Console.WriteLine(); - } - - if (x() == a) - { - Console.WriteLine(); - } - if (x() != a) - { - Console.WriteLine(); - } - if (x() > a) - { - Console.WriteLine(); - } - } - - public static void StructValueBasic(TS? a, TS? b, int? i) - { - Console.WriteLine(a == b); - Console.WriteLine(a != b); - Console.WriteLine(a > b); - - Console.WriteLine(!(a == b)); - Console.WriteLine(!(a != b)); - Console.WriteLine(!(a > b)); - - Console.WriteLine(a + b); - Console.WriteLine(a - b); - Console.WriteLine(a * b); - Console.WriteLine(a / b); - Console.WriteLine(a % b); - Console.WriteLine(a & b); - Console.WriteLine(a | b); - Console.WriteLine(a ^ b); - Console.WriteLine(a << i); - Console.WriteLine(a >> i); - Console.WriteLine(a ?? b); - Console.WriteLine(+a); - Console.WriteLine(-a); - Console.WriteLine(!a); - Console.WriteLine(~a); - // TODO: - //Console.WriteLine(a++); - //Console.WriteLine(a--); - //Console.WriteLine(++a); - //Console.WriteLine(--a); - //Console.WriteLine((int?)a); - a += b; - a -= b; - a *= b; - a /= b; - a %= b; - a &= b; - a |= b; - a ^= b; - a <<= i; - a >>= i; - } - - public static void StructValueComplex(TS? a, Func<TS> x, Func<int> i) - { - Console.WriteLine(a == x()); - Console.WriteLine(a != x()); - Console.WriteLine(a > x()); - - Console.WriteLine(x() == a); - Console.WriteLine(x() != a); - Console.WriteLine(x() > a); - - Console.WriteLine(a + x()); - Console.WriteLine(a - x()); - Console.WriteLine(a * x()); - Console.WriteLine(a / x()); - Console.WriteLine(a % x()); - Console.WriteLine(a & x()); - Console.WriteLine(a | x()); - Console.WriteLine(a ^ x()); - Console.WriteLine(a << i()); - Console.WriteLine(a >> i()); - Console.WriteLine(a ?? x()); - a += x(); - a -= x(); - a *= x(); - a /= x(); - a %= x(); - a &= x(); - a |= x(); - a ^= x(); - a <<= i(); - a >>= i(); - - Console.WriteLine(x() + a); - (new TS?[0])[0] += x(); - } -} - -// dummy structure for testing custom operators -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct TS -{ - // unary - public static TS operator +(TS a) - { - throw null; - } - public static TS operator -(TS a) - { - throw null; - } - public static TS operator !(TS a) - { - throw null; - } - public static TS operator ~(TS a) - { - throw null; - } - public static TS operator ++(TS a) - { - throw null; - } - public static TS operator --(TS a) - { - throw null; - } - - public static explicit operator int(TS a) - { - throw null; - } - - // binary - public static TS operator +(TS a, TS b) - { - throw null; - } - public static TS operator -(TS a, TS b) - { - throw null; - } - public static TS operator *(TS a, TS b) - { - throw null; - } - public static TS operator /(TS a, TS b) - { - throw null; - } - public static TS operator %(TS a, TS b) - { - throw null; - } - public static TS operator &(TS a, TS b) - { - throw null; - } - public static TS operator |(TS a, TS b) - { - throw null; - } - public static TS operator ^(TS a, TS b) - { - throw null; - } - public static TS operator <<(TS a, int b) - { - throw null; - } - public static TS operator >>(TS a, int b) - { - throw null; - } - - // comparisons - public static bool operator ==(TS a, TS b) - { - throw null; - } - public static bool operator !=(TS a, TS b) - { - throw null; - } - public static bool operator <(TS a, TS b) - { - throw null; - } - public static bool operator <=(TS a, TS b) - { - throw null; - } - public static bool operator >(TS a, TS b) - { - throw null; - } - public static bool operator >=(TS a, TS b) - { - throw null; - } - - public override bool Equals(object obj) - { - throw null; - } - public override int GetHashCode() - { - throw null; - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Lock.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Lock.cs deleted file mode 100644 index da5a59c74f..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/Lock.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 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; - -public class Lock -{ - public void LockThis() - { - lock (this) - { - Console.WriteLine(); - } - } - - public void LockOnType() - { - lock (typeof(Lock)) - { - Console.WriteLine(); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Loops.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Loops.cs deleted file mode 100644 index 03427a8bdc..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/Loops.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Collections; -using System.Collections.Generic; - -public class Loops -{ - public void ForEach(IEnumerable<string> enumerable) - { - foreach (string current in enumerable) - { - current.ToLower(); - } - } - - public void ForEachOverList(List<string> list) - { - // List has a struct as enumerator, so produces quite different IL than foreach over the IEnumerable interface - foreach (string current in list) - { - current.ToLower(); - } - } - - public void ForEachOverNonGenericEnumerable(IEnumerable enumerable) - { - foreach (object current in enumerable) - { - current.ToString(); - } - } - - public void ForEachOverNonGenericEnumerableWithAutomaticCast(IEnumerable enumerable) - { - foreach (int num in enumerable) - { - num.ToString(); - } - } - -// public void ForEachOverArray(string[] array) -// { -// foreach (string text in array) -// { -// text.ToLower(); -// } -// } - - public void ForOverArray(string[] array) - { - for (int i = 0; i < array.Length; i++) - { - array[i].ToLower(); - } - } -} - diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/MultidimensionalArray.cs b/main/contrib/ICSharpCode.Decompiler/Tests/MultidimensionalArray.cs deleted file mode 100644 index c3557cee44..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/MultidimensionalArray.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 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; - -public class MultidimensionalArray -{ - internal class Generic<T, S> where T : new() - { - private T[,] a = new T[20, 20]; - private S[,][] b = new S[20, 20][]; - - public T this[int i, int j] - { - get - { - return this.a[i, j]; - } - set - { - this.a[i, j] = value; - } - } - - public void TestB(S x, ref S y) - { - this.b[5, 3] = new S[10]; - this.b[5, 3][0] = default(S); - this.b[5, 3][1] = x; - this.b[5, 3][2] = y; - } - - public void PassByReference(ref T arr) - { - this.PassByReference(ref this.a[10, 10]); - } - } - - public int[][,] MakeArray() - { - return new int[10][,]; - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/PInvoke.cs b/main/contrib/ICSharpCode.Decompiler/Tests/PInvoke.cs deleted file mode 100644 index 0c828ac696..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/PInvoke.cs +++ /dev/null @@ -1,96 +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.Runtime.InteropServices; - -// P/Invoke and marshalling attribute tests -public class PInvoke -{ - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 2)] - public struct MarshalAsTest - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public uint[] FixedArray; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.Bool)] - public int[] FixedBoolArray; - - [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] - public string[] SafeBStrArray; - - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)] - public string FixedString; - } - - [StructLayout(LayoutKind.Explicit)] - public struct Rect - { - [FieldOffset(0)] - public int left; - [FieldOffset(4)] - public int top; - [FieldOffset(8)] - public int right; - [FieldOffset(12)] - public int bottom; - } - - public static decimal MarshalAttributesOnPropertyAccessors - { - [return: MarshalAs(UnmanagedType.Currency)] - get - { - return 0m; - } - [param: MarshalAs(UnmanagedType.Currency)] - set - { - } - } - - [DllImport("xyz.dll", CharSet = CharSet.Auto)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool Method([MarshalAs(UnmanagedType.LPStr)] string input); - - [DllImport("xyz.dll")] - private static extern void New1(int ElemCnt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] ar); - - [DllImport("xyz.dll")] - private static extern void New2([MarshalAs(UnmanagedType.LPArray, SizeConst = 128)] int[] ar); - - [DllImport("xyz.dll")] - private static extern void New3([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Bool, SizeConst = 64, SizeParamIndex = 1)] int[] ar); - - public void CustomMarshal1([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "MyCompany.MyMarshaler")] object o) - { - } - - public void CustomMarshal2([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "MyCompany.MyMarshaler", MarshalCookie = "Cookie")] object o) - { - } - - [DllImport("ws2_32.dll", SetLastError = true)] - internal static extern IntPtr ioctlsocket([In] IntPtr socketHandle, [In] int cmd, [In] [Out] ref int argp); - - public void CallMethodWithInOutParameter() - { - int num = 0; - PInvoke.ioctlsocket(IntPtr.Zero, 0, ref num); - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs b/main/contrib/ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs deleted file mode 100644 index 9b3d165893..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 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.Text; - -public class PropertiesAndEvents -{ - public event EventHandler AutomaticEvent; - - [field: NonSerialized] - public event EventHandler AutomaticEventWithInitializer = delegate(object sender, EventArgs e) - { - }; - - public event EventHandler CustomEvent - { - add - { - this.AutomaticEvent += value; - } - remove - { - this.AutomaticEvent -= value; - } - } - - public int AutomaticProperty - { - get; - set; - } - - public int CustomProperty - { - get - { - return this.AutomaticProperty; - } - set - { - this.AutomaticProperty = value; - } - } - - public int Getter(StringBuilder b) - { - return b.Length; - } - - public void Setter(StringBuilder b) - { - b.Capacity = 100; - } - - public char IndexerGetter(StringBuilder b) - { - return b[50]; - } - - public void IndexerSetter(StringBuilder b) - { - b[42] = 'b'; - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/QueryExpressions.cs b/main/contrib/ICSharpCode.Decompiler/Tests/QueryExpressions.cs deleted file mode 100644 index d8b6e06234..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/QueryExpressions.cs +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; - -public class QueryExpressions -{ - public class Customer - { - public int CustomerID; - public IEnumerable<QueryExpressions.Order> Orders; - public string Name; - public string Country; - public string City; - } - - public class Order - { - public int OrderID; - public DateTime OrderDate; - public QueryExpressions.Customer Customer; - public int CustomerID; - public decimal Total; - public IEnumerable<QueryExpressions.OrderDetail> Details; - } - - public class OrderDetail - { - public decimal UnitPrice; - public int Quantity; - } - - public IEnumerable<QueryExpressions.Customer> customers; - public IEnumerable<QueryExpressions.Order> orders; - - public object MultipleWhere() - { - return from c in this.customers - where c.Orders.Count<QueryExpressions.Order>() > 10 - where c.Country == "DE" - select c; - } - - public object SelectManyFollowedBySelect() - { - return from c in this.customers - from o in c.Orders - select new - { - c.Name, - o.OrderID, - o.Total - }; - } - - public object SelectManyFollowedByOrderBy() - { - return from c in this.customers - from o in c.Orders - orderby o.Total descending - select new - { - c.Name, - o.OrderID, - o.Total - }; - } - - public object MultipleSelectManyFollowedBySelect() - { - return from c in this.customers - from o in c.Orders - from d in o.Details - select new - { - c.Name, - o.OrderID, - d.Quantity - }; - } - - public object MultipleSelectManyFollowedByLet() - { - return from c in this.customers - from o in c.Orders - from d in o.Details - let x = d.Quantity * d.UnitPrice - select new - { - c.Name, - o.OrderID, - x - }; - } - - public object FromLetWhereSelect() - { - return from o in this.orders - let t = o.Details.Sum((QueryExpressions.OrderDetail d) => d.UnitPrice * d.Quantity) - where t >= 1000m - select new - { - OrderID = o.OrderID, - Total = t - }; - } - - public object MultipleLet() - { - return from a in this.customers - let b = a.Country - let c = a.Name - select b + c; - } - - public object Join() - { - return from c in this.customers - join o in this.orders on c.CustomerID equals o.CustomerID - select new - { - c.Name, - o.OrderDate, - o.Total - }; - } - - public object JoinInto() - { - return from c in this.customers - join o in this.orders on c.CustomerID equals o.CustomerID into co - let n = co.Count<QueryExpressions.Order>() - where n >= 10 - select new - { - Name = c.Name, - OrderCount = n - }; - } - - public object OrderBy() - { - return from o in this.orders - orderby o.Customer.Name, o.Total descending - select o; - } - - public object GroupBy() - { - return from c in this.customers - group c.Name by c.Country; - } - - public object ExplicitType() - { - return from QueryExpressions.Customer c in this.customers - where c.City == "London" - select c; - } - - public object QueryContinuation() - { - return from c in this.customers - group c by c.Country into g - select new - { - Country = g.Key, - CustCount = g.Count<QueryExpressions.Customer>() - }; - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Switch.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Switch.cs deleted file mode 100644 index 4a20a6392d..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/Switch.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 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; - -public static class Switch -{ - public static string ShortSwitchOverString(string text) - { - switch (text) { - case "First case": - return "Text"; - default: - return "Default"; - } - } - - public static string SwitchOverString1(string text) - { - switch (text) - { - case "First case": - return "Text1"; - case "Second case": - case "2nd case": - return "Text2"; - case "Third case": - return "Text3"; - case "Fourth case": - return "Text4"; - case "Fifth case": - return "Text5"; - case "Sixth case": - return "Text6"; - case null: - return null; - default: - return "Default"; - } - } - - public static string SwitchOverString2() - { - switch (Environment.UserName) - { - case "First case": - return "Text1"; - case "Second case": - return "Text2"; - case "Third case": - return "Text3"; - case "Fourth case": - return "Text4"; - case "Fifth case": - return "Text5"; - case "Sixth case": - return "Text6"; - default: - return "Default"; - } - } - - public static string SwitchOverBool(bool b) - { - switch (b) { - case true: - return bool.TrueString; - case false: - return bool.FalseString; - default: - return null; - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/TestRunner.cs b/main/contrib/ICSharpCode.Decompiler/Tests/TestRunner.cs deleted file mode 100644 index 215725b0a6..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (c) 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.CodeDom.Compiler; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using DiffLib; -using ICSharpCode.Decompiler.Ast; -using ICSharpCode.Decompiler.Tests.Helpers; -using Microsoft.CSharp; -using Mono.Cecil; -using NUnit.Framework; - -namespace ICSharpCode.Decompiler.Tests -{ - [TestFixture] - public class TestRunner : DecompilerTestBase - { - [Test] - public void Async() - { - TestFile(@"..\..\Tests\Async.cs"); - } - - [Test, Ignore("disambiguating overloads is not yet implemented")] - public void CallOverloadedMethod() - { - TestFile(@"..\..\Tests\CallOverloadedMethod.cs"); - } - - [Test, Ignore("unncessary primitive casts")] - public void CheckedUnchecked() - { - TestFile(@"..\..\Tests\CheckedUnchecked.cs"); - } - - [Test, Ignore("Missing cast on null")] - public void DelegateConstruction() - { - TestFile(@"..\..\Tests\DelegateConstruction.cs"); - } - - [Test, Ignore("Not yet implemented")] - public void ExpressionTrees() - { - TestFile(@"..\..\Tests\ExpressionTrees.cs"); - } - - [Test] - public void ExceptionHandling() - { - AssertRoundtripCode(@"..\..\Tests\ExceptionHandling.cs", optimize: false); - AssertRoundtripCode(@"..\..\Tests\ExceptionHandling.cs", optimize: false); - } - - [Test] - public void Generics() - { - TestFile(@"..\..\Tests\Generics.cs"); - } - - [Test] - public void CustomShortCircuitOperators() - { - TestFile(@"..\..\Tests\CustomShortCircuitOperators.cs"); - } - - [Test] - public void ControlFlowWithDebug() - { - AssertRoundtripCode(@"..\..\Tests\ControlFlow.cs", optimize: false, useDebug: true); - AssertRoundtripCode(@"..\..\Tests\ControlFlow.cs", optimize: false, useDebug: true); - } - - [Test] - public void DoubleConstants() - { - TestFile(@"..\..\Tests\DoubleConstants.cs"); - } - - [Test] - public void IncrementDecrement() - { - TestFile(@"..\..\Tests\IncrementDecrement.cs"); - } - - [Test] - public void InitializerTests() - { - TestFile(@"..\..\Tests\InitializerTests.cs"); - } - - [Test] - public void LiftedOperators() - { - TestFile(@"..\..\Tests\LiftedOperators.cs"); - } - - [Test] - public void Lock() - { - //TestFile(@"..\..\Tests\Lock.cs", compilerVersion: 2); - TestFile(@"..\..\Tests\Lock.cs", compilerVersion: 4); - } - - [Test] - public void Loops() - { - TestFile(@"..\..\Tests\Loops.cs"); - } - - [Test] - public void MultidimensionalArray() - { - TestFile(@"..\..\Tests\MultidimensionalArray.cs"); - } - - [Test] - public void PInvoke() - { - TestFile(@"..\..\Tests\PInvoke.cs"); - } - - [Test] - public void PropertiesAndEvents() - { - TestFile(@"..\..\Tests\PropertiesAndEvents.cs"); - } - - [Test] - public void QueryExpressions() - { - TestFile(@"..\..\Tests\QueryExpressions.cs"); - } - - [Test, Ignore("switch transform doesn't recreate the exact original switch")] - public void Switch() - { - TestFile(@"..\..\Tests\Switch.cs"); - } - - [Test] - public void UndocumentedExpressions() - { - TestFile(@"..\..\Tests\UndocumentedExpressions.cs"); - } - - [Test, Ignore("has incorrect casts to IntPtr")] - public void UnsafeCode() - { - TestFile(@"..\..\Tests\UnsafeCode.cs"); - } - - [Test] - public void ValueTypes() - { - TestFile(@"..\..\Tests\ValueTypes.cs"); - } - - [Test, Ignore("Redundant yield break; not removed")] - public void YieldReturn() - { - TestFile(@"..\..\Tests\YieldReturn.cs"); - } - - [Test] - public void TypeAnalysis() - { - TestFile(@"..\..\Tests\TypeAnalysisTests.cs"); - } - - static void TestFile(string fileName, bool useDebug = false, int compilerVersion = 4) - { - AssertRoundtripCode(fileName, optimize: false, useDebug: useDebug, compilerVersion: compilerVersion); - AssertRoundtripCode(fileName, optimize: true, useDebug: useDebug, compilerVersion: compilerVersion); - AssertRoundtripCode(fileName, optimize: false, useDebug: useDebug, compilerVersion: compilerVersion); - AssertRoundtripCode(fileName, optimize: true, useDebug: useDebug, compilerVersion: compilerVersion); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/TypeAnalysisTests.cs b/main/contrib/ICSharpCode.Decompiler/Tests/TypeAnalysisTests.cs deleted file mode 100644 index 760bb862d3..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/TypeAnalysisTests.cs +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (c) 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; - -public class TypeAnalysisTests -{ - public byte SubtractFrom256(byte b) - { - return (byte)(256 - (int)b); - } - - #region Shift - public int LShiftInteger(int num1, int num2) - { - return num1 << num2; - } - - public uint LShiftUnsignedInteger(uint num1, uint num2) - { - return num1 << (int)num2; - } - - public long LShiftLong(long num1, long num2) - { - return num1 << (int)num2; - } - - public ulong LShiftUnsignedLong(ulong num1, ulong num2) - { - return num1 << (int)num2; - } - - public int RShiftInteger(int num1, int num2) - { - return num1 >> num2; - } - - public uint RShiftUnsignedInteger(uint num1, int num2) - { - return num1 >> num2; - } - - public long RShiftLong(long num1, long num2) - { - return num1 >> (int)num2; - } - - public ulong RShiftUnsignedLong(ulong num1, ulong num2) - { - return num1 >> (int)num2; - } - - public int ShiftByte(byte num) - { - return (int)num << 8; - } - - public int RShiftByte(byte num) - { - return num >> 8; - } - - public uint RShiftByteWithZeroExtension(byte num) - { - return (uint)num >> 8; - } - - public int RShiftByteAsSByte(byte num) - { - return (sbyte)num >> 8; - } - - public int RShiftSByte(sbyte num) - { - return num >> 8; - } - - public uint RShiftSByteWithZeroExtension(sbyte num) - { - return (uint)num >> 8; - } - - public int RShiftSByteAsByte(sbyte num) - { - return (byte)num >> 8; - } - #endregion - - public int GetHashCode(long num) - { - return (int)num ^ (int)(num >> 32); - } - - public void TernaryOp(Random a, Random b, bool c) - { - if ((c ? a : b) == null) - { - Console.WriteLine(); - } - } - - public void OperatorIs(object o) - { - Console.WriteLine(o is Random); - Console.WriteLine(!(o is Random)); - } - - public byte[] CreateArrayWithInt(int length) - { - return new byte[length]; - } - - public byte[] CreateArrayWithLong(long length) - { - return new byte[length]; - } - - public byte[] CreateArrayWithUInt(uint length) - { - return new byte[length]; - } - - public byte[] CreateArrayWithULong(ulong length) - { - return new byte[length]; - } - - public StringComparison EnumDiffNumber(StringComparison data) - { - return data - 1; - } - - public int EnumDiff(StringComparison a, StringComparison b) - { - return Math.Abs(a - b); - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Types/EnumTests.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Types/EnumTests.cs deleted file mode 100644 index fb48d016ed..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/Types/EnumTests.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using NUnit.Framework; - -namespace ICSharpCode.Decompiler.Tests.Types -{ - [TestFixture] - public class EnumTests : DecompilerTestBase - { - [Test] - public void EnumSamples() - { - ValidateFileRoundtrip(@"Types\S_EnumSamples.cs"); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_EnumSamples.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_EnumSamples.cs deleted file mode 100644 index 3149f57539..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_EnumSamples.cs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2014 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. - -//$CS -using System; -//$CE - -//$$ SingleValue -public class TS_SingleValue -{ - public AttributeTargets Method() - { - return AttributeTargets.Class; - } -} -//$$ TwoValuesOr -public class TS_TwoValuesOr -{ - public AttributeTargets Method() - { - return AttributeTargets.Class | AttributeTargets.Method; - } -} -//$$ ThreeValuesOr -public class TS_ThreeValuesOr -{ - public AttributeTargets Method() - { - return AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Parameter; - } -} -//$$ UnknownNumericValue -public class TS_UnknownNumericValue -{ - public AttributeTargets Method() - { - return (AttributeTargets)1000000; - } -} -//$$ AllValue -public class TS_AllValue -{ - public AttributeTargets Method() - { - return AttributeTargets.All; - } -} -//$$ ZeroValue -public class TS_ZeroValue -{ - public AttributeTargets Method() - { - return (AttributeTargets)0; - } -} -//$$ PreservingTypeWhenBoxed -public class TS_PreservingTypeWhenBoxed -{ - public object Method() - { - return AttributeTargets.Delegate; - } -} -//$$ PreservingTypeWhenBoxedTwoEnum -public class TS_PreservingTypeWhenBoxedTwoEnum -{ - public object Method() - { - return AttributeTargets.Class | AttributeTargets.Delegate; - } -} -//$$ DeclarationSimpleEnum -public enum TS_DeclarationSimpleEnum -{ - Item1, - Item2 -} -//$$ DeclarationLongBasedEnum -public enum TS_DeclarationLongBasedEnum : long -{ - Item1, - Item2 -} -//$$ DeclarationLongWithInitializers -public enum TS_DeclarationLongWithInitializers : long -{ - Item1, - Item2 = 20L, - Item3 -} -//$$ DeclarationShortWithInitializers -public enum TS_DeclarationShortWithInitializers : short -{ - Item1, - Item2 = 20, - Item3 -} -//$$ DeclarationByteWithInitializers -public enum TS_DeclarationByteWithInitializers : byte -{ - Item1, - Item2 = 20, - Item3 -} -//$$ DeclarationFlags -[Flags] -public enum TS_DeclarationFlags -{ - None = 0, - Item1 = 1, - Item2 = 2, - Item3 = 4, - All = 7 -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeDeclarations.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeDeclarations.cs deleted file mode 100644 index 99bf4279e3..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeDeclarations.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace ClassMultiInterface -{ - public interface IA - { - } - public interface IA2 : IA - { - } - public interface IB - { - } - public class C : IA2, IB - { - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs deleted file mode 100644 index 4a85b9f88a..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs +++ /dev/null @@ -1,1138 +0,0 @@ -// Copyright (c) 2014 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. - -//$CS -using System; -//$CE - -//$$ IndexerWithGetOnly -namespace IndexerWithGetOnly -{ - public class MyClass - { - public int this[int i] - { - get - { - return i; - } - } - } -} -//$$ IndexerWithSetOnly -namespace IndexerWithSetOnly -{ - public class MyClass - { - public int this[int i] - { - set - { - } - } - } -} -//$$ IndexerWithMoreParameters -namespace IndexerWithMoreParameters -{ - public class MyClass - { - public int this[int i, string s, Type t] - { - get - { - return 0; - } - } - } -} -//$$ IndexerInGenericClass -namespace IndexerInGenericClass -{ - public class MyClass<T> - { - public int this[T t] - { - get - { - return 0; - } - } - } -} -//$$ OverloadedIndexer -namespace OverloadedIndexer -{ - public class MyClass - { - public int this[int t] - { - get - { - return 0; - } - } - public int this[string s] - { - get - { - return 0; - } - set - { - Console.WriteLine(value + " " + s); - } - } - } -} -//$$ IndexerInInterface -namespace IndexerInInterface -{ - public interface IInterface - { - int this[string s, string s2] - { - set; - } - } -} -//$$ IndexerInterfaceExplicitImplementation -namespace IndexerInterfaceExplicitImplementation -{ - public interface IMyInterface - { - int this[string s] - { - get; - } - } - public class MyClass : IMyInterface - { - int IMyInterface.this[string s] - { - get - { - return 3; - } - } - } -} -//$$ IndexerInterfaceImplementation -namespace IndexerInterfaceImplementation -{ - public interface IMyInterface - { - int this[string s] - { - get; - } - } - public class MyClass : IMyInterface - { - public int this[string s] - { - get - { - return 3; - } - } - } -} -//$$ IndexerAbstract -namespace IndexerAbstract -{ - public abstract class MyClass - { - public abstract int this[string s, string s2] - { - set; - } - protected abstract string this[int index] - { - get; - } - } -} -//$$ MethodExplicit -namespace MethodExplicit -{ - public interface IMyInterface - { - void MyMethod(); - } - public class MyClass : IMyInterface - { - void IMyInterface.MyMethod() - { - } - } -} -//$$ MethodFromInterfaceVirtual -namespace MethodFromInterfaceVirtual -{ - public interface IMyInterface - { - void MyMethod(); - } - public class MyClass : IMyInterface - { - public virtual void MyMethod() - { - } - } -} -//$$ MethodFromInterface -namespace MethodFromInterface -{ - public interface IMyInterface - { - void MyMethod(); - } - public class MyClass : IMyInterface - { - public void MyMethod() - { - } - } -} -//$$ MethodFromInterfaceAbstract -namespace MethodFromInterfaceAbstract -{ - public interface IMyInterface - { - void MyMethod(); - } - public abstract class MyClass : IMyInterface - { - public abstract void MyMethod(); - } -} -//$$ PropertyInterface -namespace PropertyInterface -{ - public interface IMyInterface - { - int MyProperty - { - get; - set; - } - } -} -//$$ PropertyInterfaceExplicitImplementation -namespace PropertyInterfaceExplicitImplementation -{ - public interface IMyInterface - { - int MyProperty - { - get; - set; - } - } - public class MyClass : IMyInterface - { - int IMyInterface.MyProperty - { - get - { - return 0; - } - set - { - } - } - } -} -//$$ PropertyInterfaceImplementation -namespace PropertyInterfaceImplementation -{ - public interface IMyInterface - { - int MyProperty - { - get; - set; - } - } - public class MyClass : IMyInterface - { - public int MyProperty - { - get - { - return 0; - } - set - { - } - } - } -} -//$$ PropertyPrivateGetPublicSet -namespace PropertyPrivateGetPublicSet -{ - public class MyClass - { - public int MyProperty - { - private get - { - return 3; - } - set - { - } - } - } -} -//$$ PropertyPublicGetProtectedSet -namespace PropertyPublicGetProtectedSet -{ - public class MyClass - { - public int MyProperty - { - get - { - return 3; - } - protected set - { - } - } - } -} -//$$ PropertyOverrideDefaultAccessorOnly -namespace PropertyOverrideDefaultAccessorOnly -{ - public class MyClass - { - public virtual int MyProperty - { - get - { - return 3; - } - protected set - { - } - } - } - public class Derived : MyClass - { - public override int MyProperty - { - get - { - return 4; - } - } - } -} -//$$ PropertyOverrideRestrictedAccessorOnly -namespace PropertyOverrideRestrictedAccessorOnly -{ - public class MyClass - { - public virtual int MyProperty - { - get - { - return 3; - } - protected set - { - } - } - } - public class Derived : MyClass - { - public override int MyProperty - { - protected set - { - } - } - } -} -//$$ PropertyOverrideOneAccessor -namespace PropertyOverrideOneAccessor -{ - public class MyClass - { - protected internal virtual int MyProperty - { - get - { - return 3; - } - protected set - { - } - } - } - public class DerivedNew : MyClass - { - public new virtual int MyProperty - { - set - { - } - } - } - public class DerivedOverride : DerivedNew - { - public override int MyProperty - { - set - { - } - } - } -} -//$$ IndexerOverrideRestrictedAccessorOnly -namespace IndexerOverrideRestrictedAccessorOnly -{ - public class MyClass - { - public virtual int this[string s] - { - get - { - return 3; - } - protected set - { - } - } - protected internal virtual int this[int i] - { - protected get - { - return 2; - } - set - { - } - } - } - public class Derived : MyClass - { - protected internal override int this[int i] - { - protected get - { - return 4; - } - } - } -} -//$$ HideProperty -namespace HideProperty -{ - public class A - { - public virtual int P - { - get - { - return 0; - } - set - { - } - } - } - public class B : A - { - private new int P - { - get - { - return 0; - } - set - { - } - } - } - public class C : B - { - public override int P - { - set - { - } - } - } -} -//$$ HideMembers -namespace HideMembers -{ - public class A - { - public int F; - public int Prop - { - get - { - return 3; - } - } - public int G - { - get - { - return 3; - } - } - } - public class B : A - { - public new int F - { - get - { - return 3; - } - } - public new string Prop - { - get - { - return "a"; - } - } - } - public class C : A - { - public new int G; - } - public class D : A - { - public new void F() - { - } - } - public class D1 : D - { - public new int F; - } - public class E : A - { - private new class F - { - } - } -} -//$$ HideMembers2 -namespace HideMembers2 -{ - public class G - { - public int Item - { - get - { - return 1; - } - } - } - public class G2 : G - { - public int this[int i] - { - get - { - return 2; - } - } - } - public class G3 : G2 - { - public new int Item - { - get - { - return 4; - } - } - } - public class H - { - public int this[int j] - { - get - { - return 0; - } - } - } - public class H2 : H - { - public int Item - { - get - { - return 2; - } - } - } - public class H3 : H2 - { - public new string this[int j] - { - get - { - return null; - } - } - } -} -//$$ HideMembers2a -namespace HideMembers2a -{ - public interface IA - { - int this[int i] - { - get; - } - } - public class A : IA - { - int IA.this[int i] - { - get - { - throw new NotImplementedException(); - } - } - } - public class A1 : A - { - public int this[int i] - { - get - { - return 3; - } - } - } -} -//$$ HideMembers3 -namespace HideMembers3 -{ - public class G<T> - { - public void M1(T p) - { - } - public int M2(int t) - { - return 3; - } - } - public class G1<T> : G<int> - { - public new int M1(int i) - { - return 0; - } - public int M2(T i) - { - return 2; - } - } - public class G2<T> : G<int> - { - public int M1(T p) - { - return 4; - } - } - public class J - { - public int P - { - get - { - return 2; - } - } - } - public class J2 : J - { -#pragma warning disable 0108 // Deliberate bad code for test case - public int get_P; -#pragma warning restore 0108 - } -} -//$$ HideMembers4 -namespace HideMembers4 -{ - public class A - { - public void M<T>(T t) - { - } - } - public class A1 : A - { - public new void M<K>(K t) - { - } - public void M(int t) - { - } - } - public class B - { - public void M<T>() - { - } - public void M1<T>() - { - } - public void M2<T>(T t) - { - } - } - public class B1 : B - { - public void M<T1, T2>() - { - } - public new void M1<R>() - { - } - public new void M2<R>(R r) - { - } - } - public class C<T> - { - public void M<TT>(T t) - { - } - } - public class C1<K> : C<K> - { - public void M<TT>(TT t) - { - } - } -} -//$$ HideMembers5 -namespace HideMembers5 -{ - public class A - { - public void M(int t) - { - } - } - public class A1 : A - { - public void M(ref int t) - { - } - } - public class B - { - public void M(ref int l) - { - } - } - public class B1 : B - { - public void M(out int l) - { - l = 2; - } - public void M(ref long l) - { - } - } -} -//$$ HideMemberSkipNotVisible -namespace HideMemberSkipNotVisible -{ - public class A - { - protected int F; - protected string P - { - get - { - return null; - } - } - } - public class B : A - { - private new string F; - private new int P - { - set - { - } - } - } -} -//$$ HideNestedClass -namespace HideNestedClass -{ - public class A - { - public class N1 - { - } - protected class N2 - { - } - private class N3 - { - } - internal class N4 - { - } - protected internal class N5 - { - } - } - public class B : A - { - public new int N1; - public new int N2; - public int N3; - public new int N4; - public new int N5; - } -} -//$$ HidePropertyReservedMethod -namespace HidePropertyReservedMethod -{ - public class A - { - public int P - { - get - { - return 1; - } - } - } - public class B : A - { - public int get_P() - { - return 2; - } - public void set_P(int value) - { - } - } -} -//$$ HideIndexerDiffAccessor -namespace HideIndexerDiffAccessor -{ - public class A - { - public int this[int i] - { - get - { - return 2; - } - } - } - public class B : A - { - public new int this[int j] - { - set - { - } - } - } -} -//$$ HideIndexerGeneric -namespace HideIndexerGeneric -{ - public class A<T> - { - public virtual int this[T r] - { - get - { - return 0; - } - set - { - } - } - } - public class B : A<int> - { - private new int this[int k] - { - get - { - return 0; - } - set - { - } - } - } - public class C<T> : A<T> - { - public override int this[T s] - { - set - { - } - } - } - public class D<T> : C<T> - { - public new virtual int this[T s] - { - set - { - } - } - } -} -//$$ HideMethod -namespace HideMethod -{ - public class A - { - public virtual void F() - { - } - } - public class B : A - { - private new void F() - { - base.F(); - } - } - public class C : B - { - public override void F() - { - base.F(); - } - } -} -//$$ HideMethodGeneric -namespace HideMethodGeneric -{ - public class A<T> - { - public virtual void F(T s) - { - } - public new static bool Equals(object o1, object o2) - { - return true; - } - } - public class B : A<string> - { - private new void F(string k) - { - } - public void F(int i) - { - } - } - public class C<T> : A<T> - { - public override void F(T r) - { - } - public void G(T t) - { - } - } - public class D<T1> : C<T1> - { - public new virtual void F(T1 k) - { - } - public virtual void F<T2>(T2 k) - { - } - public virtual void G<T2>(T2 t) - { - } - } -} -//$$ HideMethodGenericSkipPrivate -namespace HideMethodGenericSkipPrivate -{ - public class A<T> - { - public virtual void F(T t) - { - } - } - public class B<T> : A<T> - { - private new void F(T t) - { - } - private void K() - { - } - } - public class C<T> : B<T> - { - public override void F(T tt) - { - } - public void K() - { - } - } - public class D : B<int> - { - public override void F(int t) - { - } - } -} -//$$ HideMethodGeneric2 -namespace HideMethodGeneric2 -{ - public class A - { - public virtual void F(int i) - { - } - public void K() - { - } - } - public class B<T> : A - { - protected virtual void F(T t) - { - } - public void K<T2>() - { - } - } - public class C : B<int> - { - protected override void F(int k) - { - } - public new void K<T3>() - { - } - } - public class D : B<string> - { - public override void F(int k) - { - } - public void L<T4>() - { - } - } - public class E<T> - { - public void M<T2>(T t, T2 t2) - { - } - } - public class F<T> : E<T> - { - public void M(T t1, T t2) - { - } - } -} -//$$ HideMethodDiffSignatures -namespace HideMethodDiffSignatures -{ - public class C1<T> - { - public virtual void M(T arg) - { - } - } - public class C2<T1, T2> : C1<T2> - { - public new virtual void M(T2 arg) - { - } - } - public class C3 : C2<int, bool> - { - public new virtual void M(bool arg) - { - } - } -} -//$$ HideMethodStatic -namespace HideMethodStatic -{ - public class A - { - public int N - { - get - { - return 0; - } - } - } - public class B - { - public int N() - { - return 0; - } - } -} -//$$ HideEvent -namespace HideEvent -{ - public class A - { - public virtual event EventHandler E; - public event EventHandler F; - } - public class B : A - { - public new virtual event EventHandler E; - public new event EventHandler F; - } - public class C : B - { - public override event EventHandler E; - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Types/TypeTests.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Types/TypeTests.cs deleted file mode 100644 index 3bb80ed84b..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/Types/TypeTests.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using NUnit.Framework; - -namespace ICSharpCode.Decompiler.Tests.Types -{ - [TestFixture] - public class TypeTests : DecompilerTestBase - { - [Test] - public void TypeMemberDeclarations() - { - ValidateFileRoundtrip(@"Types\S_TypeMemberDeclarations.cs"); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/UndocumentedExpressions.cs b/main/contrib/ICSharpCode.Decompiler/Tests/UndocumentedExpressions.cs deleted file mode 100644 index 80f5b0371e..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/UndocumentedExpressions.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 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; - -public class UndocumentedExpressions -{ - public static int GetArgCount(__arglist) - { - ArgIterator argIterator = new ArgIterator(__arglist); - return argIterator.GetRemainingCount(); - } - - public static void MakeTypedRef(object o) - { - TypedReference tr = __makeref(o); - UndocumentedExpressions.AcceptTypedRef(tr); - } - - private static void AcceptTypedRef(TypedReference tr) - { - Console.WriteLine("Value is: " + __refvalue(tr, object).ToString()); - Console.WriteLine("Type is: " + __reftype(tr).Name); - __refvalue(tr, object) = 1; - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/UnsafeCode.cs b/main/contrib/ICSharpCode.Decompiler/Tests/UnsafeCode.cs deleted file mode 100644 index cce6d525c7..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/UnsafeCode.cs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (c) 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; - -public class UnsafeCode -{ - public unsafe int* NullPointer - { - get - { - return null; - } - } - - public unsafe long ConvertDoubleToLong(double d) - { - return *(long*)(&d); - } - - public unsafe double ConvertLongToDouble(long d) - { - return *(double*)(&d); - } - - public unsafe int ConvertFloatToInt(float d) - { - return *(int*)(&d); - } - - public unsafe float ConvertIntToFloat(int d) - { - return *(float*)(&d); - } - - public unsafe void PassRefParameterAsPointer(ref int p) - { - fixed (int* ptr = &p) - { - this.PassPointerAsRefParameter(ptr); - } - } - - public unsafe void PassPointerAsRefParameter(int* p) - { - this.PassRefParameterAsPointer(ref *p); - } - - public unsafe void AddressInMultiDimensionalArray(double[,] matrix) - { - fixed (double* ptr = &matrix[1, 2]) - { - this.PointerReferenceExpression(ptr); - } - } - - public unsafe void FixedStringAccess(string text) - { - fixed (char* ptr = text) - { - char* ptr2 = ptr; - while (*ptr2 != '\0') - { - *ptr2 = 'A'; - ptr2++; - } - } - } - - public unsafe void PutDoubleIntoLongArray1(long[] array, int index, double val) - { - fixed (long* ptr = array) - { - ((double*)ptr)[index] = val; - } - } - - public unsafe void PutDoubleIntoLongArray2(long[] array, int index, double val) - { - fixed (long* ptr = &array[index]) - { - *(double*)ptr = val; - } - } - - public unsafe string PointerReferenceExpression(double* d) - { - return d->ToString(); - } - - public unsafe void FixMultipleStrings(string text) - { - fixed (char* ptr = text, userName = Environment.UserName, ptr2 = text) - { - *ptr = 'c'; - *userName = 'd'; - *ptr2 = 'e'; - } - } - - public unsafe string StackAlloc(int count) - { - char* ptr = stackalloc char[count]; - for (int i = 0; i < count; i++) - { - ptr[i] = (char)i; - } - return this.PointerReferenceExpression((double*)ptr); - } - - public unsafe int* PointerArithmetic(int* p) - { - return p + 2; - } - - public unsafe byte* PointerArithmetic2(long* p, int y, int x) - { - return (byte*)((short*)p + (y * x)); - } - - public unsafe long* PointerArithmetic3(long* p) - { - return (long*)((byte*)p + 3); - } - - public unsafe long* PointerArithmetic4(void* p) - { - return (long*)((byte*)p + 3); - } - - public unsafe int PointerArithmetic5(void* p, byte* q, int i) - { - return (int)(q[i] + *(byte*)p); - } - - public unsafe int PointerSubtraction(long* p, long* q) - { - return (int)((long)(p - q)); - } - - public unsafe int PointerSubtraction2(long* p, short* q) - { - return (int)((long)((byte*)p - (byte*)q)); - } - - public unsafe int PointerSubtraction3(void* p, void* q) - { - return (int)((long)((byte*)p - (byte*)q)); - } - - unsafe ~UnsafeCode() - { - this.PassPointerAsRefParameter(this.NullPointer); - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/ValueTypes.cs b/main/contrib/ICSharpCode.Decompiler/Tests/ValueTypes.cs deleted file mode 100644 index 1493cff4c9..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/ValueTypes.cs +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (c) 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; - -public static class ValueTypes -{ - public struct S - { - public int Field; - - public S(int field) - { - this.Field = field; - } - - public void SetField() - { - this.Field = 5; - } - - public void MethodCalls() - { - this.SetField(); - ValueTypes.S.Test(this); - ValueTypes.S.Test(ref this); - } - - private static void Test(ValueTypes.S byVal) - { - } - - private static void Test(ref ValueTypes.S byRef) - { - } - } - - private static readonly ValueTypes.S ReadOnlyS = default(ValueTypes.S); - private static ValueTypes.S MutableS = default(ValueTypes.S); - private static volatile int VolatileInt; - - public static void CallMethodViaField() - { - ValueTypes.ReadOnlyS.SetField(); - ValueTypes.MutableS.SetField(); - ValueTypes.S mutableS = ValueTypes.MutableS; - mutableS.SetField(); - } - - public static ValueTypes.S InitObj1() - { - ValueTypes.S result = default(ValueTypes.S); - ValueTypes.MakeArray(); - return result; - } - - public static ValueTypes.S InitObj2() - { - return default(ValueTypes.S); - } - - public static void InitObj3(out ValueTypes.S p) - { - p = default(ValueTypes.S); - } - - public static ValueTypes.S CallValueTypeCtor1() - { - return new ValueTypes.S(10); - } - - public static ValueTypes.S CallValueTypeCtor2() - { - ValueTypes.S result = new ValueTypes.S(10); - return result; - } - - public static ValueTypes.S Copy1(ValueTypes.S p) - { - return p; - } - - public static ValueTypes.S Copy2(ref ValueTypes.S p) - { - return p; - } - - public static void Copy3(ValueTypes.S p, out ValueTypes.S o) - { - o = p; - } - - public static void Copy4(ref ValueTypes.S p, out ValueTypes.S o) - { - o = p; - } - - public static void Copy4b(ref ValueTypes.S p, out ValueTypes.S o) - { - // test passing through by-ref arguments - ValueTypes.Copy4(ref p, out o); - } - - public static void Issue56(int i, out string str) - { - str = "qq"; - str += i.ToString(); - } - - public static void CopyAroundAndModifyField(ValueTypes.S s) - { - ValueTypes.S s2 = s; - s2.Field += 10; - s = s2; - } - - private static int[] MakeArray() - { - return null; - } - - public static void IncrementArrayLocation() - { - ValueTypes.MakeArray()[Environment.TickCount]++; - } - - public static bool Is(object obj) - { - return obj is ValueTypes.S; - } - - public static bool IsNullable(object obj) - { - return obj is ValueTypes.S?; - } - - public static ValueTypes.S? As(object obj) - { - return obj as ValueTypes.S?; - } - - public static ValueTypes.S OnlyChangeTheCopy(ValueTypes.S p) - { - ValueTypes.S s = p; - s.SetField(); - return p; - } - - public static void UseRefBoolInCondition(ref bool x) - { - if (x) - { - Console.WriteLine("true"); - } - } - - public static void CompareNotEqual0IsReallyNotEqual(IComparable<int> a) - { - if (a.CompareTo(0) != 0) - { - Console.WriteLine("true"); - } - } - - public static void CompareEqual0IsReallyEqual(IComparable<int> a) - { - if (a.CompareTo(0) == 0) - { - Console.WriteLine("true"); - } - } - -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/YieldReturn.cs b/main/contrib/ICSharpCode.Decompiler/Tests/YieldReturn.cs deleted file mode 100644 index 7546b194f6..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/YieldReturn.cs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Collections.Generic; - -public static class YieldReturn -{ - public static IEnumerable<string> SimpleYieldReturn() - { - yield return "A"; - yield return "B"; - yield return "C"; - } - - public static IEnumerable<int> YieldReturnInLoop() - { - for (int i = 0; i < 100; i++) { - yield return i; - } - } - - public static IEnumerable<int> YieldReturnWithTryFinally() - { - yield return 0; - try { - yield return 1; - } finally { - Console.WriteLine("Finally!"); - } - yield return 2; - } - - public static IEnumerable<int> YieldReturnInLock1(object o) - { - lock (o) { - yield return 1; - } - } - - public static IEnumerable<int> YieldReturnInLock2(object o) - { - lock (o) { - yield return 1; - o = null; - yield return 2; - } - } - - public static IEnumerable<string> YieldReturnWithNestedTryFinally(bool breakInMiddle) - { - Console.WriteLine("Start of method - 1"); - yield return "Start of method"; - Console.WriteLine("Start of method - 2"); - try { - Console.WriteLine("Within outer try - 1"); - yield return "Within outer try"; - Console.WriteLine("Within outer try - 2"); - try { - Console.WriteLine("Within inner try - 1"); - yield return "Within inner try"; - Console.WriteLine("Within inner try - 2"); - if (breakInMiddle) - yield break; - Console.WriteLine("End of inner try - 1"); - yield return "End of inner try"; - Console.WriteLine("End of inner try - 2"); - } finally { - Console.WriteLine("Inner Finally"); - } - Console.WriteLine("End of outer try - 1"); - yield return "End of outer try"; - Console.WriteLine("End of outer try - 2"); - } finally { - Console.WriteLine("Outer Finally"); - } - Console.WriteLine("End of method - 1"); - yield return "End of method"; - Console.WriteLine("End of method - 2"); - } - - public static IEnumerable<string> YieldReturnWithTwoNonNestedFinallyBlocks(IEnumerable<string> input) - { - // outer try-finally block - foreach (string line in input) { - // nested try-finally block - try { - yield return line; - } finally { - Console.WriteLine("Processed " + line); - } - } - yield return "A"; - yield return "B"; - yield return "C"; - yield return "D"; - yield return "E"; - yield return "F"; - // outer try-finally block - foreach (string line in input) - yield return line.ToUpper(); - } - - public static IEnumerable<Func<string>> YieldReturnWithAnonymousMethods1(IEnumerable<string> input) - { - foreach (string line in input) { - yield return () => line; - } - } - - public static IEnumerable<Func<string>> YieldReturnWithAnonymousMethods2(IEnumerable<string> input) - { - foreach (string line in input) { - string copy = line; - yield return () => copy; - } - } - - public static IEnumerable<int> GetEvenNumbers(int n) - { - for (int i = 0; i < n; i++) { - if (i % 2 == 0) - yield return i; - } - } - - public static IEnumerable<char> YieldChars() - { - yield return 'a'; - yield return 'b'; - yield return 'c'; - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/nunit.framework.dll b/main/contrib/ICSharpCode.Decompiler/Tests/nunit.framework.dll Binary files differdeleted file mode 100644 index 875e098420..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/nunit.framework.dll +++ /dev/null diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/packages.config b/main/contrib/ICSharpCode.Decompiler/Tests/packages.config deleted file mode 100644 index 4d992b4245..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/Tests/packages.config +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<packages> - <package id="DiffLib" version="1.0.0.55" /> -</packages>
\ No newline at end of file diff --git a/main/contrib/ICSharpCode.Decompiler/TextOutputWriter.cs b/main/contrib/ICSharpCode.Decompiler/TextOutputWriter.cs deleted file mode 100644 index 964187a316..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/TextOutputWriter.cs +++ /dev/null @@ -1,55 +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.IO; -using System.Text; - -namespace ICSharpCode.Decompiler -{ - public class TextOutputWriter : TextWriter - { - readonly ITextOutput output; - - public TextOutputWriter(ITextOutput output) - { - if (output == null) - throw new ArgumentNullException("output"); - this.output = output; - } - - public override Encoding Encoding { - get { return Encoding.UTF8; } - } - - public override void Write(char value) - { - output.Write(value); - } - - public override void Write(string value) - { - output.Write(value); - } - - public override void WriteLine() - { - output.WriteLine(); - } - } -} diff --git a/main/contrib/ICSharpCode.Decompiler/packages.config b/main/contrib/ICSharpCode.Decompiler/packages.config deleted file mode 100644 index bf394b856a..0000000000 --- a/main/contrib/ICSharpCode.Decompiler/packages.config +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<packages>
- <package id="ICSharpCode.NRefactory" version="5.5.1" targetFramework="net40-Client" />
- <package id="Mono.Cecil" version="0.10.0-beta6" targetFramework="net40" />
-</packages>
\ No newline at end of file diff --git a/main/contrib/Makefile.am b/main/contrib/Makefile.am deleted file mode 100644 index 2509f9c86f..0000000000 --- a/main/contrib/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -SUBDIRS = ICSharpCode.Decompiler - -EXTRA_DIST = $(srcdir)/extract_makefile_variable.sh - diff --git a/main/contrib/extract_makefile_variable.sh b/main/contrib/extract_makefile_variable.sh deleted file mode 100644 index 4bfde712ea..0000000000 --- a/main/contrib/extract_makefile_variable.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -sed -e :a -e '/\\$/N; s/\\\n//; ta' -e "/^$2/!d" -e "s/$2 = //" $1 diff --git a/main/external/fsharpbinding/MonoDevelop.FSharpBinding/RefactoringOperationsHandler.fs b/main/external/fsharpbinding/MonoDevelop.FSharpBinding/RefactoringOperationsHandler.fs index d5ed9d0d4e..faaa003083 100644 --- a/main/external/fsharpbinding/MonoDevelop.FSharpBinding/RefactoringOperationsHandler.fs +++ b/main/external/fsharpbinding/MonoDevelop.FSharpBinding/RefactoringOperationsHandler.fs @@ -129,12 +129,12 @@ module Refactoring = lineInfo, symbol /// Perform the renaming of a symbol - let renameSymbol (editor:TextEditor, ctx:DocumentContext, lastIdent, symbol:FSharpSymbolUse) = + let renameSymbol (editor:TextEditor, ctx:DocumentContext, lastIdent, symbolUse:FSharpSymbolUse) = // Collect the uses of the symbol across the solution. The use of RunSynchronously makes this a blocking UI // action and will presumably cause the operation to fail with a timeout exception if it takes too long. let symbols = let activeDocFileName = editor.FileName.ToString () - languageService.GetUsesOfSymbolInProject (ctx.Project.FileName.ToString(), activeDocFileName, editor.Text, symbol.Symbol) + languageService.GetUsesOfSymbolInProject (ctx.Project.FileName.ToString(), activeDocFileName, editor.Text, symbolUse.Symbol) |> (fun p -> Async.RunSynchronously(p, timeout=ServiceSettings.maximumTimeout)) let locations = @@ -159,7 +159,7 @@ module Refactoring = links.Add (link) editor.StartTextLinkMode (TextLinkModeOptions (links)) else - MessageService.ShowCustomDialog (Dialog.op_Implicit (new Rename.RenameItemDialog("Rename Item", symbol.Symbol.DisplayName, performChanges symbol locations))) + MessageService.ShowCustomDialog (Dialog.op_Implicit (new Rename.RenameItemDialog("Rename Item", symbolUse.Symbol.DisplayName, performChanges symbolUse locations))) |> ignore let getJumpTypePartSearchResult (location: Range.range) = @@ -241,6 +241,9 @@ module Refactoring = p.GetOutputFileName(config).ToString() |> Path.GetFileNameWithoutExtension let getDependentProjects (project:Project) (symbolUse:FSharpSymbolUse) = + match symbolUse with + | Val _local -> [] + | _ -> try let allProjects = project.ParentSolution.GetAllProjects() |> Seq.toList let config = IdeApp.Workspace.ActiveConfiguration diff --git a/main/external/mono-addins b/main/external/mono-addins -Subproject 1499f1e5b93946ea973726329b28f06c4b35a34 +Subproject c1d963c49a3c7311c92c3b85630afbc83807298 diff --git a/main/monodevelop.in b/main/monodevelop.in index bd56ab8d78..2cb4119b7b 100755 --- a/main/monodevelop.in +++ b/main/monodevelop.in @@ -9,6 +9,9 @@ export LIBOVERLAY_SCROLLBAR=0 # The Oxygen GTK theme crashes unless this is set export OXYGEN_DISABLE_INNER_SHADOWS_HACK=1 +# Add our bin/ to LD_LIBRARY_PATH so we can override system libs with bundles +export LD_LIBRARY_PATH="${0%%/bin/monodevelop}/lib/monodevelop/bin/:${LD_LIBRARY_PATH}" + #this script should be in $PREFIX/bin MONO_EXEC="exec -a monodevelop mono-sgen" EXE_PATH="${0%%/bin/monodevelop}/lib/monodevelop/bin/MonoDevelop.exe" diff --git a/main/src/addins/AspNet/Tests/MonoDevelop.AspNet.Tests.csproj b/main/src/addins/AspNet/Tests/MonoDevelop.AspNet.Tests.csproj index f9cf1c7f23..04b84aae5c 100644 --- a/main/src/addins/AspNet/Tests/MonoDevelop.AspNet.Tests.csproj +++ b/main/src/addins/AspNet/Tests/MonoDevelop.AspNet.Tests.csproj @@ -130,9 +130,9 @@ <Name>MonoDevelop.SourceEditor</Name> <Private>False</Private> </ProjectReference> - <ProjectReference Include="..\..\..\..\tests\Ide.Tests\MonoDevelop.Ide.Tests.csproj"> - <Project>{73D4CC8B-BAB9-4A29-841B-F25C6311F067}</Project> - <Name>MonoDevelop.Ide.Tests</Name> + <ProjectReference Include="..\..\..\..\tests\IdeUnitTests\IdeUnitTests.csproj"> + <Project>{F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}</Project> + <Name>IdeUnitTests</Name> <Private>False</Private> </ProjectReference> </ItemGroup> diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/Counters.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/Counters.cs index 51dccb3cf8..a445e26877 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/Counters.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/Counters.cs @@ -32,7 +32,7 @@ namespace MonoDevelop.CSharp.Navigation { internal static class Counters { - public static TimerCounter NavigateTo = InstrumentationService.CreateTimerCounter ("Navigate to", "Code Navigation", id: "CodeNavigation.NavigateTo"); + public static TimerCounter<NavigationMetadata> NavigateTo = InstrumentationService.CreateTimerCounter<NavigationMetadata> ("Navigate to", "Code Navigation", id: "CodeNavigation.NavigateTo"); public static IDictionary<string, string> CreateNavigateToMetadata (string navigationType) { @@ -65,5 +65,34 @@ namespace MonoDevelop.CSharp.Navigation { metadata ["Result"] = "UserFault"; } + + public class NavigationMetadata: CounterMetadata + { + public NavigationMetadata () + { + } + + public NavigationMetadata (string type) + { + Properties["Type"] = type; + Result = CounterResult.Failure; // Will be updated when navigation finishes. + } + + public string Type { + get { return Properties.TryGetValue ("Type", out var type) ? type : null; } + set { Properties ["Type"] = value; } + } + + public void SetResult (bool result) + { + Properties ["Result"] = result ? "Success" : "Failure"; + } + + public void UpdateUserCancellation (CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + Properties ["Result"] = "UserCancel"; + } + } } } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindBaseSymbolsHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindBaseSymbolsHandler.cs index 7dceb59c9b..807570635f 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindBaseSymbolsHandler.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindBaseSymbolsHandler.cs @@ -84,20 +84,19 @@ namespace MonoDevelop.CSharp.Navigation protected override async void Run () { - var metadata = Counters.CreateNavigateToMetadata ("BaseSymbols"); - using (var timer = Counters.NavigateTo.BeginTiming (metadata)) { + using (var timer = Counters.NavigateTo.BeginTiming (new Counters.NavigationMetadata ("BaseSymbols"))) { var sym = await GetSymbolAtCaret (IdeApp.Workbench.ActiveDocument); if (sym == null) { - Counters.UpdateUserFault (metadata); + timer.Metadata.SetUserFault (); return; } using (var source = new CancellationTokenSource ()) { try { await FindSymbols (sym, source); - Counters.UpdateNavigateResult (metadata, true); + timer.Metadata.SetResult (true); } finally { - Counters.UpdateUserCancellation (metadata, source.Token); + timer.Metadata.UpdateUserCancellation (source.Token); } } } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.OptionProvider/CSharpDocumentOptionsProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.OptionProvider/CSharpDocumentOptionsProvider.cs index 52546c9416..aeed1835e3 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.OptionProvider/CSharpDocumentOptionsProvider.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.OptionProvider/CSharpDocumentOptionsProvider.cs @@ -113,16 +113,6 @@ namespace MonoDevelop.CSharp.OptionProvider } } - if (option.Option == Microsoft.CodeAnalysis.Editing.GenerationOptions.PlaceSystemNamespaceFirst) { - value = PropertyService.Get (LanguageNames.CSharp + ".PlaceSystemNamespaceFirst", true); - return true; - } - - if (option.Option == Microsoft.CodeAnalysis.Editing.GenerationOptions.SeparateImportDirectiveGroups) { - value = PropertyService.Get (LanguageNames.CSharp + ".SeparateImportDirectiveGroups", false); - return true; - } - var result = optionSet.GetOption (option); if (result == underlyingOptions.GetOption (option)) { diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpFoldingParser.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpFoldingParser.cs index 378decad02..cf6ba5fb22 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpFoldingParser.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpFoldingParser.cs @@ -184,13 +184,15 @@ namespace MonoDevelop.CSharp.Parser ptr += 2; column += 2; inMultiLineComment = false; - result.Add (new MonoDevelop.Ide.TypeSystem.Comment () { - Region = new DocumentRegion (startLoc, new DocumentLocation (line, column)), - OpenTag = "/*", - CommentType = MonoDevelop.Ide.TypeSystem.CommentType.Block, - Text = content.Substring ((int)(beginPtr - startPtr), (int)(ptr - beginPtr)), - CommentStartsLine = hasStartedAtLine - }); + if (bracketDepth <= 1) { + result.Add (new MonoDevelop.Ide.TypeSystem.Comment () { + Region = new DocumentRegion (startLoc, new DocumentLocation (line, column)), + OpenTag = "/*", + CommentType = MonoDevelop.Ide.TypeSystem.CommentType.Block, + Text = content.Substring ((int)(beginPtr - startPtr), (int)(ptr - beginPtr)), + CommentStartsLine = hasStartedAtLine + }); + } continue; } } @@ -388,5 +390,4 @@ namespace MonoDevelop.CSharp.Parser return text.Substring (start, end - start); } } -} - +}
\ No newline at end of file diff --git a/main/src/addins/CSharpBinding/MonoDevelop.Ide.Completion.Presentation/RoslynCommandTarget.cs b/main/src/addins/CSharpBinding/MonoDevelop.Ide.Completion.Presentation/RoslynCommandTarget.cs index 97bee57d7e..277808578b 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.Ide.Completion.Presentation/RoslynCommandTarget.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.Ide.Completion.Presentation/RoslynCommandTarget.cs @@ -24,13 +24,6 @@ namespace MonoDevelop.Ide.Completion.Presentation internal ITextBuffer _languageBuffer;
internal ITextView _textView;
- static RoslynCommandTarget ()
- {
- var defaultForegroundThreadData = ForegroundThreadData.CreateDefault (
- defaultKind: ForegroundThreadDataKind.ForcedByPackageInitialize);
- ForegroundThreadAffinitizedObject.CurrentForegroundThreadData = defaultForegroundThreadData;
- }
-
private RoslynCommandTarget (ITextView textView, ITextBuffer languageBuffer)
{
var commandHandlerServiceFactory = CompositionManager.GetExportedValue<ICommandHandlerServiceFactory> ();
diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser.csproj b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser.csproj index 9af811aea2..b26060629e 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser.csproj +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser.csproj @@ -87,6 +87,15 @@ <HintPath>..\..\..\packages\Mono.Cecil.0.10.0-beta6\lib\net40\Mono.Cecil.Rocks.dll</HintPath> <Private>False</Private> </Reference> + <Reference Include="mscorlib" /> + <Reference Include="System.ValueTuple"> + <HintPath>..\..\..\build\bin\System.ValueTuple.dll</HintPath> + <Private>False</Private> + </Reference> + <Reference Include="Microsoft.CodeAnalysis.Workspaces"> + <HintPath>..\..\..\build\bin\Microsoft.CodeAnalysis.Workspaces.dll</HintPath> + <Private>False</Private> + </Reference> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\core\MonoDevelop.Core\MonoDevelop.Core.csproj"> @@ -124,19 +133,12 @@ <Compile Include="AssemblyInfo.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\DisplayBinding.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\AssemblyBrowserWidget.cs" /> - <Compile Include="MonoDevelop.AssemblyBrowser\Namespace.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\Error.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\BaseTypeFolder.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\IAssemblyBrowserNodeBuilder.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\DocumentationPanel.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\ColoredCSharpFormatter.cs" /> - <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\DomEventNodeBuilder.cs" /> - <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\DomFieldNodeBuilder.cs" /> - <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\DomMethodNodeBuilder.cs" /> - <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\DomPropertyNodeBuilder.cs" /> - <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\DomTypeNodeBuilder.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\AssemblyBrowserTypeNodeBuilder.cs" /> - <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\AssemblyNodeBuilder.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\BaseTypeFolderNodeBuilder.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\MethodBodyRemoveVisitor.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\NamespaceBuilder.cs" /> @@ -146,21 +148,36 @@ <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\AssemblyReferenceNodeBuilder.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\AssemblyReferenceFolderNodeBuilder.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\AssemblyResourceFolderNodeBuilder.cs" /> - <Compile Include="MonoDevelop.AssemblyBrowser\AssemblyBrowserHandler.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\AssemblyResourceFolder.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\AssemblyBrowserViewContent.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\AssemblyReferenceFolder.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\AssemblyLoader.cs" /> - <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\ProjectNodeBuilder.cs" /> <Compile Include="XmlDocIdLib\CompatibilityType.cs" /> <Compile Include="XmlDocIdLib\OperatorType.cs" /> <Compile Include="XmlDocIdLib\XmlDocIdGenerator.cs" /> <Compile Include="AddinInfo.cs" /> - <Compile Include="MonoDevelop.AssemblyBrowser\NRefactoryStock.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\HelpExtensions.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\AssemblyBrowserNavigationPoint.cs" /> <Compile Include="MonoDevelop.AssemblyBrowser\UnresolvedIdStringProvider.cs" /> <Compile Include="Gui\MonoDevelop.AssemblyBrowser.AssemblyBrowserWidget.cs" /> + <Compile Include="Util\CSharpLanguage.cs" /> + <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\Cecil\EventDefinitionNodeBuilder.cs" /> + <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\Cecil\FieldDefinitionNodeBuilder.cs" /> + <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\Cecil\MethodDefinitionNodeBuilder.cs" /> + <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\Cecil\PropertyDefinitionNodeBuilder.cs" /> + <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\Cecil\TypeDefinitionNodeBuilder.cs" /> + <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\Roslyn\ProjectNodeBuilder.cs" /> + <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\Roslyn\RoslynEventNodeBuilder.cs" /> + <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\Roslyn\RoslynFieldNodeBuilder.cs" /> + <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\Roslyn\RoslynMemberNodeBuilder.cs" /> + <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\Roslyn\RoslynMethodNodeBuilder.cs" /> + <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\Roslyn\RoslynPropertyNodeBuilder.cs" /> + <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\Roslyn\RoslynTypeNodeBuilder.cs" /> + <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\NamespaceData.cs" /> + <Compile Include="MonoDevelop.AssemblyBrowser\TreeNodes\Cecil\AssemblyNodeBuilder.cs" /> + <Compile Include="MonoDevelop.AssemblyBrowser\AssemblyBrowserHandler.cs" /> + <Compile Include="MonoDevelop.AssemblyBrowser\AssemblyBrowserWidget.AssemblyBrowserTreeView.cs" /> + <Compile Include="MonoDevelop.AssemblyBrowser\AssemblyBrowserWidget.SearchIdleRunner.cs" /> </ItemGroup> <ItemGroup> <EmbeddedResource Include="MonoDevelop.AssemblyBrowser.addin.xml"> diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserNavigationPoint.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserNavigationPoint.cs index f06f4acf01..7790c79a8d 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserNavigationPoint.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserNavigationPoint.cs @@ -30,15 +30,16 @@ using MonoDevelop.Ide.Gui; using MonoDevelop.Ide.Navigation; using System.Collections.Generic; using System.Threading.Tasks; +using System.Collections.Immutable; namespace MonoDevelop.AssemblyBrowser { class AssemblyBrowserNavigationPoint : DocumentNavigationPoint { - List<AssemblyLoader> definitions; + ImmutableList<AssemblyLoader> definitions; string idString; - public AssemblyBrowserNavigationPoint (List<AssemblyLoader> definitions, AssemblyLoader assembly, string idString) : base (assembly?.FileName) + public AssemblyBrowserNavigationPoint (ImmutableList<AssemblyLoader> definitions, AssemblyLoader assembly, string idString) : base (assembly?.FileName) { this.definitions = definitions; this.idString = idString; diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserViewContent.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserViewContent.cs index 965bfc7cb2..beb7c29a44 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserViewContent.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserViewContent.cs @@ -36,6 +36,9 @@ using MonoDevelop.Ide.Navigation; using MonoDevelop.Projects; using System.Collections.Generic;
using System.Threading.Tasks; +using System.Collections.Immutable; +using MonoDevelop.Ide; +using Microsoft.CodeAnalysis.CodeRefactorings; namespace MonoDevelop.AssemblyBrowser { @@ -84,7 +87,7 @@ namespace MonoDevelop.AssemblyBrowser return Task.FromResult (true); } - internal void EnsureDefinitionsLoaded (List<AssemblyLoader> definitions) + internal void EnsureDefinitionsLoaded (ImmutableList<AssemblyLoader> definitions) { widget.EnsureDefinitionsLoaded (definitions); } @@ -104,6 +107,9 @@ namespace MonoDevelop.AssemblyBrowser { IsDisposed = true; base.Dispose (); + if (currentWs != null) + currentWs.WorkspaceLoaded -= Handle_WorkspaceLoaded; + widget = null; if (Disposed != null) Disposed (this, EventArgs.Empty); @@ -157,6 +163,16 @@ namespace MonoDevelop.AssemblyBrowser //FindDerivedClassesHandler.FindDerivedClasses (type); } + void Handle_WorkspaceLoaded (object sender, EventArgs e) + { + foreach (var project in Ide.IdeApp.ProjectOperations.CurrentSelectedSolution.GetAllProjects ()) { + var nav = Widget.TreeView.GetNodeAtObject (project); + if (nav != null) + Widget.TreeView.RefreshNode (nav); + } + } + + Ide.TypeSystem.MonoDevelopWorkspace currentWs; public async void FillWidget () { if (Ide.IdeApp.ProjectOperations.CurrentSelectedSolution == null) { @@ -164,21 +180,35 @@ namespace MonoDevelop.AssemblyBrowser Widget.AddReferenceByAssemblyName (assembly); } } else { + var alreadyAdded = new HashSet<string> (); + currentWs = MonoDevelop.Ide.TypeSystem.TypeSystemService.GetWorkspace (Ide.IdeApp.ProjectOperations.CurrentSelectedSolution); + if (currentWs != null) + currentWs.WorkspaceLoaded += Handle_WorkspaceLoaded; + var allTasks = new List<Task> (); foreach (var project in Ide.IdeApp.ProjectOperations.CurrentSelectedSolution.GetAllProjects ()) { - Widget.AddProject (project, false); - - var netProject = project as DotNetProject; - if (netProject == null) - continue; - foreach (var file in await netProject.GetReferencedAssemblies (ConfigurationSelector.Default, false)) { - if (!System.IO.File.Exists (file.FilePath)) + try { + Widget.AddProject (project, false); + var netProject = project as DotNetProject; + if (netProject == null) continue; - Widget.AddReferenceByFileName (file.FilePath); + foreach (var file in await netProject.GetReferencedAssemblies (ConfigurationSelector.Default, false)) { + if (!System.IO.File.Exists (file.FilePath)) + continue; + if (!alreadyAdded.Add (file.FilePath)) + continue; + var loader = Widget.AddReferenceByFileName (file.FilePath); + allTasks.Add (loader.LoadingTask); + } + } catch (Exception e) { + LoggingService.LogError ("Error while adding project " + project.Name + " to the tree.", e); } } + await Task.WhenAll (allTasks).ContinueWith (delegate { + Runtime.RunInMainThread (delegate { + widget.StartSearch (); + }); + }); } } } - - } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserWidget.AssemblyBrowserTreeView.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserWidget.AssemblyBrowserTreeView.cs new file mode 100644 index 0000000000..25773d5413 --- /dev/null +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserWidget.AssemblyBrowserTreeView.cs @@ -0,0 +1,62 @@ +// +// AssemblyBrowserWidget.cs +// +// Author: +// Mike Krüger <mkrueger@novell.com> +// +// Copyright (C) 2008 Novell, Inc (http://www.novell.com) +// +// 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 MonoDevelop.Ide.Gui.Components; + +namespace MonoDevelop.AssemblyBrowser +{ + partial class AssemblyBrowserWidget + { + public class AssemblyBrowserTreeView : ExtensibleTreeView + { + bool publicApiOnly = true; + + public bool PublicApiOnly { + get { + return publicApiOnly; + } + set { + if (publicApiOnly == value) + return; + publicApiOnly = value; + var root = GetRootNode (); + if (root != null) { + do { + RefreshNode (root); + } while (root.MoveNext ()); + } + } + } + + public AssemblyBrowserTreeView (NodeBuilder [] builders, TreePadOption [] options) : base (builders, options) + { + } + } + } +} + diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserWidget.SearchIdleRunner.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserWidget.SearchIdleRunner.cs new file mode 100644 index 0000000000..88fc84e688 --- /dev/null +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserWidget.SearchIdleRunner.cs @@ -0,0 +1,192 @@ +// +// AssemblyBrowserWidget.cs +// +// Author: +// Mike Krüger <mkrueger@novell.com> +// +// Copyright (C) 2008 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; + +using Mono.Cecil; +using MonoDevelop.Ide; +using System.Threading; +using MonoDevelop.Core.Text; +using System.Collections.Immutable; + +namespace MonoDevelop.AssemblyBrowser +{ + partial class AssemblyBrowserWidget + { + class SearchIdleRunner + { + readonly AssemblyBrowserWidget assemblyBrowserWidget; + readonly CancellationToken token; + readonly List<IMemberDefinition> memberList = new List<IMemberDefinition> (); + readonly bool publicOnly; + readonly string pattern; + readonly ImmutableList<AssemblyLoader> definitions; + readonly SearchMode searchMode; + readonly StringMatcher matcher; + bool fillStepFinished; + int currentDefinition; + int i = 0; + IEnumerator<TypeDefinition> currentTypeEnumerator; + + public SearchIdleRunner (AssemblyBrowserWidget assemblyBrowserWidget, string pattern, CancellationToken token) + { + this.assemblyBrowserWidget = assemblyBrowserWidget; + this.publicOnly = assemblyBrowserWidget.PublicApiOnly; + this.token = token; + this.pattern = pattern; + this.definitions = assemblyBrowserWidget.definitions; + this.searchMode = assemblyBrowserWidget.searchMode; + matcher = StringMatcher.GetMatcher (pattern, true); + } + + public void Update () + { + GLib.Idle.Add (IdleHandler); + } + + int lastUpdateTick = Environment.TickCount; + bool IdleHandler () + { + if (token.IsCancellationRequested || (fillStepFinished && i >= memberList.Count)) { + IdeApp.Workbench.StatusBar.EndProgress (); + IdeApp.Workbench.StatusBar.ShowReady (); + return false; + } + DoFillStep (); + if (i < memberList.Count) { + // only update if there are either many members or some time has passed + if (!fillStepFinished && (memberList.Count - i > 50 || (Environment.TickCount - lastUpdateTick) > 2000)) + return true; + lastUpdateTick = Environment.TickCount; + assemblyBrowserWidget.searchTreeview.FreezeChildNotify (); + for (int j = 0; j < 50 && i < memberList.Count; j++) { + assemblyBrowserWidget.resultListStore.AppendValues (memberList [i++]); + } + assemblyBrowserWidget.searchTreeview.ThawChildNotify (); + } + return true; + } + + void DoFillStep () + { + if (fillStepFinished) + return; + if (currentDefinition >= definitions.Count) { + fillStepFinished = true; + return; + } + var unit = definitions [currentDefinition]; + if (currentTypeEnumerator == null) { + currentTypeEnumerator = unit.Assembly.MainModule.Types.GetEnumerator (); + if (!currentTypeEnumerator.MoveNext ()) { + currentTypeEnumerator = null; + currentDefinition++; + return; + } + } + var type = currentTypeEnumerator.Current; + + if (!currentTypeEnumerator.MoveNext ()) { + currentTypeEnumerator = null; + currentDefinition++; + } + + switch (searchMode) { + case SearchMode.Member: + if (token.IsCancellationRequested) + return; + if (!type.IsPublic && publicOnly) + return; + foreach (var member in type.Methods) { + if (token.IsCancellationRequested) + return; + if (!member.IsPublic && publicOnly) + continue; + if (member.IsSpecialName || member.IsRuntimeSpecialName) + continue; + if (matcher.IsMatch (member.Name)) + memberList.Add (member); + } + foreach (var member in type.Fields) { + if (token.IsCancellationRequested) + return; + if (!member.IsPublic && publicOnly) + continue; + if (member.IsSpecialName || member.IsRuntimeSpecialName) + continue; + if (matcher.IsMatch (member.Name)) + memberList.Add (member); + } + foreach (var member in type.Properties) { + if (token.IsCancellationRequested) + return; + var accessor = member.GetMethod ?? member.SetMethod; + if (!accessor.IsPublic && publicOnly) + continue; + if (member.IsSpecialName || member.IsRuntimeSpecialName) + continue; + if (matcher.IsMatch (member.Name)) + memberList.Add (member); + } + foreach (var member in type.Events) { + if (token.IsCancellationRequested) + return; + if (member.IsSpecialName || member.IsRuntimeSpecialName) + continue; + var accessor = member.AddMethod ?? member.RemoveMethod; + if (!accessor.IsPublic && publicOnly) + continue; + if (matcher.IsMatch (member.Name)) + memberList.Add (member); + } + break; + case SearchMode.Type: + if (!type.IsPublic && publicOnly) + return; + if (type.IsSpecialName || type.IsRuntimeSpecialName || type.Name == "<Module>") + return; + if (matcher.IsMatch (type.FullName)) + memberList.Add (type); + break; + case SearchMode.TypeAndMembers: + if (token.IsCancellationRequested) + return; + if (!type.IsPublic && publicOnly) + return; + if (type.IsSpecialName || type.IsRuntimeSpecialName || type.Name == "<Module>") + return; + if (matcher.IsMatch (type.FullName)) + memberList.Add (type); + goto case SearchMode.Member; + } + } + } + } +} + diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserWidget.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserWidget.cs index 55d23a58c0..840569238b 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserWidget.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserWidget.cs @@ -53,7 +53,7 @@ using MonoDevelop.Ide.Editor; using MonoDevelop.Ide.Navigation; using MonoDevelop.Ide.Gui.Content; using System.IO; -using MonoDevelop.Core.Text; +using System.Collections.Immutable; namespace MonoDevelop.AssemblyBrowser { @@ -87,32 +87,6 @@ namespace MonoDevelop.AssemblyBrowser DocumentationPanel documentationPanel = new DocumentationPanel (); readonly TextEditor inspectEditor; - public class AssemblyBrowserTreeView : ExtensibleTreeView - { - bool publicApiOnly = true; - - public bool PublicApiOnly { - get { - return publicApiOnly; - } - set { - if (publicApiOnly == value) - return; - publicApiOnly = value; - var root = GetRootNode (); - if (root != null) { - do { - RefreshNode (root); - } while (root.MoveNext ()); - } - } - } - - public AssemblyBrowserTreeView (NodeBuilder[] builders, TreePadOption[] options) : base (builders, options) - { - } - } - static string GetLink (ReferenceSegment referencedSegment, out bool? isNotPublic) { isNotPublic = null; @@ -172,7 +146,7 @@ namespace MonoDevelop.AssemblyBrowser comboboxVisibilty.Changed += delegate { TreeView.PublicApiOnly = comboboxVisibilty.Active == 0; PropertyService.Set ("AssemblyBrowser.MemberSelection", comboboxVisibilty.Active); - FillInspectLabel (); + GenerateOutput (); }; searchentry1 = new MonoDevelop.Components.SearchEntry (); @@ -240,13 +214,18 @@ namespace MonoDevelop.AssemblyBrowser new AssemblyResourceFolderNodeBuilder (), new ResourceNodeBuilder (), new NamespaceBuilder (this), - new DomTypeNodeBuilder (this), - new DomMethodNodeBuilder (this), - new DomFieldNodeBuilder (this), - new DomEventNodeBuilder (this), - new DomPropertyNodeBuilder (this), + new TypeDefinitionNodeBuilder (this), + new MethodDefinitionNodeBuilder (this), + new FieldDefinitionNodeBuilder (this), + new EventDefinitionNodeBuilder (this), + new PropertyDefinitionNodeBuilder (this), new BaseTypeFolderNodeBuilder (this), - new BaseTypeNodeBuilder (this) + new BaseTypeNodeBuilder (this), + new RoslynTypeNodeBuilder (this), + new RoslynEventNodeBuilder (), + new RoslynFieldNodeBuilder (), + new RoslynMethodNodeBuilder (), + new RoslynPropertyNodeBuilder (), }, new TreePadOption [0]); TreeView.PublicApiOnly = comboboxVisibilty.Active == 0; TreeView.AllowsMultipleSelection = false; @@ -281,7 +260,7 @@ namespace MonoDevelop.AssemblyBrowser notebook1.Page = 0; //this.searchWidget.Visible = false; - resultListStore = new Gtk.ListStore (typeof(IUnresolvedEntity)); + resultListStore = new Gtk.ListStore (typeof(IMemberDefinition)); CreateColumns (); // this.searchEntry.Changed += SearchEntryhandleChanged; @@ -305,7 +284,6 @@ namespace MonoDevelop.AssemblyBrowser default: throw new ArgumentOutOfRangeException (); } - } internal void SetToolbar (DocumentToolbar toolbar) @@ -354,7 +332,7 @@ namespace MonoDevelop.AssemblyBrowser { TreeIter selectedIter; if (searchTreeview.Selection.GetSelected (out selectedIter)) { - var member = (IUnresolvedEntity)resultListStore.GetValue (selectedIter, 0); + var member = (IMemberDefinition)resultListStore.GetValue (selectedIter, 0); var nav = SearchMember (member); if (nav != null) { @@ -372,7 +350,7 @@ namespace MonoDevelop.AssemblyBrowser { this.notebook1.Page = 0; PropertyService.Set ("AssemblyBrowser.Language", this.languageCombobox.Active); - FillInspectLabel (); + GenerateOutput (); } @@ -387,7 +365,7 @@ namespace MonoDevelop.AssemblyBrowser TreeView.GrabFocus (); } - ITreeNavigator SearchMember (IUnresolvedEntity member, bool expandNode = true) + ITreeNavigator SearchMember (IMemberDefinition member, bool expandNode = true) { return SearchMember (GetIdString (member), expandNode); } @@ -404,72 +382,8 @@ namespace MonoDevelop.AssemblyBrowser } return null; } - - static void AppendTypeReference (StringBuilder result, ITypeReference type) - { - if (type is ByReferenceTypeReference) { - var brtr = (ByReferenceTypeReference)type; - AppendTypeReference (result, brtr.ElementType); - return; - } - - if (type is ArrayTypeReference) { - var array = (ArrayTypeReference)type; - AppendTypeReference (result, array.ElementType); - result.Append ("["); - result.Append (new string (',', array.Dimensions - 1)); - result.Append ("]"); - return; - } - - if (type is PointerTypeReference) { - var ptr = (PointerTypeReference)type; - AppendTypeReference (result, ptr.ElementType); - result.Append ("*"); - return; - } - - if (type is GetClassTypeReference) { - var r = (GetClassTypeReference)type; - var n = r.FullTypeName.TopLevelTypeName; - result.Append (n.Namespace); - result.Append ('.'); - result.Append (n.Name); - return; - } - - if (type is IUnresolvedTypeDefinition) { - result.Append (((IUnresolvedTypeDefinition)type).FullName); - } - - if (type is TypeParameterReference) { - result.Append ('`'); - result.Append (((TypeParameterReference)type).Index); - } - } - - static void AppendHelpParameterList (StringBuilder result, IList<IUnresolvedParameter> parameters) - { - if (parameters == null || parameters.Count == 0) - return; - result.Append ('('); - if (parameters != null) { - for (int i = 0; i < parameters.Count; i++) { - if (i > 0) - result.Append (','); - var p = parameters [i]; - if (p == null) - continue; - AppendTypeReference (result, p.Type); - if (p.IsRef || p.IsOut) { - result.Append ("@"); - } - } - } - result.Append (')'); - } - internal string GetIdString (IUnresolvedEntity member) + internal string GetIdString (IMemberDefinition member) { var result = UnresolvedIdStringProvider.GetIdString (member); // Console.WriteLine (result); @@ -478,24 +392,15 @@ namespace MonoDevelop.AssemblyBrowser bool IsMatch (ITreeNavigator nav, string helpUrl, bool searchType) { - var member = nav.DataItem as IUnresolvedEntity; + var member = nav.DataItem as IMemberDefinition; if (member == null) return false; - - if (searchType) { - if (member is IUnresolvedTypeDefinition) - return GetIdString (member) == helpUrl; - } else { - if (member is IUnresolvedMember) { - return GetIdString (member) == helpUrl; - } - } - return false; + return GetIdString (member) == helpUrl; } static bool SkipChildren (ITreeNavigator nav, string helpUrl, bool searchType) { - if (nav.DataItem is IUnresolvedMember) + if (nav.DataItem is IMemberDefinition && !(nav.DataItem is TypeDefinition)) return true; if (nav.DataItem is BaseTypeFolder) return true; @@ -513,10 +418,10 @@ namespace MonoDevelop.AssemblyBrowser int idx = helpUrl.IndexOf ('~', startIndex); if (idx > 0) endIndex = idx; - var type = nav.DataItem as IUnresolvedTypeDefinition; + var type = nav.DataItem as TypeDefinition; if (type != null && helpUrl.IndexOf (type.FullName, startIndex, Math.Min (endIndex - startIndex, type.FullName.Length), StringComparison.Ordinal) == -1) return true; - var @namespace = nav.DataItem as Namespace; + var @namespace = nav.DataItem as NamespaceData; if (@namespace != null && helpUrl.IndexOf (@namespace.Name, startIndex, Math.Min (endIndex - startIndex, @namespace.Name.Length), StringComparison.Ordinal) == -1) return true; return false; @@ -646,20 +551,24 @@ namespace MonoDevelop.AssemblyBrowser void RenderDeclaringTypeOrNamespace (TreeViewColumn tree_column, CellRenderer cell, TreeModel tree_model, TreeIter iter) { var ct = (Gtk.CellRendererText)cell; - var entity = tree_model.GetValue (iter, 0) as IUnresolvedEntity; + var entity = tree_model.GetValue (iter, 0) as IMemberDefinition; if (entity != null) { - if (entity.DeclaringTypeDefinition != null) { - ct.Text = entity.DeclaringTypeDefinition.FullName; + if (entity.DeclaringType != null) { + ct.Text = entity.DeclaringType.FullName; return; } - ct.Text = entity.Namespace; + if (entity is TypeDefinition) { + ct.Text = ((TypeDefinition)entity).Namespace; + } else { + ct.Text = entity.DeclaringType.Namespace; + } } } void RenderText (TreeViewColumn tree_column, CellRenderer cell, TreeModel tree_model, TreeIter iter) { var ct = (Gtk.CellRendererText)cell; - var entity = tree_model.GetValue (iter, 0) as IUnresolvedEntity; + var entity = tree_model.GetValue (iter, 0) as IMemberDefinition; if (entity != null) ct.Text = entity.Name; } @@ -667,9 +576,35 @@ namespace MonoDevelop.AssemblyBrowser void RenderImage (TreeViewColumn tree_column, CellRenderer cell, TreeModel tree_model, TreeIter iter) { var ct = (CellRendererImage)cell; - var entity = tree_model.GetValue (iter, 0) as IUnresolvedEntity; - if (entity != null) - ct.Image = ImageService.GetIcon (entity.GetStockIcon (), Gtk.IconSize.Menu); + var evt = tree_model.GetValue (iter, 0) as EventDefinition; + if (evt != null) { + ct.Image = ImageService.GetIcon (EventDefinitionNodeBuilder.GetStockIcon (evt), Gtk.IconSize.Menu); + return; + } + + var field = tree_model.GetValue (iter, 0) as FieldDefinition; + if (field != null) { + ct.Image = ImageService.GetIcon (FieldDefinitionNodeBuilder.GetStockIcon (field), Gtk.IconSize.Menu); + return; + } + + var method = tree_model.GetValue (iter, 0) as MethodDefinition; + if (method != null) { + ct.Image = ImageService.GetIcon (MethodDefinitionNodeBuilder.GetStockIcon (method), Gtk.IconSize.Menu); + return; + } + + var property = tree_model.GetValue (iter, 0) as PropertyDefinition; + if (property != null) { + ct.Image = ImageService.GetIcon (PropertyDefinitionNodeBuilder.GetStockIcon (property), Gtk.IconSize.Menu); + return; + } + + var type = tree_model.GetValue (iter, 0) as TypeDefinition; + if (type != null) { + ct.Image = ImageService.GetIcon (TypeDefinitionNodeBuilder.GetStockIcon (type), Gtk.IconSize.Menu); + return; + } } CancellationTokenSource searchTokenSource = new CancellationTokenSource (); @@ -682,6 +617,8 @@ namespace MonoDevelop.AssemblyBrowser if (string.IsNullOrEmpty (query)) { notebook1.Page = 0; + IdeApp.Workbench.StatusBar.EndProgress (); + IdeApp.Workbench.StatusBar.ShowReady (); return; } @@ -694,135 +631,16 @@ namespace MonoDevelop.AssemblyBrowser case SearchMode.Type: IdeApp.Workbench.StatusBar.BeginProgress (GettextCatalog.GetString ("Searching type...")); break; - case SearchMode.TypeAndMembers: + case SearchMode.TypeAndMembers: IdeApp.Workbench.StatusBar.BeginProgress (GettextCatalog.GetString ("Searching types and members...")); break; } resultListStore.Clear (); var token = searchTokenSource.Token; - var publicOnly = PublicApiOnly; - var defArray = definitions.ToArray (); - Task.Run (delegate { - var memberList = SearchDoWork (defArray, query, publicOnly, token); - if (memberList == null || token.IsCancellationRequested) - return; - Runtime.RunInMainThread (delegate { - if (token.IsCancellationRequested) - return; - var updater = new Updater (this, memberList, token); - updater.Update (); - }); - }); + var updater = new SearchIdleRunner (this, query, token); + updater.Update (); } - class Updater - { - readonly AssemblyBrowserWidget assemblyBrowserWidget; - readonly List<IUnresolvedEntity> memberList; - readonly CancellationToken token; - int i = 0; - - public Updater (AssemblyBrowserWidget assemblyBrowserWidget, List<IUnresolvedEntity> memberList, CancellationToken token) - { - this.assemblyBrowserWidget = assemblyBrowserWidget; - this.memberList = memberList; - this.token = token; - } - - public void Update () - { - GLib.Idle.Add (IdleHandler); - } - - bool IdleHandler () - { - if (token.IsCancellationRequested || i >= memberList.Count) { - IdeApp.Workbench.StatusBar.EndProgress (); - IdeApp.Workbench.StatusBar.ShowReady (); - return false; - } - - assemblyBrowserWidget.searchTreeview.FreezeChildNotify (); - for (int j = 0; j < 100 && i < memberList.Count; j++) { - assemblyBrowserWidget.resultListStore.AppendValues (memberList [i++]); - } - assemblyBrowserWidget.searchTreeview.ThawChildNotify (); - return true; - } - } - - List<IUnresolvedEntity> SearchDoWork (AssemblyLoader[] definitions, string pattern, bool publicOnly, CancellationToken cancellationToken) - { - var result = new List<IUnresolvedEntity> (); - int types = 0, curType = 0; - foreach (var unit in definitions) { - types += unit.UnresolvedAssembly.TopLevelTypeDefinitions.Count (); - } - var matcher = StringMatcher.GetMatcher (pattern, true); - - switch (searchMode) { - case SearchMode.Member: - foreach (var unit in definitions) { - foreach (var type in unit.UnresolvedAssembly.TopLevelTypeDefinitions) { - if (cancellationToken.IsCancellationRequested) - return null; - if (!type.IsPublic && publicOnly) - continue; - curType++; - foreach (var member in type.Members) { - if (cancellationToken.IsCancellationRequested) - return null; - if (!member.IsPublic && publicOnly) - continue; - if (matcher.IsMatch (member.Name)) { - result.Add (member); - } - } - } - } - - break; - case SearchMode.Type: - foreach (var unit in definitions) { - var typeList = new List<IUnresolvedTypeDefinition> (); - foreach (var type in unit.UnresolvedAssembly.TopLevelTypeDefinitions) { - if (cancellationToken.IsCancellationRequested) - return null; - if (!type.IsPublic && publicOnly) - continue; - if (matcher.IsMatch (type.FullName)) - result.Add (type); - } - } - break; - case SearchMode.TypeAndMembers: - foreach (var unit in definitions) { - foreach (var type in unit.UnresolvedAssembly.TopLevelTypeDefinitions) { - if (cancellationToken.IsCancellationRequested) - return null; - if (!type.IsPublic && publicOnly) - continue; - var parent = type.FullName; - if (matcher.IsMatch (parent)) - result.Add (type); - - foreach (var member in type.Members) { - if (cancellationToken.IsCancellationRequested) - return null; - if (!member.IsPublic && publicOnly) - continue; - if (matcher.IsMatch (member.Name)) - result.Add (member); - } - - } - } - break; - } - - return result; - } - static bool preformat = false; internal static string FormatText (string text) { @@ -1069,7 +887,7 @@ namespace MonoDevelop.AssemblyBrowser if (request == LinkRequest.RequestNewView) { AssemblyBrowserViewContent assemblyBrowserView = new AssemblyBrowserViewContent (); foreach (var cu in definitions) { - assemblyBrowserView.Load (cu.UnresolvedAssembly.AssemblyName); + assemblyBrowserView.Load (cu.FileName); } IdeApp.Workbench.OpenDocument (assemblyBrowserView, true); ((AssemblyBrowserWidget)assemblyBrowserView.Control).Open (link); @@ -1083,7 +901,7 @@ namespace MonoDevelop.AssemblyBrowser } } - void FillInspectLabel () + void GenerateOutput () { ITreeNavigator nav = TreeView.GetSelectedNode (); if (nav == null) @@ -1118,50 +936,7 @@ namespace MonoDevelop.AssemblyBrowser break; } } - - void CreateOutput () - { - ITreeNavigator nav = TreeView.GetSelectedNode (); - - if (nav != null) { - IMember member = nav.DataItem as IMember; - string documentation = GettextCatalog.GetString ("No documentation available."); - if (member != null) { - try { - XmlNode node = member.GetMonodocDocumentation (); - if (node != null) { - documentation = TransformDocumentation (node) ?? documentation; - /* - StringWriter writer = new StringWriter (); - XmlTextWriter w = new XmlTextWriter (writer); - node.WriteTo (w); - System.Console.WriteLine ("---------------------------"); - System.Console.WriteLine (writer);*/ - - } - } catch (Exception) { - } - } -// this.documentationLabel.Markup = documentation; -/* IAssemblyBrowserNodeBuilder builder = nav.TypeNodeBuilder as IAssemblyBrowserNodeBuilder; - if (builder != null) { - this.descriptionLabel.Markup = builder.GetDescription (nav); - } else { - this.descriptionLabel.Markup = ""; - }*/ - - } - FillInspectLabel (); - } - - /*int oldSize = -1; - void VPaneExpose (object sender, Gtk.ExposeEventArgs args) - { - int size = this.vpaned1.Allocation.Height - 96; - if (size == oldSize) - return; - this.vpaned1.Position = oldSize = size; - }*/ + int oldSize2 = -1; void HPaneExpose (object sender, Gtk.ExposeEventArgs args) { @@ -1174,11 +949,11 @@ namespace MonoDevelop.AssemblyBrowser internal void Open (string url, AssemblyLoader currentAssembly = null, bool expandNode = true) { - Task.WhenAll (this.definitions.Select (d => d.LoadingTask).ToArray ()).ContinueWith (d => { + Task.WhenAll (this.definitions.Select (d => d.LoadingTask)).ContinueWith (d => { Application.Invoke ((o, args) => { suspendNavigation = false; ITreeNavigator nav = SearchMember (url, expandNode); - if (definitions == null) // we've been disposed + if (definitions.Count == 0) // we've been disposed return; if (nav != null) return; @@ -1215,11 +990,11 @@ namespace MonoDevelop.AssemblyBrowser loadNext (); return; } - var result = AddReferenceByFileName (fileName); + var result = AddReferenceByFileName (fileName, expandNode); if (result == null) return; result.LoadingTask.ContinueWith (t2 => { - if (definitions == null) // disposed + if (definitions.Count == 0) // disposed return; Application.Invoke ((o, args) => { var nav = SearchMember (url, expandNode); @@ -1237,10 +1012,10 @@ namespace MonoDevelop.AssemblyBrowser void OpenFromAssemblyNames (string url) { var tasks = new List<Task> (); - foreach (var definition in definitions.ToArray ()) { + foreach (var definition in definitions) { var cecilObject = definition.Assembly; if (cecilObject == null) { - LoggingService.LogWarning ("Assembly browser: Can't find assembly: " + definition.UnresolvedAssembly.FullAssemblyName + "."); + LoggingService.LogWarning ("Assembly browser: Can't find assembly: " + definition.Assembly.FullName + "."); continue; } foreach (var assemblyNameReference in cecilObject.MainModule.AssemblyReferences) { @@ -1270,7 +1045,7 @@ namespace MonoDevelop.AssemblyBrowser throw ex; } } - if (definitions == null) // disposed + if (definitions.Count == 0) // disposed return; Application.Invoke ((o, args) => { var nav = SearchMember (url); @@ -1348,11 +1123,10 @@ namespace MonoDevelop.AssemblyBrowser this.TreeView = null; } - if (definitions != null) { + if (definitions.Count > 0) { foreach (var def in definitions) def.Dispose (); - definitions.Clear (); - definitions = null; + definitions = definitions.Clear (); } ActiveMember = null; @@ -1380,17 +1154,17 @@ namespace MonoDevelop.AssemblyBrowser hpaned1.ExposeEvent -= HPaneExpose; base.OnDestroyed (); } - - List<AssemblyLoader> definitions = new List<AssemblyLoader> (); + + ImmutableList<AssemblyLoader> definitions = ImmutableList<AssemblyLoader>.Empty; List<Project> projects = new List<Project> (); - internal AssemblyLoader AddReferenceByAssemblyName (AssemblyNameReference reference) + internal AssemblyLoader AddReferenceByAssemblyName (AssemblyNameReference reference, bool expand = false) { - return AddReferenceByAssemblyName (reference.Name); + return AddReferenceByAssemblyName (reference.Name, expand, querySearch: false); } - internal AssemblyLoader AddReferenceByAssemblyName (string assemblyFullName) + internal AssemblyLoader AddReferenceByAssemblyName (string assemblyFullName, bool expand = false, bool querySearch = true) { string assemblyFile = Runtime.SystemAssemblyService.DefaultAssemblyContext.GetAssemblyLocation (assemblyFullName, null); if (assemblyFile == null || !System.IO.File.Exists (assemblyFile)) { @@ -1403,51 +1177,43 @@ namespace MonoDevelop.AssemblyBrowser if (assemblyFile == null || !System.IO.File.Exists (assemblyFile)) return null; - return AddReferenceByFileName (assemblyFile); + return AddReferenceByFileName (assemblyFile, expand, querySearch); } - - internal AssemblyLoader AddReferenceByFileName (string fileName) - { - var result = definitions.FirstOrDefault (d => d.FileName == fileName); - if (result != null) { -// // Select the result. -// if (selectReference) { -// ITreeNavigator navigator = TreeView.GetNodeAtObject (result); -// if (navigator != null) { -// navigator.Selected = true; -// } else { -// LoggingService.LogWarning (result + " could not be found."); -// } -// } + object assemblyLoadingLock = new object (); - return result; - } - if (!File.Exists (fileName)) - return null; - result = new AssemblyLoader (this, fileName); - - definitions.Add (result); - result.LoadingTask = result.LoadingTask.ContinueWith (task => { - Application.Invoke ((o, args) => { - if (definitions == null) - return; - try { - ITreeBuilder builder; - if (definitions.Count + projects.Count == 1) { - builder = TreeView.LoadTree (result); - } else { - builder = TreeView.AddChild (result); + internal AssemblyLoader AddReferenceByFileName (string fileName, bool expand = false, bool querySearch = true) + { + lock (assemblyLoadingLock) { + foreach (var def in definitions) { + if (FilePath.PathComparer.Equals (fileName, def.FileName)) + return def; + } + if (!File.Exists (fileName)) + return null; + var result = new AssemblyLoader (this, fileName); + definitions = definitions.Add (result); + result.LoadingTask = result.LoadingTask.ContinueWith (task => { + Application.Invoke ((o, args) => { + if (definitions.Count == 0) + return; + try { + ITreeBuilder builder; + if (definitions.Count + projects.Count == 1) { + builder = TreeView.LoadTree (result); + } else { + builder = TreeView.AddChild (result, false); + } + if (TreeView.GetSelectedNode () == null) + builder.Selected = builder.Expanded = expand; + } catch (Exception e) { + LoggingService.LogError ("Error while adding assembly to the assembly list", e); } - if (TreeView.GetSelectedNode () == null) - builder.Selected = builder.Expanded = true; - } catch (Exception e) { - LoggingService.LogError ("Error while adding assembly to the assembly list", e); - } - }); - return task.Result; + }); + return task.Result; + } + ); + return result; } - ); - return result; } public void AddProject (Project project, bool selectReference = true) @@ -1483,18 +1249,18 @@ namespace MonoDevelop.AssemblyBrowser void HandleCursorChanged (object sender, EventArgs e) { if (!suspendNavigation) { - var selectedEntity = TreeView.GetSelectedNode ()?.DataItem as IUnresolvedEntity; + var selectedEntity = TreeView.GetSelectedNode ()?.DataItem as IMemberDefinition; if (selectedEntity != null) NavigationHistoryService.LogActiveDocument (); } notebook1.Page = 0; - CreateOutput (); + GenerateOutput (); } public NavigationPoint BuildNavigationPoint () { var node = TreeView.GetSelectedNode (); - var selectedEntity = node?.DataItem as IUnresolvedEntity; + var selectedEntity = node?.DataItem as IMemberDefinition; AssemblyLoader loader = null; if (selectedEntity != null) { loader = (AssemblyLoader)this.TreeView.GetSelectedNode ().GetParentDataItem (typeof (AssemblyLoader), true); @@ -1507,20 +1273,22 @@ namespace MonoDevelop.AssemblyBrowser } #endregion - internal void EnsureDefinitionsLoaded (List<AssemblyLoader> definitions) + internal void EnsureDefinitionsLoaded (ImmutableList<AssemblyLoader> definitions) { if (definitions == null) throw new ArgumentNullException (nameof (definitions)); - foreach (var def in definitions) { - if (!this.definitions.Contains (def)) { - this.definitions.Add (def); - Application.Invoke ((o, args) => { - if (definitions.Count + projects.Count == 1) { - TreeView.LoadTree (def.LoadingTask.Result); - } else { - TreeView.AddChild (def.LoadingTask.Result); - } - }); + lock (assemblyLoadingLock) { + foreach (var def in definitions) { + if (!this.definitions.Contains (def)) { + this.definitions = this.definitions.Add (def); + Application.Invoke ((o, args) => { + if (definitions.Count + projects.Count == 1) { + TreeView.LoadTree (def.LoadingTask.Result); + } else { + TreeView.AddChild (def.LoadingTask.Result); + } + }); + } } } } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyLoader.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyLoader.cs index 4ee4fd011e..6d98d93da6 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyLoader.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyLoader.cs @@ -39,16 +39,16 @@ namespace MonoDevelop.AssemblyBrowser { readonly CancellationTokenSource src = new CancellationTokenSource (); readonly AssemblyBrowserWidget widget; - + public string FileName { get; private set; } - - Task<Tuple<AssemblyDefinition, IUnresolvedAssembly>> assemblyLoaderTask; + + Task<AssemblyDefinition> assemblyLoaderTask; TaskCompletionSource<AssemblyDefinition> assemblyDefinitionTaskSource; - public Task<Tuple<AssemblyDefinition, IUnresolvedAssembly>> LoadingTask { + public Task<AssemblyDefinition> LoadingTask { get { return assemblyLoaderTask; } @@ -60,35 +60,33 @@ namespace MonoDevelop.AssemblyBrowser public AssemblyDefinition Assembly => AssemblyTask.Result; public Task<AssemblyDefinition> AssemblyTask => assemblyDefinitionTaskSource.Task; - public IUnresolvedAssembly UnresolvedAssembly { + public ModuleDefinition ModuleDefinition { get { - return assemblyLoaderTask.Result.Item2; + return assemblyLoaderTask.Result.MainModule; } } CSharpDecompiler csharpDecompiler; - public CSharpDecompiler CSharpDecompiler - { + + public CSharpDecompiler CSharpDecompiler { get { if (csharpDecompiler == null) { - csharpDecompiler = new CSharpDecompiler(DecompilerTypeSystem, new ICSharpCode.Decompiler.DecompilerSettings()); + csharpDecompiler = new CSharpDecompiler (DecompilerTypeSystem, new ICSharpCode.Decompiler.DecompilerSettings ()); } return csharpDecompiler; } } - public DecompilerTypeSystem DecompilerTypeSystem { get; private set; } + DecompilerTypeSystem decompilerTypeSystem; + public DecompilerTypeSystem DecompilerTypeSystem { + get { + if (decompilerTypeSystem == null) { + decompilerTypeSystem = new DecompilerTypeSystem (Assembly.MainModule); - internal T GetCecilObject<T>(IUnresolvedEntity unresolvedEntity) - where T : IMemberDefinition - { - // this method has been made public in 3.0.0.3447 (see https://github.com/icsharpcode/ILSpy/issues/1028) - // TODO: get rid of reflection here once we migrate to that version - var getCecil = DecompilerTypeSystem.GetType().GetMethod("GetCecil", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); - var cecilObject = getCecil.Invoke(DecompilerTypeSystem, new object[] { unresolvedEntity }) as MemberReference; - var resolved = (T)cecilObject.Resolve(); - return resolved; + } + return decompilerTypeSystem; + } } public AssemblyLoader (AssemblyBrowserWidget widget, string fileName) @@ -102,19 +100,16 @@ namespace MonoDevelop.AssemblyBrowser if (!File.Exists (fileName)) throw new ArgumentException ("File doesn't exist.", nameof (fileName)); - assemblyDefinitionTaskSource = new TaskCompletionSource<AssemblyDefinition>(); + assemblyDefinitionTaskSource = new TaskCompletionSource<AssemblyDefinition> (); - assemblyLoaderTask = Task.Run ( () => { + assemblyLoaderTask = Task.Run (() => { try { var assemblyDefinition = AssemblyDefinition.ReadAssembly (FileName, new ReaderParameters { AssemblyResolver = this }); - assemblyDefinitionTaskSource.SetResult(assemblyDefinition); - DecompilerTypeSystem = new DecompilerTypeSystem(assemblyDefinition.MainModule); - var loadedAssembly = DecompilerTypeSystem.MainAssembly.UnresolvedAssembly; - return Tuple.Create(assemblyDefinition, loadedAssembly); - } - catch (Exception e) { + assemblyDefinitionTaskSource.SetResult (assemblyDefinition); + return assemblyDefinition; + } catch (Exception e) { LoggingService.LogError ("Error while reading assembly " + FileName, e); return null; } @@ -159,23 +154,23 @@ namespace MonoDevelop.AssemblyBrowser var loader = widget.AddReferenceByAssemblyName (name); return loader != null ? loader.Assembly : null; } - + AssemblyDefinition IAssemblyResolver.Resolve (AssemblyNameReference name, ReaderParameters parameters) { var loader = widget.AddReferenceByAssemblyName (name); return loader != null ? loader.Assembly : null; } #endregion - + public string LookupAssembly (string fullAssemblyName) { var assemblyFile = Runtime.SystemAssemblyService.DefaultAssemblyContext.GetAssemblyLocation (fullAssemblyName, null); if (assemblyFile != null && File.Exists (assemblyFile)) return assemblyFile; - + var name = AssemblyNameReference.Parse (fullAssemblyName); var path = Path.GetDirectoryName (FileName); - + var dll = Path.Combine (path, name.Name + ".dll"); if (File.Exists (dll)) return dll; diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/BaseTypeFolder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/BaseTypeFolder.cs index 6b6436be7e..c036cb19d3 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/BaseTypeFolder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/BaseTypeFolder.cs @@ -34,12 +34,12 @@ namespace MonoDevelop.AssemblyBrowser { class BaseTypeFolder : IDisposable { - public IUnresolvedTypeDefinition Type { + public TypeDefinition Type { get; private set; } - public BaseTypeFolder (IUnresolvedTypeDefinition type) + public BaseTypeFolder (TypeDefinition type) { this.Type = type; } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/HelpExtensions.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/HelpExtensions.cs index 0c41660f8f..0a8bd41522 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/HelpExtensions.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/HelpExtensions.cs @@ -84,24 +84,6 @@ namespace MonoDevelop.AssemblyBrowser result.Append (')'); } - static void AppendHelpParameterList (StringBuilder result, IList<IUnresolvedParameter> parameters) - { - result.Append ('('); - if (parameters != null) { - for (int i = 0; i < parameters.Count; i++) { - if (i > 0) - result.Append (','); - var p = parameters [i]; - if (p == null) - continue; - if (p.IsRef || p.IsOut) - result.Append ("&"); - AppendTypeReference (result, p.Type); - } - } - result.Append (')'); - } - static XmlNode FindMatch (IMethod method, XmlNodeList nodes) { foreach (XmlNode node in nodes) { diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/IAssemblyBrowserNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/IAssemblyBrowserNodeBuilder.cs index 32da37dbec..c897e4ea03 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/IAssemblyBrowserNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/IAssemblyBrowserNodeBuilder.cs @@ -36,7 +36,6 @@ namespace MonoDevelop.AssemblyBrowser { interface IAssemblyBrowserNodeBuilder { - string GetDocumentationMarkup (ITreeNavigator navigator); List<ReferenceSegment> Disassemble (TextEditor data, ITreeNavigator navigator); List<ReferenceSegment> Decompile (TextEditor data, ITreeNavigator navigator, DecompileFlags flags); } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/NRefactoryStock.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/NRefactoryStock.cs deleted file mode 100644 index 2366f0a5d8..0000000000 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/NRefactoryStock.cs +++ /dev/null @@ -1,232 +0,0 @@ -// -// NRefactoryStock.cs -// -// Author: -// Mike Krüger <mkrueger@xamarin.com> -// -// Copyright (c) 2015 Xamarin Inc. (http://xamarin.com) -// -// 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 MonoDevelop.Core; -using ICSharpCode.Decompiler.TypeSystem; - -namespace MonoDevelop.AssemblyBrowser -{ - static class NRefactoryStock - { - static readonly IconId Field = "md-field"; - public static readonly IconId Namespace = "md-name-space"; - - public static IconId GetStockIcon (this INamedElement element) - { - if (element is IType) - return ((IType)element).GetStockIcon (); - if (element is ITypeParameter) - return ((ITypeParameter)element).GetStockIcon (); - if (element is IUnresolvedEntity) - return ((IUnresolvedEntity)element).GetStockIcon (); - return ((IEntity)element).GetStockIcon (); - } - - public static IconId GetStockIcon (this ITypeDefinition entity) - { - return GetStockIcon ((IType)entity); - } - - public static IconId GetStockIcon (this IField field) - { - return GetStockIcon ((IEntity)field); - } - - public static IconId GetStockIcon (this IVariable variable) - { - return Field; - } - - public static IconId GetStockIcon (this IParameter parameter) - { - return Field; - } - - public static IconId GetStockIcon (this IUnresolvedTypeParameter parameter) - { - return Field; - } - - public static IconId GetStockIcon (this IEntity entity, bool showAccessibility = true) - { - return "md-" + GetAccess (entity) + GetGlobal (entity) + GetSource (entity); - } - - static string GetAccess (IEntity entity) - { - switch (entity.Accessibility) { - case Accessibility.None: - return ""; - case Accessibility.Private: - return "private-"; - case Accessibility.Public: - return ""; - case Accessibility.Protected: - return "protected-"; - case Accessibility.Internal: - return "internal-"; - case Accessibility.ProtectedOrInternal: - case Accessibility.ProtectedAndInternal: - return "ProtectedOrInternal-"; - default: - throw new ArgumentOutOfRangeException (); - } - } - - static string GetGlobal (IEntity entity) - { - switch (entity.SymbolKind) { - case SymbolKind.Field: - if (((IField)entity).IsConst) - return ""; - return entity.IsStatic ? "static-" : ""; - case SymbolKind.Method: - case SymbolKind.Constructor: - case SymbolKind.Destructor: - case SymbolKind.Operator: - case SymbolKind.Property: - case SymbolKind.Indexer: - return entity.IsStatic ? "static-" : ""; - } - return ""; - - } - - static string GetSource (IEntity entity) - { - switch (entity.SymbolKind) { - case SymbolKind.TypeDefinition: - var type = (IType)entity; - switch (type.Kind) { - case TypeKind.Class: - return "class"; - case TypeKind.Interface: - return "interface"; - case TypeKind.Struct: - return "struct"; - case TypeKind.Delegate: - return "delegate"; - case TypeKind.Enum: - return "enum"; - } - return "class"; - case SymbolKind.Field: - if (((IField)entity).IsConst) - return "literal"; - return "field"; - case SymbolKind.Event: - return "event"; - case SymbolKind.Method: - case SymbolKind.Constructor: - case SymbolKind.Destructor: - case SymbolKind.Operator: - return "method"; - case SymbolKind.Property: - case SymbolKind.Indexer: - return "property"; - } - return ""; - } - - public static IconId GetStockIcon (this IUnresolvedEntity entity, bool showAccessibility = true) - { - return "md-" + GetAccess (entity) + GetGlobal (entity) + GetSource (entity); - } - - static string GetAccess (IUnresolvedEntity entity) - { - switch (entity.Accessibility) { - case Accessibility.None: - return ""; - case Accessibility.Private: - return "private-"; - case Accessibility.Public: - return ""; - case Accessibility.Protected: - return "protected-"; - case Accessibility.Internal: - return "internal-"; - case Accessibility.ProtectedOrInternal: - case Accessibility.ProtectedAndInternal: - return "ProtectedOrInternal-"; - default: - throw new ArgumentOutOfRangeException (); - } - } - - static string GetGlobal (IUnresolvedEntity entity) - { - switch (entity.SymbolKind) { - case SymbolKind.Field: - var field = (IUnresolvedField)entity; - return field.IsStatic && !field.IsConst ? "static-" : ""; - case SymbolKind.Method: - case SymbolKind.Constructor: - case SymbolKind.Destructor: - case SymbolKind.Operator: - case SymbolKind.Property: - case SymbolKind.Indexer: - return entity.IsStatic ? "static-" : ""; - } - return ""; - } - - static string GetSource (IUnresolvedEntity entity) - { - switch (entity.SymbolKind) { - case SymbolKind.TypeDefinition: - var type = (IUnresolvedTypeDefinition)entity; - switch (type.Kind) { - case TypeKind.Class: - return "class"; - case TypeKind.Interface: - return "interface"; - case TypeKind.Struct: - return "struct"; - case TypeKind.Delegate: - return "delegate"; - case TypeKind.Enum: - return "enum"; - } - return "class"; - case SymbolKind.Field: - var field = (IUnresolvedField)entity; - return field.IsConst ? "literal" : "field"; - case SymbolKind.Event: - return "event"; - case SymbolKind.Method: - case SymbolKind.Constructor: - case SymbolKind.Destructor: - case SymbolKind.Operator: - return "method"; - case SymbolKind.Property: - case SymbolKind.Indexer: - return "property"; - } - return ""; - } - } -}
\ No newline at end of file diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/AssemblyBrowserTypeNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/AssemblyBrowserTypeNodeBuilder.cs index 1c5611b9cd..a116fa5de3 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/AssemblyBrowserTypeNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/AssemblyBrowserTypeNodeBuilder.cs @@ -60,8 +60,8 @@ namespace MonoDevelop.AssemblyBrowser try { if (thisNode == null || otherNode == null) return -1; - var e1 = thisNode.DataItem as IUnresolvedEntity; - var e2 = otherNode.DataItem as IUnresolvedEntity; + var e1 = thisNode.DataItem as IMemberDefinition; + var e2 = otherNode.DataItem as IMemberDefinition; if (e1 == null && e2 == null) return 0; @@ -70,9 +70,6 @@ namespace MonoDevelop.AssemblyBrowser if (e2 == null) return 1; - if (e1.SymbolKind != e2.SymbolKind) - return e2.SymbolKind.CompareTo (e1.SymbolKind); - return e1.Name.CompareTo (e2.Name); } catch (Exception e) { LoggingService.LogError ("Exception in assembly browser sort function.", e); @@ -90,46 +87,5 @@ namespace MonoDevelop.AssemblyBrowser return treeBuilder.GetParentDataItem (typeof(AssemblyLoader), true) != null; } - protected IUnresolvedAssembly GetMainAssembly (ITreeNavigator treeBuilder) - { - var loader = (AssemblyLoader)treeBuilder.GetParentDataItem (typeof(AssemblyLoader), true); - if (loader != null) - return loader.UnresolvedAssembly; - return null; - } - - protected ITypeResolveContext GetContext (ITreeNavigator treeBuilder) - { - var mainAssembly = GetMainAssembly (treeBuilder); - if (mainAssembly != null) { - var simpleCompilation = new SimpleCompilation (mainAssembly); - return new SimpleTypeResolveContext (simpleCompilation.MainAssembly); - } - // TODO: roslyn port ? - // var project = (Project)treeBuilder.GetParentDataItem (typeof(Project), true); - // var compilation = TypeSystemService.GetCompilation (project); - // return new SimpleTypeResolveContext (compilation.MainAssembly); - return null; - } - - protected IMember Resolve (ITreeNavigator treeBuilder, IUnresolvedMember member, ITypeDefinition currentType = null) - { - var ctx = GetContext (treeBuilder); - return member.CreateResolved (currentType != null ? ctx.WithCurrentTypeDefinition (currentType) : ctx); - } - - protected IType Resolve (ITreeNavigator treeBuilder, IUnresolvedTypeDefinition type) - { - var mainAssembly = GetMainAssembly (treeBuilder); - if (mainAssembly != null) { - var simpleCompilation = new SimpleCompilation (mainAssembly); - return type.Resolve (new SimpleTypeResolveContext (simpleCompilation.MainAssembly)); - } - // TODO: roslyn port ? - // var project = (Project)treeBuilder.GetParentDataItem (typeof(Project), true); - // var ctx = TypeSystemService.GetCompilation (project); - // return ctx.MainAssembly.GetTypeDefinition (type.Namespace, type.Name, type.TypeParameters.Count); - return null; - } } } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/BaseTypeFolderNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/BaseTypeFolderNodeBuilder.cs index 6e5e49121a..e0eeb9f581 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/BaseTypeFolderNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/BaseTypeFolderNodeBuilder.cs @@ -100,7 +100,8 @@ namespace MonoDevelop.AssemblyBrowser public override void BuildChildNodes (ITreeBuilder builder, object dataObject) { var baseTypeFolder = (BaseTypeFolder)dataObject; - builder.AddChildren (baseTypeFolder.Type.BaseTypes); + builder.AddChild (baseTypeFolder.Type.BaseType); + builder.AddChildren (baseTypeFolder.Type.Interfaces); } public override bool HasChildNodes (ITreeBuilder builder, object dataObject) { diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/AssemblyNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/AssemblyNodeBuilder.cs index 844821a7ba..34a1364cac 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/AssemblyNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/AssemblyNodeBuilder.cs @@ -38,6 +38,7 @@ using MonoDevelop.Ide.Gui.Components; using System.Collections.Generic; using System.IO; using MonoDevelop.Ide.Editor; +using ICSharpCode.Decompiler.TypeSystem; namespace MonoDevelop.AssemblyBrowser { @@ -78,22 +79,22 @@ namespace MonoDevelop.AssemblyBrowser if (resources.Resources.Any ()) treeBuilder.AddChild (resources); - var namespaces = new Dictionary<string, Namespace> (); + var namespaces = new Dictionary<string, NamespaceData> (); bool publicOnly = Widget.PublicApiOnly; - foreach (var type in compilationUnit.UnresolvedAssembly.TopLevelTypeDefinitions) { + foreach (var type in compilationUnit.ModuleDefinition.Types) { string namespaceName = string.IsNullOrEmpty (type.Namespace) ? "" : type.Namespace; if (!namespaces.ContainsKey (namespaceName)) - namespaces [namespaceName] = new Namespace (namespaceName); + namespaces [namespaceName] = new NamespaceData (namespaceName); var ns = namespaces [namespaceName]; - ns.Types.Add (type); + ns.Types.Add ((type.IsPublic, type)); } - treeBuilder.AddChildren (namespaces.Where (ns => ns.Key != "" && (!publicOnly || ns.Value.Types.Any (t => t.IsPublic))).Select (n => n.Value)); + treeBuilder.AddChildren (namespaces.Where (ns => ns.Key != "" && (!publicOnly || ns.Value.Types.Any (t => t.isPublic))).Select (n => n.Value)); if (namespaces.ContainsKey ("")) { foreach (var child in namespaces [""].Types) { - if (child.Name == "<Module>") + if (((TypeDefinition)child.typeObject).Name == "<Module>") continue; treeBuilder.AddChild (child); } @@ -156,27 +157,22 @@ namespace MonoDevelop.AssemblyBrowser public List<ReferenceSegment> Disassemble (TextEditor data, ITreeNavigator navigator) { var assemblyLoader = (AssemblyLoader)navigator.DataItem; - var assembly = assemblyLoader.UnresolvedAssembly; var compilationUnit = assemblyLoader.Assembly; if (compilationUnit == null) { - LoggingService.LogError ("Can't get cecil object for assembly:" + assembly); + LoggingService.LogError ("Can't get cecil object for assembly:" + assemblyLoader.Assembly.FullName); return new List<ReferenceSegment> (); } - return DomMethodNodeBuilder.Disassemble (data, rd => rd.WriteAssemblyHeader (compilationUnit)); + return MethodDefinitionNodeBuilder.Disassemble (data, rd => rd.WriteAssemblyHeader (compilationUnit)); } public List<ReferenceSegment> Decompile (TextEditor data, ITreeNavigator navigator, DecompileFlags flags) { var assemblyLoader = (AssemblyLoader)navigator.DataItem; - return DomMethodNodeBuilder.Decompile (data, DomMethodNodeBuilder.GetAssemblyLoader (navigator), b => + return MethodDefinitionNodeBuilder.Decompile (data, MethodDefinitionNodeBuilder.GetAssemblyLoader (navigator), b => b.DecompileModuleAndAssemblyAttributes(), flags: flags); } - public string GetDocumentationMarkup (ITreeNavigator navigator) - { - return null; - } #endregion } } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/DomEventNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/EventDefinitionNodeBuilder.cs index 93a1b36645..735559a4eb 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/DomEventNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/EventDefinitionNodeBuilder.cs @@ -38,48 +38,55 @@ using Mono.Cecil; using MonoDevelop.Ide.TypeSystem; using ICSharpCode.Decompiler.TypeSystem; using MonoDevelop.Ide.Editor; +using ICSharpCode.Decompiler.CSharp; +using ICSharpCode.ILSpy; +using MonoDevelop.Core; namespace MonoDevelop.AssemblyBrowser { - class DomEventNodeBuilder : AssemblyBrowserTypeNodeBuilder, IAssemblyBrowserNodeBuilder + class EventDefinitionNodeBuilder : AssemblyBrowserTypeNodeBuilder, IAssemblyBrowserNodeBuilder { public override Type NodeDataType { - get { return typeof(IUnresolvedEvent); } + get { return typeof(EventDefinition); } } - public DomEventNodeBuilder (AssemblyBrowserWidget widget) : base (widget) + public EventDefinitionNodeBuilder (AssemblyBrowserWidget widget) : base (widget) { } public override string GetNodeName (ITreeNavigator thisNode, object dataObject) { - var evt = (IUnresolvedEvent)dataObject; + var evt = (EventDefinition)dataObject; return evt.Name; } public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) { - var evt = (IUnresolvedEvent)dataObject; - try { - var resolved = Resolve (treeBuilder, evt); - nodeInfo.Label = MonoDevelop.Ide.TypeSystem.Ambience.EscapeText (Ambience.ConvertSymbol (resolved)); - } catch (Exception) { - nodeInfo.Label = evt.Name; - } - if (evt.IsPrivate || evt.IsInternal) - nodeInfo.Label = DomMethodNodeBuilder.FormatPrivate (nodeInfo.Label); - nodeInfo.Icon = Context.GetIcon (evt.GetStockIcon ()); + var evt = (EventDefinition)dataObject; + nodeInfo.Label = evt.Name + " : " + CSharpLanguage.Instance.TypeToString (evt.EventType, false, evt); + var accessor = evt.AddMethod ?? evt.RemoveMethod; + + if (((MethodAttributes.Private | MethodAttributes.Assembly) & accessor.Attributes) != 0) + nodeInfo.Label = MethodDefinitionNodeBuilder.FormatPrivate (nodeInfo.Label); + + nodeInfo.Icon = Context.GetIcon (GetStockIcon (evt)); } - + + public static IconId GetStockIcon (EventDefinition evt) + { + var accessor = evt.AddMethod ?? evt.RemoveMethod; + return MethodDefinitionNodeBuilder.GetStockIcon (accessor); + } + public override void BuildChildNodes (ITreeBuilder ctx, object dataObject) { - var evt = (IUnresolvedEvent)dataObject; - if (evt.CanAdd) - ctx.AddChild (evt.AddAccessor); - if (evt.CanRemove) - ctx.AddChild (evt.RemoveAccessor); - if (evt.CanInvoke) - ctx.AddChild (evt.InvokeAccessor); + var evt = (EventDefinition)dataObject; + if (evt.AddMethod != null) + ctx.AddChild (evt.AddMethod); + if (evt.RemoveMethod != null) + ctx.AddChild (evt.RemoveMethod); + if (evt.InvokeMethod != null) + ctx.AddChild (evt.InvokeMethod); } public override bool HasChildNodes (ITreeBuilder builder, object dataObject) @@ -90,37 +97,22 @@ namespace MonoDevelop.AssemblyBrowser #region IAssemblyBrowserNodeBuilder List<ReferenceSegment> IAssemblyBrowserNodeBuilder.Disassemble (TextEditor data, ITreeNavigator navigator) { - if (DomMethodNodeBuilder.HandleSourceCodeEntity (navigator, data)) + if (MethodDefinitionNodeBuilder.HandleSourceCodeEntity (navigator, data)) return null; - var evt = GetCecilLoader (navigator).GetCecilObject <EventDefinition> ((IUnresolvedEvent)navigator.DataItem); - return DomMethodNodeBuilder.Disassemble (data, rd => rd.DisassembleEvent (evt)); + var evt = (EventDefinition)navigator.DataItem; + return MethodDefinitionNodeBuilder.Disassemble (data, rd => rd.DisassembleEvent (evt)); } List<ReferenceSegment> IAssemblyBrowserNodeBuilder.Decompile (TextEditor data, ITreeNavigator navigator, DecompileFlags flags) { - if (DomMethodNodeBuilder.HandleSourceCodeEntity (navigator, data)) + if (MethodDefinitionNodeBuilder.HandleSourceCodeEntity (navigator, data)) return null; - var evt = GetCecilLoader (navigator).GetCecilObject <EventDefinition>((IUnresolvedEvent)navigator.DataItem); + var evt = navigator.DataItem as EventDefinition; if (evt == null) return null; - return DomMethodNodeBuilder.Decompile (data, DomMethodNodeBuilder.GetAssemblyLoader (navigator), b => b.Decompile (evt), flags: flags); + return MethodDefinitionNodeBuilder.Decompile (data, MethodDefinitionNodeBuilder.GetAssemblyLoader (navigator), b => b.Decompile (evt), flags: flags); } - string IAssemblyBrowserNodeBuilder.GetDocumentationMarkup (ITreeNavigator navigator) - { - var evt = (IUnresolvedEvent)navigator.DataItem; - var resolved = Resolve (navigator, evt); - StringBuilder result = new StringBuilder (); - result.Append ("<big>"); - result.Append (MonoDevelop.Ide.TypeSystem.Ambience.EscapeText (Ambience.ConvertSymbol (resolved))); - result.Append ("</big>"); - result.AppendLine (); - result.AppendLine (); - - //result.Append (AmbienceService.GetDocumentationMarkup (resolved, AmbienceService.GetDocumentation (resolved), options)); - - return result.ToString (); - } #endregion } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/DomFieldNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/FieldDefinitionNodeBuilder.cs index 4cba85a574..b66b28cfc4 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/DomFieldNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/FieldDefinitionNodeBuilder.cs @@ -39,77 +39,87 @@ using Mono.Cecil; using MonoDevelop.Ide.TypeSystem; using ICSharpCode.Decompiler.TypeSystem; using MonoDevelop.Ide.Editor; +using ICSharpCode.ILSpy; +using MonoDevelop.Core; namespace MonoDevelop.AssemblyBrowser { - class DomFieldNodeBuilder : AssemblyBrowserTypeNodeBuilder, IAssemblyBrowserNodeBuilder + class FieldDefinitionNodeBuilder : AssemblyBrowserTypeNodeBuilder, IAssemblyBrowserNodeBuilder { public override Type NodeDataType { - get { return typeof(IUnresolvedField); } + get { return typeof(FieldDefinition); } } - public DomFieldNodeBuilder (AssemblyBrowserWidget widget) : base (widget) + public FieldDefinitionNodeBuilder (AssemblyBrowserWidget widget) : base (widget) { } public override string GetNodeName (ITreeNavigator thisNode, object dataObject) { - var field = (IUnresolvedField)dataObject; + var field = (FieldDefinition)dataObject; return field.Name; } public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) { - var field = (IUnresolvedField)dataObject; - try { - var resolved = Resolve (treeBuilder, field); - nodeInfo.Label = MonoDevelop.Ide.TypeSystem.Ambience.EscapeText (Ambience.ConvertSymbol (resolved)); - } catch (Exception) { - nodeInfo.Label = field.Name; - } + var field = (FieldDefinition)dataObject; + nodeInfo.Label = field.Name + " : " + CSharpLanguage.Instance.TypeToString (field.FieldType, false, field); - if (field.IsPrivate || field.IsInternal) - nodeInfo.Label = DomMethodNodeBuilder.FormatPrivate (nodeInfo.Label); - nodeInfo.Icon = Context.GetIcon (field.GetStockIcon ()); + if (((FieldAttributes.Private | FieldAttributes.Assembly) & field.Attributes) != 0) + nodeInfo.Label = MethodDefinitionNodeBuilder.FormatPrivate (nodeInfo.Label); + nodeInfo.Icon = Context.GetIcon (GetStockIcon(field)); } - + + public static IconId GetStockIcon (FieldDefinition field) + { + var isStatic = (field.Attributes & FieldAttributes.Static) != 0; + var source = field.HasConstant ? "literal" : "field"; + var global = field.HasConstant ? "" : (isStatic ? "static-" : ""); + return "md-" + GetAccess (field.Attributes) + global + source; + } + + static string GetAccess (FieldAttributes attributes) + { + switch (attributes & FieldAttributes.FieldAccessMask) { + case FieldAttributes.Private: + return "private-"; + case FieldAttributes.Public: + return ""; + case FieldAttributes.Family: + return "protected-"; + case FieldAttributes.Assembly: + return "internal-"; + case FieldAttributes.FamORAssem: + case FieldAttributes.FamANDAssem: + return "ProtectedOrInternal-"; + default: + return ""; + } + } + #region IAssemblyBrowserNodeBuilder List<ReferenceSegment> IAssemblyBrowserNodeBuilder.Disassemble (TextEditor data, ITreeNavigator navigator) { - if (DomMethodNodeBuilder.HandleSourceCodeEntity (navigator, data)) + if (MethodDefinitionNodeBuilder.HandleSourceCodeEntity (navigator, data)) return null; - var field = GetCecilLoader (navigator).GetCecilObject <FieldDefinition>((IUnresolvedField)navigator.DataItem); + var field = (FieldDefinition)navigator.DataItem; if (field == null) return null; - return DomMethodNodeBuilder.Disassemble (data, rd => rd.DisassembleField (field)); + return MethodDefinitionNodeBuilder.Disassemble (data, rd => rd.DisassembleField (field)); } List<ReferenceSegment> IAssemblyBrowserNodeBuilder.Decompile (TextEditor data, ITreeNavigator navigator, DecompileFlags flags) { - if (DomMethodNodeBuilder.HandleSourceCodeEntity (navigator, data)) + if (MethodDefinitionNodeBuilder.HandleSourceCodeEntity (navigator, data)) return null; - var field = GetCecilLoader (navigator).GetCecilObject <FieldDefinition>((IUnresolvedField)navigator.DataItem); + var field = (FieldDefinition)navigator.DataItem; if (field == null) return null; - return DomMethodNodeBuilder.Decompile (data, DomMethodNodeBuilder.GetAssemblyLoader (navigator), b => b.Decompile (field), flags: flags); + return MethodDefinitionNodeBuilder.Decompile (data, MethodDefinitionNodeBuilder.GetAssemblyLoader (navigator), b => b.Decompile (field), flags: flags); } - string IAssemblyBrowserNodeBuilder.GetDocumentationMarkup (ITreeNavigator navigator) - { - var field = (IUnresolvedField)navigator.DataItem; - var resolved = Resolve (navigator, field); - StringBuilder result = new StringBuilder (); - result.Append ("<big>"); - result.Append (MonoDevelop.Ide.TypeSystem.Ambience.EscapeText (Ambience.ConvertSymbol (resolved))); - result.Append ("</big>"); - result.AppendLine (); - - //result.Append (AmbienceService.GetDocumentationMarkup (resolved, AmbienceService.GetDocumentation (resolved), options)); - - return result.ToString (); - } #endregion } } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/DomMethodNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/MethodDefinitionNodeBuilder.cs index 9a182e1899..ed03941295 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/DomMethodNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/MethodDefinitionNodeBuilder.cs @@ -46,25 +46,25 @@ using MonoDevelop.Core.Text; using MonoDevelop.Ide; using MonoDevelop.Ide.Editor; using MonoDevelop.Ide.Gui.Components; +using ICSharpCode.ILSpy; namespace MonoDevelop.AssemblyBrowser { - class DomMethodNodeBuilder : AssemblyBrowserTypeNodeBuilder, IAssemblyBrowserNodeBuilder + class MethodDefinitionNodeBuilder : AssemblyBrowserTypeNodeBuilder, IAssemblyBrowserNodeBuilder { public override Type NodeDataType { - get { return typeof(IUnresolvedMethod); } + get { return typeof(MethodDefinition); } } - public DomMethodNodeBuilder (AssemblyBrowserWidget widget) : base (widget) + public MethodDefinitionNodeBuilder (AssemblyBrowserWidget widget) : base (widget) { - } public override string GetNodeName (ITreeNavigator thisNode, object dataObject) { - var method = (IUnresolvedMethod)dataObject; - if (method.SymbolKind == SymbolKind.Constructor || method.SymbolKind == SymbolKind.Destructor) - return method.DeclaringTypeDefinition.Name; + var method = (MethodDefinition)dataObject; + if (method.IsConstructor) + return method.DeclaringType.Name; return method.Name; } @@ -72,24 +72,77 @@ namespace MonoDevelop.AssemblyBrowser { return "<span foreground= \"#666666\">" + label + "</span>"; } + public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) { - var method = (IUnresolvedMethod)dataObject; - var dt = new DefaultResolvedTypeDefinition (GetContext (treeBuilder), method.DeclaringTypeDefinition); - var resolved = (DefaultResolvedMethod)Resolve (treeBuilder, method, dt); + var method = (MethodDefinition)dataObject; + var ambience = new CSharpAmbience (); try { - nodeInfo.Label = MonoDevelop.Ide.TypeSystem.Ambience.EscapeText (ambience.ConvertSymbol (resolved)); + nodeInfo.Label = MonoDevelop.Ide.TypeSystem.Ambience.EscapeText (GetText (method)); } catch (Exception) { nodeInfo.Label = method.Name; } - if (method.IsPrivate || method.IsInternal) - nodeInfo.Label = DomMethodNodeBuilder.FormatPrivate (nodeInfo.Label); + if (method.IsPrivate || method.IsAssembly) + nodeInfo.Label = MethodDefinitionNodeBuilder.FormatPrivate (nodeInfo.Label); - nodeInfo.Icon = Context.GetIcon (resolved.GetStockIcon ()); + nodeInfo.Icon = Context.GetIcon (GetStockIcon (method)); } - + + public static IconId GetStockIcon (MethodDefinition method) + { + var isStatic = (method.Attributes & MethodAttributes.Static) != 0; + var global = isStatic ? "static-" : ""; + return "md-" + GetAccess (method.Attributes) + global + "method"; + } + + static string GetAccess (MethodAttributes attributes) + { + switch (attributes & MethodAttributes.MemberAccessMask) { + case MethodAttributes.Private: + return "private-"; + case MethodAttributes.Public: + return ""; + case MethodAttributes.Family: + return "protected-"; + case MethodAttributes.Assembly: + return "internal-"; + case MethodAttributes.FamORAssem: + case MethodAttributes.FamANDAssem: + return "ProtectedOrInternal-"; + default: + return ""; + } + } + + public static string GetText (MethodDefinition method) + { + var b = StringBuilderCache.Allocate (); + try { + b.Append ('('); + for (int i = 0; i < method.Parameters.Count; i++) { + if (i > 0) + b.Append (", "); + b.Append (CSharpLanguage.Instance.TypeToString (method.Parameters [i].ParameterType, false, method.Parameters [i])); + } + if (method.CallingConvention == MethodCallingConvention.VarArg) { + if (method.HasParameters) + b.Append (", "); + b.Append ("..."); + } + if (method.IsConstructor) { + b.Append (')'); + } else { + b.Append (") : "); + b.Append (CSharpLanguage.Instance.TypeToString (method.ReturnType, false, method.MethodReturnType)); + } + return CSharpLanguage.Instance.FormatMethodName (method) + b; + } finally { + StringBuilderCache.Free (b); + } + } + #region IAssemblyBrowserNodeBuilder internal static void PrintDeclaringType (StringBuilder result, ITreeNavigator navigator) { @@ -121,7 +174,7 @@ namespace MonoDevelop.AssemblyBrowser { var types = DesktopService.GetMimeTypeInheritanceChain (data.MimeType); var codePolicy = MonoDevelop.Projects.Policies.PolicyService.GetDefaultPolicy<MonoDevelop.CSharp.Formatting.CSharpFormattingPolicy> (types); - var settings = DomTypeNodeBuilder.CreateDecompilerSettings (publicOnly, codePolicy); + var settings = TypeDefinitionNodeBuilder.CreateDecompilerSettings (publicOnly, codePolicy); return settings; } @@ -168,13 +221,12 @@ namespace MonoDevelop.AssemblyBrowser public List<ReferenceSegment> Decompile (TextEditor data, ITreeNavigator navigator, DecompileFlags flags) { - var method = (IUnresolvedMethod)navigator.DataItem; if (HandleSourceCodeEntity (navigator, data)) return null; - var cecilMethod = GetCecilLoader (navigator).GetCecilObject<MethodDefinition> (method); + var cecilMethod = (MethodDefinition)navigator.DataItem; if (cecilMethod == null) return null; - return DomMethodNodeBuilder.Decompile (data, DomMethodNodeBuilder.GetAssemblyLoader (navigator), b => b.Decompile (cecilMethod), flags: flags); + return MethodDefinitionNodeBuilder.Decompile (data, MethodDefinitionNodeBuilder.GetAssemblyLoader (navigator), b => b.Decompile (cecilMethod), flags: flags); } static void AppendLink (StringBuilder sb, string link, string text) @@ -198,45 +250,27 @@ namespace MonoDevelop.AssemblyBrowser internal static bool HandleSourceCodeEntity (ITreeNavigator navigator, TextEditor data) { - if (IsFromAssembly (navigator)) - return false; - - var method = (IUnresolvedEntity)navigator.DataItem; - var source = StringTextSource.ReadFrom (method.Region.FileName); - data.Text = source.Text; - data.CaretLocation = new MonoDevelop.Ide.Editor.DocumentLocation (method.Region.BeginLine, method.Region.BeginColumn); - return true; + /* if (IsFromAssembly (navigator)) + return false; + + var method = (MethodDefinition)navigator.DataItem; + var source = StringTextSource.ReadFrom (method.Region.FileName); + data.Text = source.Text; + data.CaretLocation = new MonoDevelop.Ide.Editor.DocumentLocation (method.Region.BeginLine, method.Region.BeginColumn); + return true;*/ + return false; } List<ReferenceSegment> IAssemblyBrowserNodeBuilder.Disassemble (TextEditor data, ITreeNavigator navigator) { - var method = (IUnresolvedMethod)navigator.DataItem; if (HandleSourceCodeEntity (navigator, data)) return null; - var cecilMethod = GetCecilLoader (navigator).GetCecilObject <MethodDefinition> (method); + var cecilMethod = (MethodDefinition)navigator.DataItem; if (cecilMethod == null) return null; return Disassemble (data, rd => rd.DisassembleMethod (cecilMethod)); } - - string IAssemblyBrowserNodeBuilder.GetDocumentationMarkup (ITreeNavigator navigator) - { - var method = (IUnresolvedMethod)navigator.DataItem; - var resolved = Resolve (navigator, method); - if (GetMainAssembly (navigator) == null) { - return StringTextSource.ReadFrom (method.Region.FileName).Text; - } - StringBuilder result = new StringBuilder (); - result.Append ("<big>"); - result.Append (MonoDevelop.Ide.TypeSystem.Ambience.EscapeText (Ambience.ConvertSymbol (resolved))); - result.Append ("</big>"); - result.AppendLine (); - //result.Append (AmbienceService.GetDocumentationMarkup (resolved, AmbienceService.GetDocumentation (resolved), options)); - - return result.ToString (); - } #endregion - } } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/DomPropertyNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/PropertyDefinitionNodeBuilder.cs index c40828ec8a..eca7adcb10 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/DomPropertyNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/PropertyDefinitionNodeBuilder.cs @@ -39,44 +39,79 @@ using Mono.Cecil; using ICSharpCode.Decompiler.TypeSystem; using MonoDevelop.Ide.TypeSystem; using MonoDevelop.Ide.Editor; +using ICSharpCode.ILSpy; +using MonoDevelop.Core; namespace MonoDevelop.AssemblyBrowser { - class DomPropertyNodeBuilder : AssemblyBrowserTypeNodeBuilder, IAssemblyBrowserNodeBuilder + class PropertyDefinitionNodeBuilder : AssemblyBrowserTypeNodeBuilder, IAssemblyBrowserNodeBuilder { public override Type NodeDataType { - get { return typeof(IUnresolvedProperty); } + get { return typeof(PropertyDefinition); } } - public DomPropertyNodeBuilder (AssemblyBrowserWidget widget) : base (widget) + public PropertyDefinitionNodeBuilder (AssemblyBrowserWidget widget) : base (widget) { } public override string GetNodeName (ITreeNavigator thisNode, object dataObject) { - var property = (IUnresolvedProperty)dataObject; + var property = (PropertyDefinition)dataObject; return property.Name; } public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) { - var property = (IUnresolvedProperty)dataObject; - try { - var resolved = Resolve (treeBuilder, property); - nodeInfo.Label = MonoDevelop.Ide.TypeSystem.Ambience.EscapeText (Ambience.ConvertSymbol (resolved)); - } catch (Exception) { - nodeInfo.Label = property.Name; + var property = (PropertyDefinition)dataObject; + nodeInfo.Label = MonoDevelop.Ide.TypeSystem.Ambience.EscapeText (GetText (property, property.IsIndexer ())); + + var accessor = property.GetMethod ?? property.SetMethod; + + if (((MethodAttributes.Private | MethodAttributes.Assembly) & accessor.Attributes) != 0) + nodeInfo.Label = MethodDefinitionNodeBuilder.FormatPrivate (nodeInfo.Label); + + nodeInfo.Icon = Context.GetIcon (GetStockIcon (property)); + } + + public static IconId GetStockIcon (PropertyDefinition property) + { + var accessor = property.GetMethod ?? property.SetMethod; + return MethodDefinitionNodeBuilder.GetStockIcon (accessor); + } + + static string GetText (PropertyDefinition property, bool? isIndexer = null) + { + string name = CSharpLanguage.Instance.FormatPropertyName (property, isIndexer); + + var b = new System.Text.StringBuilder (); + if (property.HasParameters) { + b.Append ('('); + for (int i = 0; i < property.Parameters.Count; i++) { + if (i > 0) + b.Append (", "); + b.Append (CSharpLanguage.Instance.TypeToString (property.Parameters [i].ParameterType, false, property.Parameters [i])); + } + var method = property.GetMethod ?? property.SetMethod; + if (method.CallingConvention == MethodCallingConvention.VarArg) { + if (property.HasParameters) + b.Append (", "); + b.Append ("..."); + } + b.Append (") : "); + } else { + b.Append (" : "); } - if (property.IsPrivate || property.IsInternal) - nodeInfo.Label = DomMethodNodeBuilder.FormatPrivate (nodeInfo.Label); - nodeInfo.Icon = Context.GetIcon (property.GetStockIcon ()); + b.Append (CSharpLanguage.Instance.TypeToString (property.PropertyType, false, property)); + + return name + b; } + public override void BuildChildNodes (ITreeBuilder ctx, object dataObject) { } - + public override bool HasChildNodes (ITreeBuilder builder, object dataObject) { return false; @@ -87,10 +122,10 @@ namespace MonoDevelop.AssemblyBrowser List<ReferenceSegment> IAssemblyBrowserNodeBuilder.Disassemble (TextEditor data, ITreeNavigator navigator) { - if (DomMethodNodeBuilder.HandleSourceCodeEntity (navigator, data)) + if (MethodDefinitionNodeBuilder.HandleSourceCodeEntity (navigator, data)) return null; - var property = GetCecilLoader (navigator).GetCecilObject<PropertyDefinition> ((IUnresolvedProperty)navigator.DataItem); - return DomMethodNodeBuilder.Disassemble (data, rd => rd.DisassembleProperty (property)); + var property = (PropertyDefinition)navigator.DataItem; + return MethodDefinitionNodeBuilder.Disassemble (data, rd => rd.DisassembleProperty (property)); } static string GetBody (string text) @@ -107,27 +142,12 @@ namespace MonoDevelop.AssemblyBrowser List<ReferenceSegment> IAssemblyBrowserNodeBuilder.Decompile (TextEditor data, ITreeNavigator navigator, DecompileFlags flags) { - if (DomMethodNodeBuilder.HandleSourceCodeEntity (navigator, data)) + if (MethodDefinitionNodeBuilder.HandleSourceCodeEntity (navigator, data)) return null; - var property = GetCecilLoader (navigator).GetCecilObject<PropertyDefinition> ((IUnresolvedProperty)navigator.DataItem); + var property = navigator.DataItem as PropertyDefinition; if (property == null) return null; - return DomMethodNodeBuilder.Decompile (data, DomMethodNodeBuilder.GetAssemblyLoader (navigator), b => b.Decompile (property), flags: flags); - } - - string IAssemblyBrowserNodeBuilder.GetDocumentationMarkup (ITreeNavigator navigator) - { - var property = (IUnresolvedProperty)navigator.DataItem; - var resolved = Resolve (navigator, property); - StringBuilder result = new StringBuilder (); - result.Append ("<big>"); - result.Append (MonoDevelop.Ide.TypeSystem.Ambience.EscapeText (Ambience.ConvertSymbol (resolved))); - result.Append ("</big>"); - result.AppendLine (); - - //result.Append (AmbienceService.GetDocumentationMarkup (resolved, AmbienceService.GetDocumentation (resolved), options)); - - return result.ToString (); + return MethodDefinitionNodeBuilder.Decompile (data, MethodDefinitionNodeBuilder.GetAssemblyLoader (navigator), b => b.Decompile (property), flags: flags); } #endregion diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/TypeDefinitionNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/TypeDefinitionNodeBuilder.cs new file mode 100644 index 0000000000..bd74132fef --- /dev/null +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/TypeDefinitionNodeBuilder.cs @@ -0,0 +1,240 @@ +// +// DomTypeNodeBuilder.cs +// +// Author: +// Mike Krüger <mkrueger@novell.com> +// +// Copyright (C) 2008 Novell, Inc (http://www.novell.com) +// +// 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.Text; +using System.Linq; +using Mono.Cecil; +using MonoDevelop.Core; +using MonoDevelop.Ide.Gui.Components; +using MonoDevelop.Ide; +using ICSharpCode.Decompiler; +using System.Threading; +using System.Collections.Generic; +using MonoDevelop.Ide.TypeSystem; +using ICSharpCode.Decompiler.TypeSystem; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Ide.Editor.Highlighting; +using MonoDevelop.Ide.Gui.Content; +using ICSharpCode.Decompiler.CSharp.OutputVisitor; +using ICSharpCode.ILSpy; + +namespace MonoDevelop.AssemblyBrowser +{ + class TypeDefinitionNodeBuilder : AssemblyBrowserTypeNodeBuilder, IAssemblyBrowserNodeBuilder + { + public override Type NodeDataType { + get { return typeof(TypeDefinition); } + } + + public override string ContextMenuAddinPath { + get { return "/MonoDevelop/AssemblyBrowser/TypeNode/ContextMenu"; } + } + + public TypeDefinitionNodeBuilder (AssemblyBrowserWidget widget) : base (widget) + { + } + + public override string GetNodeName (ITreeNavigator thisNode, object dataObject) + { + var type = (TypeDefinition)dataObject; + return type.Name; + } + + public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) + { + var type = (TypeDefinition)dataObject; + nodeInfo.Label = MonoDevelop.Ide.TypeSystem.Ambience.EscapeText (CSharpLanguage.Instance.FormatTypeName (type)); + if (IsNonPublic(type)) + nodeInfo.Label = MethodDefinitionNodeBuilder.FormatPrivate (nodeInfo.Label); + nodeInfo.Icon = Context.GetIcon (GetStockIcon(type)); + } + + public static IconId GetStockIcon (TypeDefinition type) + { + return "md-" + GetAccess (type.Attributes) + GetSource (type); + } + + static string GetSource (TypeDefinition type) + {
+ if (type.IsInterface) + return "interface"; + if (type.IsValueType) + return "struct"; + if (type.IsEnum) + return "enum"; + if (type.IsDelegate ()) + return "delegate"; + return "class"; + } + + static string GetAccess (TypeAttributes attributes) + { + switch (attributes & TypeAttributes.VisibilityMask) { + case TypeAttributes.NestedPrivate: + return "private-"; + case TypeAttributes.Public: + case TypeAttributes.NestedPublic: + return ""; + case TypeAttributes.NestedFamily: + return "protected-"; + case TypeAttributes.NestedAssembly: + return "internal-"; + case TypeAttributes.NestedFamORAssem: + case TypeAttributes.NestedFamANDAssem: + return "ProtectedOrInternal-"; + default: + return ""; + } + } + + bool IsNonPublic (TypeDefinition type) + { + return (type.Attributes & (TypeAttributes.Public | TypeAttributes.NestedPublic)) == 0; + } + + public override void BuildChildNodes (ITreeBuilder builder, object dataObject) + { + var type = (TypeDefinition)dataObject; + var list = new System.Collections.ArrayList (); + if (type.BaseType != null || type.HasInterfaces) + list.Add (new BaseTypeFolder (type)); + bool publicOnly = Widget.PublicApiOnly; + + foreach (var nestedType in type.NestedTypes.OrderBy (m => m.Name, StringComparer.InvariantCulture)) { + if (publicOnly && IsNonPublic (nestedType)) + continue; + builder.AddChild (nestedType); + } + + foreach (var field in type.Fields.OrderBy (m => m.Name, StringComparer.InvariantCulture)) { + if (publicOnly && ((FieldAttributes.Private | FieldAttributes.Assembly) & field.Attributes) != 0) + continue; + builder.AddChild (field); + } + + foreach (var property in type.Properties.OrderBy (m => m.Name, StringComparer.InvariantCulture)) { + var accessor = property.GetMethod ?? property.SetMethod; + if (publicOnly && ((MethodAttributes.Private | MethodAttributes.Assembly) & accessor.Attributes) != 0) + continue; + builder.AddChild (property); + } + + foreach (var evt in type.Events.OrderBy (m => m.Name, StringComparer.InvariantCulture)) { + var accessor = evt.AddMethod ?? evt.RemoveMethod; + if (publicOnly && ((MethodAttributes.Private | MethodAttributes.Assembly) & accessor.Attributes) != 0) + continue; + builder.AddChild (evt); + } + + var accessorMethods = type.GetAccessorMethods (); + foreach (var method in type.Methods.OrderBy (m => m.Name, StringComparer.InvariantCulture)) { + if (!accessorMethods.Contains (method)) { + builder.AddChild (method); + } + } + } + + public override bool HasChildNodes (ITreeBuilder builder, object dataObject) + { + return true; + } + + #region IAssemblyBrowserNodeBuilder + internal static void PrintAssembly (StringBuilder result, ITreeNavigator navigator) + { + var assemblyDefinition = (AssemblyDefinition)navigator.GetParentDataItem (typeof (AssemblyDefinition), false); + if (assemblyDefinition == null) + return; + + result.Append (GettextCatalog.GetString ("<b>Assembly:</b>\t{0}, Version={1}", + assemblyDefinition.Name.Name, + assemblyDefinition.Name.Version)); + result.AppendLine (); + } + + public string GetDescription (ITreeNavigator navigator) + { + /* var type = (TypeDefinition)navigator.DataItem; + var resolved = Resolve (navigator, type); + StringBuilder result = new StringBuilder (); + result.Append ("<span font_family=\"monospace\">"); + result.Append (MonoDevelop.Ide.TypeSystem.Ambience.EscapeText (Ambience.ConvertType (resolved))); + result.Append ("</span>"); + result.AppendLine (); + result.Append (GettextCatalog.GetString ("<b>Name:</b>\t{0}", type.FullName)); + result.AppendLine (); + PrintAssembly (result, navigator); + return result.ToString ();*/ + return ""; + } + + public List<ReferenceSegment> Disassemble (TextEditor data, ITreeNavigator navigator) + { + if (MethodDefinitionNodeBuilder.HandleSourceCodeEntity (navigator, data)) + return null; + var type = (TypeDefinition)navigator.DataItem; + if (type == null) + return null; + + return MethodDefinitionNodeBuilder.Disassemble (data, rd => rd.DisassembleType (type)); + } + + internal static DecompilerSettings CreateDecompilerSettings (bool publicOnly, MonoDevelop.CSharp.Formatting.CSharpFormattingPolicy codePolicy) + { + return new DecompilerSettings { + AnonymousMethods = true, + AutomaticEvents = true, + AutomaticProperties = true, + ExpressionTrees = true, + YieldReturn = true, + ForEachStatement = true, + LockStatement = true, + AsyncAwait = true, + ShowXmlDocumentation = true, + CSharpFormattingOptions = FormattingOptionsFactory.CreateMono () + }; + } + + public List<ReferenceSegment> Decompile (TextEditor data, ITreeNavigator navigator, DecompileFlags flags) + { + if (MethodDefinitionNodeBuilder.HandleSourceCodeEntity (navigator, data)) + return null; + var type = (TypeDefinition)navigator.DataItem; + if (type == null) + return null; + var settings = MethodDefinitionNodeBuilder.GetDecompilerSettings (data, flags.PublicOnly); + // CSharpLanguage.Instance.DecompileType (type, output, settings); + return MethodDefinitionNodeBuilder.Decompile ( + data, + MethodDefinitionNodeBuilder.GetAssemblyLoader (navigator), + builder => builder.DecompileType (type.GetFullTypeName ()), flags: flags); + } + #endregion + } +} diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/DomTypeNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/DomTypeNodeBuilder.cs deleted file mode 100644 index 1b2962c8f0..0000000000 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/DomTypeNodeBuilder.cs +++ /dev/null @@ -1,184 +0,0 @@ -// -// DomTypeNodeBuilder.cs -// -// Author: -// Mike Krüger <mkrueger@novell.com> -// -// Copyright (C) 2008 Novell, Inc (http://www.novell.com) -// -// 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.Text; -using System.Linq; -using Mono.Cecil; -using MonoDevelop.Core; -using MonoDevelop.Ide.Gui.Components; -using MonoDevelop.Ide; -using ICSharpCode.Decompiler; -using System.Threading; -using System.Collections.Generic; -using MonoDevelop.Ide.TypeSystem; -using ICSharpCode.Decompiler.TypeSystem; -using MonoDevelop.Ide.Editor; -using MonoDevelop.Ide.Editor.Highlighting; -using MonoDevelop.Ide.Gui.Content; -using ICSharpCode.Decompiler.CSharp.OutputVisitor; - -namespace MonoDevelop.AssemblyBrowser -{ - class DomTypeNodeBuilder : AssemblyBrowserTypeNodeBuilder, IAssemblyBrowserNodeBuilder - { - public override Type NodeDataType { - get { return typeof(IUnresolvedTypeDefinition); } - } - - public override string ContextMenuAddinPath { - get { return "/MonoDevelop/AssemblyBrowser/TypeNode/ContextMenu"; } - } - - public DomTypeNodeBuilder (AssemblyBrowserWidget widget) : base (widget) - { - } - - public override string GetNodeName (ITreeNavigator thisNode, object dataObject) - { - var type = (IUnresolvedTypeDefinition)dataObject; - return type.Name; - } - - public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) - { - var type = (IUnresolvedTypeDefinition)dataObject; - try { - var resolved = Resolve (treeBuilder, type); - nodeInfo.Label = MonoDevelop.Ide.TypeSystem.Ambience.EscapeText (Ambience.ConvertType (resolved)); - } catch (Exception) { - nodeInfo.Label = type.Name; - } - if (type.IsPrivate) - nodeInfo.Label = DomMethodNodeBuilder.FormatPrivate (nodeInfo.Label); - nodeInfo.Icon = Context.GetIcon (type.GetStockIcon ()); - } - - public override void BuildChildNodes (ITreeBuilder builder, object dataObject) - { - var type = (IUnresolvedTypeDefinition)dataObject; - var list = new System.Collections.ArrayList (); - list.Add (new BaseTypeFolder (type)); - bool publicOnly = Widget.PublicApiOnly; - foreach (var t in type.NestedTypes.Where (m => !m.IsSynthetic && (m.IsPublic || m.IsProtected || !publicOnly))) { - list.Add (t); - } - foreach (var m in type.Members.Where (m => !m.IsSynthetic && (m.IsPublic || m.IsProtected || !publicOnly))) - list.Add (m); - builder.AddChildren (list); - } - - public override bool HasChildNodes (ITreeBuilder builder, object dataObject) - { - return true; - } - - #region IAssemblyBrowserNodeBuilder - internal static void PrintAssembly (StringBuilder result, ITreeNavigator navigator) - { - var assemblyDefinition = (AssemblyDefinition)navigator.GetParentDataItem (typeof (AssemblyDefinition), false); - if (assemblyDefinition == null) - return; - - result.Append (GettextCatalog.GetString ("<b>Assembly:</b>\t{0}, Version={1}", - assemblyDefinition.Name.Name, - assemblyDefinition.Name.Version)); - result.AppendLine (); - } - - public string GetDescription (ITreeNavigator navigator) - { - var type = (IUnresolvedTypeDefinition)navigator.DataItem; - var resolved = Resolve (navigator, type); - StringBuilder result = new StringBuilder (); - result.Append ("<span font_family=\"monospace\">"); - result.Append (MonoDevelop.Ide.TypeSystem.Ambience.EscapeText (Ambience.ConvertType (resolved))); - result.Append ("</span>"); - result.AppendLine (); - result.Append (GettextCatalog.GetString ("<b>Name:</b>\t{0}", type.FullName)); - result.AppendLine (); - PrintAssembly (result, navigator); - return result.ToString (); - } - - public List<ReferenceSegment> Disassemble (TextEditor data, ITreeNavigator navigator) - { - if (DomMethodNodeBuilder.HandleSourceCodeEntity (navigator, data)) - return null; - var type = GetCecilLoader (navigator).GetCecilObject<TypeDefinition> ((IUnresolvedTypeDefinition)navigator.DataItem); - if (type == null) - return null; - - return DomMethodNodeBuilder.Disassemble (data, rd => rd.DisassembleType (type)); - } - - internal static DecompilerSettings CreateDecompilerSettings (bool publicOnly, MonoDevelop.CSharp.Formatting.CSharpFormattingPolicy codePolicy) - { - return new DecompilerSettings { - AnonymousMethods = true, - AutomaticEvents = true, - AutomaticProperties = true, - ExpressionTrees = true, - YieldReturn = true, - ForEachStatement = true, - LockStatement = true, - AsyncAwait = true, - ShowXmlDocumentation = true, - CSharpFormattingOptions = FormattingOptionsFactory.CreateMono () - }; - } - - public List<ReferenceSegment> Decompile (TextEditor data, ITreeNavigator navigator, DecompileFlags flags) - { - if (DomMethodNodeBuilder.HandleSourceCodeEntity (navigator, data)) - return null; - var type = (IUnresolvedTypeDefinition)navigator.DataItem; - if (type == null) - return null; - var settings = DomMethodNodeBuilder.GetDecompilerSettings (data, flags.PublicOnly); - return DomMethodNodeBuilder.Decompile (data, DomMethodNodeBuilder.GetAssemblyLoader (navigator), builder => builder.DecompileType(type.FullTypeName), flags: flags); - } - - string IAssemblyBrowserNodeBuilder.GetDocumentationMarkup (ITreeNavigator navigator) - { - var type = (IUnresolvedTypeDefinition)navigator.DataItem; - var resolved = Resolve (navigator, type); - var result = new StringBuilder (); - result.Append ("<big>"); - result.Append (MonoDevelop.Ide.TypeSystem.Ambience.EscapeText (Ambience.ConvertType (resolved))); - result.Append ("</big>"); - result.AppendLine (); - - //result.Append (AmbienceService.GetDocumentationMarkup (resolved.GetDefinition (), AmbienceService.GetDocumentation (resolved.GetDefinition ()), options)); - - return result.ToString (); - } - #endregion - - } -} diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/NamespaceBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/NamespaceBuilder.cs index a4e410f593..7d82a9e52e 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/NamespaceBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/NamespaceBuilder.cs @@ -40,7 +40,7 @@ namespace MonoDevelop.AssemblyBrowser class NamespaceBuilder : AssemblyBrowserTypeNodeBuilder, IAssemblyBrowserNodeBuilder { public override Type NodeDataType { - get { return typeof(Namespace); } + get { return typeof(NamespaceData); } } public NamespaceBuilder (AssemblyBrowserWidget widget) : base (widget) @@ -53,8 +53,8 @@ namespace MonoDevelop.AssemblyBrowser try { if (thisNode == null || otherNode == null) return -1; - var e1 = thisNode.DataItem as Namespace; - var e2 = otherNode.DataItem as Namespace; + var e1 = thisNode.DataItem as NamespaceData; + var e2 = otherNode.DataItem as NamespaceData; if (e1 == null && e2 == null) return 0; @@ -72,23 +72,26 @@ namespace MonoDevelop.AssemblyBrowser public override string GetNodeName (ITreeNavigator thisNode, object dataObject) { - Namespace ns = (Namespace)dataObject; + NamespaceData ns = (NamespaceData)dataObject; return ns.Name; } public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) { - Namespace ns = (Namespace)dataObject; + NamespaceData ns = (NamespaceData)dataObject; nodeInfo.Label = GLib.Markup.EscapeText (ns.Name); nodeInfo.Icon = Context.GetIcon (MonoDevelop.Ide.Gui.Stock.NameSpace); } public override void BuildChildNodes (ITreeBuilder ctx, object dataObject) { - Namespace ns = (Namespace)dataObject; + NamespaceData ns = (NamespaceData)dataObject; bool publicOnly = Widget.PublicApiOnly; - if (ns.Types != null) - ctx.AddChildren (publicOnly ? ns.Types.Where (t => t.IsPublic) : ns.Types); + foreach (var type in ns.Types) { + if (publicOnly && !type.isPublic) + continue; + ctx.AddChild (type.typeObject); + } } public override bool HasChildNodes (ITreeBuilder builder, object dataObject) @@ -101,7 +104,7 @@ namespace MonoDevelop.AssemblyBrowser public List<ReferenceSegment> Disassemble (TextEditor data, ITreeNavigator navigator) { // bool publicOnly = Widget.PublicApiOnly; - Namespace ns = (Namespace)navigator.DataItem; + NamespaceData ns = (NamespaceData)navigator.DataItem; data.Text = "// " + ns.Name; return null; @@ -111,11 +114,6 @@ namespace MonoDevelop.AssemblyBrowser { return Disassemble (data, navigator); } - - string IAssemblyBrowserNodeBuilder.GetDocumentationMarkup (ITreeNavigator navigator) - { - return null; - } #endregion } } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/Namespace.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/NamespaceData.cs index 4b2b64ed56..4fc5a5ecea 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/Namespace.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/NamespaceData.cs @@ -33,22 +33,22 @@ using Mono.Cecil; namespace MonoDevelop.AssemblyBrowser { - class Namespace : IDisposable + class NamespaceData : IDisposable { - List<IUnresolvedTypeDefinition> types = new List<IUnresolvedTypeDefinition> (); + List<(bool, object)> types = new List<(bool, object)> (); public string Name { get; private set; } - public List<IUnresolvedTypeDefinition> Types { + public List<(bool isPublic, object typeObject)> Types { get { return types; } } - public Namespace (string name) + public NamespaceData (string name) { this.Name = name; } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/ProjectNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/ProjectNodeBuilder.cs index eaffb4db57..eb776c4a9d 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/ProjectNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/ProjectNodeBuilder.cs @@ -44,19 +44,19 @@ namespace MonoDevelop.AssemblyBrowser class ProjectNodeBuilder : TypeNodeBuilder { internal AssemblyBrowserWidget Widget { - get; - private set; + get; + private set; } - + public override Type NodeDataType { get { return typeof(Project); } } - + public ProjectNodeBuilder (AssemblyBrowserWidget widget) { Widget = widget; } - + public override string GetNodeName (ITreeNavigator thisNode, object dataObject) { var project = (Project)dataObject; @@ -73,36 +73,38 @@ namespace MonoDevelop.AssemblyBrowser public override void BuildChildNodes (ITreeBuilder builder, object dataObject) { - //var project = (Project)dataObject; - // TODO: Roslyn port. -// var ctx = TypeSystemService.GetProjectContext (project); -// if (ctx == null) -// return; -// -// var namespaces = new Dictionary<string, Namespace> (); -// -// foreach (var type in ctx.TopLevelTypeDefinitions) { -// string namespaceName = string.IsNullOrEmpty (type.Namespace) ? "-" : type.Namespace; -// if (!namespaces.ContainsKey (namespaceName)) -// namespaces [namespaceName] = new Namespace (namespaceName); -// -// var ns = namespaces [namespaceName]; -// ns.Types.Add (type); -// } -// -// foreach (var ns in namespaces.Values) { -// builder.AddChild (ns); -// } + Project project = (Project)dataObject; + bool publicOnly = Widget.PublicApiOnly; + var dom = TypeSystemService.GetCompilationAsync (project).Result; + if (dom == null) + return; + bool nestedNamespaces = builder.Options ["NestedNamespaces"]; + HashSet<string> addedNames = new HashSet<string> (); + foreach (var ns in dom.Assembly.GlobalNamespace.GetNamespaceMembers ()) { + FillNamespaces (builder, project, ns); + } + builder.AddChildren (dom.Assembly.GlobalNamespace.GetTypeMembers () + .Where (type => !publicOnly || type.DeclaredAccessibility == Microsoft.CodeAnalysis.Accessibility.Public)); + } + + public static void FillNamespaces (ITreeBuilder builder, Project project, Microsoft.CodeAnalysis.INamespaceSymbol ns) + { + var members = ns.GetTypeMembers (); + //IParserContext ctx = IdeApp.Workspace.ParserDatabase.GetProjectParserContext (project); + if (members.Any ()) { + var data = new NamespaceData (ns.Name); + foreach (var member in members) + data.Types.Add ((member.DeclaredAccessibility == Microsoft.CodeAnalysis.Accessibility.Public, member)); + builder.AddChild (data); + } + foreach (var nSpace in ns.GetNamespaceMembers ()) { + FillNamespaces (builder, project, nSpace); + } } - + public override bool HasChildNodes (ITreeBuilder builder, object dataObject) { - //var project = (Project)dataObject; - // TODO: Roslyn port. - //var ctx = TypeSystemService.GetProjectContext (project); - //if (ctx == null) - return false; - // return ctx.TopLevelTypeDefinitions.Any (); + return true; } public override int CompareObjects (ITreeNavigator thisNode, ITreeNavigator otherNode) diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynEventNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynEventNodeBuilder.cs new file mode 100644 index 0000000000..adc1897c88 --- /dev/null +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynEventNodeBuilder.cs @@ -0,0 +1,52 @@ +// +// RoslynEventNodeBuilder.cs +// +// Author: +// Mike Krüger <mikkrg@microsoft.com> +// +// Copyright (c) 2018 Microsoft Corporation. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections; + +using MonoDevelop.Projects; +using MonoDevelop.Core; +using MonoDevelop.Ide.Gui.Components; +using MonoDevelop.Ide.TypeSystem; +using Microsoft.CodeAnalysis; + +namespace MonoDevelop.AssemblyBrowser +{ + class RoslynEventNodeBuilder : RoslynMemberNodeBuilder + { + public override Type NodeDataType { + get { return typeof (IEventSymbol); } + } + + public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) + { + var data = dataObject as IEventSymbol; + nodeInfo.Label = Ambience.EscapeText (((ISymbol)dataObject).ToDisplayString (Ambience.NameFormat)); + nodeInfo.Icon = Context.GetIcon (data.GetStockIcon ()); + } + } +} +
\ No newline at end of file diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynFieldNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynFieldNodeBuilder.cs new file mode 100644 index 0000000000..3f6b433202 --- /dev/null +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynFieldNodeBuilder.cs @@ -0,0 +1,47 @@ +// +// RoslynEventNodeBuilder.cs +// +// Author: +// Mike Krüger <mikkrg@microsoft.com> +// +// Copyright (c) 2018 Microsoft Corporation. All rights reserved. +// +// 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 MonoDevelop.Ide.Gui.Components; +using MonoDevelop.Ide.TypeSystem; +using Microsoft.CodeAnalysis; + +namespace MonoDevelop.AssemblyBrowser +{ + class RoslynFieldNodeBuilder : RoslynMemberNodeBuilder + { + public override Type NodeDataType { + get { return typeof (IFieldSymbol); } + } + + public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) + { + var data = dataObject as IFieldSymbol; + nodeInfo.Label = Ambience.EscapeText (((ISymbol)dataObject).ToDisplayString (Ambience.NameFormat)); + nodeInfo.Icon = Context.GetIcon (data.GetStockIcon ()); + } + } +} diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynMemberNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynMemberNodeBuilder.cs new file mode 100644 index 0000000000..be4f1a47a8 --- /dev/null +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynMemberNodeBuilder.cs @@ -0,0 +1,115 @@ +// +// RoslynEventNodeBuilder.cs +// +// Author: +// Mike Krüger <mikkrg@microsoft.com> +// +// Copyright (c) 2018 Microsoft Corporation. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Ide.Gui.Components; +using MonoDevelop.Ide.Gui.Pads; +using MonoDevelop.Ide.TypeSystem; +using MonoDevelop.Core; + +namespace MonoDevelop.AssemblyBrowser +{ + abstract class RoslynMemberNodeBuilder : TypeNodeBuilder, IAssemblyBrowserNodeBuilder + { + public override string GetNodeName (ITreeNavigator thisNode, object dataObject) + { + return ((ISymbol)dataObject).ToDisplayString (Ambience.NameFormat); + } + + public override int CompareObjects (ITreeNavigator thisNode, ITreeNavigator otherNode) + { + if (!(otherNode.DataItem is ISymbol)) return 1; + + if (thisNode.Options ["GroupByType"]) { + int v1 = GetTypeSortValue (thisNode.DataItem); + int v2 = GetTypeSortValue (otherNode.DataItem); + if (v1 < v2) return -1; + else if (v1 > v2) return 1; + } + if (thisNode.Options ["GroupByAccess"]) { + int v1 = GetAccessSortValue (((ISymbol)thisNode.DataItem).DeclaredAccessibility); + int v2 = GetAccessSortValue (((ISymbol)otherNode.DataItem).DeclaredAccessibility); + if (v1 < v2) return -1; + else if (v1 > v2) return 1; + } + return DefaultSort; + } + + int GetTypeSortValue (object member) + { + if (member is IFieldSymbol) return 0; + if (member is IEventSymbol) return 1; + if (member is IPropertySymbol) return 2; + if (member is IMethodSymbol) return 3; + return 4; + } + + int GetAccessSortValue (Accessibility mods) + { + if ((mods & Accessibility.Private) != 0) return 0; + if ((mods & Accessibility.Internal) != 0) return 1; + if ((mods & Accessibility.Protected) != 0) return 2; + if ((mods & Accessibility.Public) != 0) return 3; + return 4; + } + + public List<ReferenceSegment> Decompile (TextEditor data, ITreeNavigator navigator, DecompileFlags flags) + { + return Disassemble (data, navigator); + } + + static readonly List<ReferenceSegment> emptyReferences = new List<ReferenceSegment> (); + + public List<ReferenceSegment> Disassemble (TextEditor data, ITreeNavigator navigator) + { + var symbol = navigator.DataItem as ISymbol; + if (symbol == null) { + data.Text = "// DataItem is no symbol " + navigator.DataItem; // should never happen + LoggingService.LogError ("DataItem is no symbol " + navigator.DataItem); + return emptyReferences; + } + var location = symbol.Locations [0]; + if (location.IsInSource) { + var root = location.SourceTree.GetRoot (); + var node = root.FindNode (location.SourceSpan); + if (node != null) { + data.Text = node.ToFullString (); + } else { + data.Text = GettextCatalog.GetString ("// no source node found for {0}", symbol.MetadataName); + } + } else { + data.Text = "// Error: Symbol " + symbol.MetadataName + " is not in source."; // should never happen + LoggingService.LogError ("Symbol " + symbol.MetadataName + " is not in source."); + } + return emptyReferences; + } + + + } +} diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynMethodNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynMethodNodeBuilder.cs new file mode 100644 index 0000000000..216d0d94af --- /dev/null +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynMethodNodeBuilder.cs @@ -0,0 +1,47 @@ +// +// RoslynEventNodeBuilder.cs +// +// Author: +// Mike Krüger <mikkrg@microsoft.com> +// +// Copyright (c) 2018 Microsoft Corporation. All rights reserved. +// +// 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 MonoDevelop.Ide.Gui.Components; +using MonoDevelop.Ide.TypeSystem; +using Microsoft.CodeAnalysis; + +namespace MonoDevelop.AssemblyBrowser +{ + class RoslynMethodNodeBuilder : RoslynMemberNodeBuilder + { + public override Type NodeDataType { + get { return typeof (IMethodSymbol); } + } + + public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) + { + var data = dataObject as IMethodSymbol; + nodeInfo.Label = Ambience.EscapeText (((ISymbol)dataObject).ToDisplayString (Ambience.NameFormat)); + nodeInfo.Icon = Context.GetIcon (data.GetStockIcon ()); + } + } +} diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynPropertyNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynPropertyNodeBuilder.cs new file mode 100644 index 0000000000..509da74476 --- /dev/null +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynPropertyNodeBuilder.cs @@ -0,0 +1,47 @@ +// +// RoslynEventNodeBuilder.cs +// +// Author: +// Mike Krüger <mikkrg@microsoft.com> +// +// Copyright (c) 2018 Microsoft Corporation. All rights reserved. +// +// 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 MonoDevelop.Ide.Gui.Components; +using MonoDevelop.Ide.TypeSystem; +using Microsoft.CodeAnalysis; + +namespace MonoDevelop.AssemblyBrowser +{ + class RoslynPropertyNodeBuilder : RoslynMemberNodeBuilder + { + public override Type NodeDataType { + get { return typeof (IPropertySymbol); } + } + + public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) + { + var data = dataObject as IPropertySymbol; + nodeInfo.Label = Ambience.EscapeText (((ISymbol)dataObject).ToDisplayString (Ambience.NameFormat)); + nodeInfo.Icon = Context.GetIcon (data.GetStockIcon ()); + } + } +} diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynTypeNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynTypeNodeBuilder.cs new file mode 100644 index 0000000000..81b74424aa --- /dev/null +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynTypeNodeBuilder.cs @@ -0,0 +1,124 @@ +// +// RoslynEventNodeBuilder.cs +// +// Author: +// Mike Krüger <mikkrg@microsoft.com> +// +// Copyright (c) 2018 Microsoft Corporation. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Linq; +using MonoDevelop.Ide.Gui.Components; +using MonoDevelop.Ide.TypeSystem; +using Microsoft.CodeAnalysis; + +namespace MonoDevelop.AssemblyBrowser +{ + class RoslynTypeNodeBuilder : RoslynMemberNodeBuilder + { + public override Type NodeDataType { + get { return typeof (ITypeSymbol); } + } + + internal AssemblyBrowserWidget Widget { + get; + private set; + } + + public RoslynTypeNodeBuilder (AssemblyBrowserWidget widget) + { + Widget = widget; + } + + public override string GetNodeName (ITreeNavigator thisNode, object dataObject) + { + return ((ITypeSymbol)dataObject).GetFullName (); + } + + //Same as MonoDevelop.Ide.TypeSystem.Ambience.NameFormat except SymbolDisplayTypeQualificationStyle is NameOnly + static readonly SymbolDisplayFormat NameFormat = + new SymbolDisplayFormat ( + globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameOnly, + propertyStyle: SymbolDisplayPropertyStyle.NameOnly, + genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance, + memberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeExplicitInterface, + parameterOptions: + SymbolDisplayParameterOptions.IncludeParamsRefOut | + SymbolDisplayParameterOptions.IncludeExtensionThis | + SymbolDisplayParameterOptions.IncludeType | + SymbolDisplayParameterOptions.IncludeName, + miscellaneousOptions: + SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | + SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) + { + ITypeSymbol classData = dataObject as ITypeSymbol; + nodeInfo.Label = Ambience.EscapeText (classData.ToDisplayString (NameFormat)); + nodeInfo.Icon = Context.GetIcon (classData.GetStockIcon ()); + } + + public override void BuildChildNodes (ITreeBuilder builder, object dataObject) + { + ITypeSymbol classData = dataObject as ITypeSymbol; + bool publicOnly = Widget.PublicApiOnly; + bool publicProtectedOnly = false; + publicOnly |= publicProtectedOnly; + + // Delegates have an Invoke method, which doesn't need to be shown. + if (classData.TypeKind == TypeKind.Delegate) + return; + + builder.AddChildren (classData.GetTypeMembers () + .Where (innerClass => innerClass.DeclaredAccessibility == Accessibility.Public || + (innerClass.DeclaredAccessibility == Accessibility.Protected && publicProtectedOnly) || + !publicOnly)); + + builder.AddChildren (classData.GetMembers ().OfType<IMethodSymbol> ().Where (m => m.MethodKind != MethodKind.PropertyGet && m.MethodKind != MethodKind.PropertySet) + .Where (method => method.DeclaredAccessibility == Accessibility.Public || + (method.DeclaredAccessibility == Accessibility.Protected && publicProtectedOnly) || + !publicOnly)); + + builder.AddChildren (classData.GetMembers ().OfType<IPropertySymbol> () + .Where (property => property.DeclaredAccessibility == Accessibility.Public || + (property.DeclaredAccessibility == Accessibility.Protected && publicProtectedOnly) || + !publicOnly)); + + builder.AddChildren (classData.GetMembers ().OfType<IFieldSymbol> () + .Where (field => field.DeclaredAccessibility == Accessibility.Public || + (field.DeclaredAccessibility == Accessibility.Protected && publicProtectedOnly) || + !publicOnly)); + + builder.AddChildren (classData.GetMembers ().OfType<IEventSymbol> () + .Where (e => e.DeclaredAccessibility == Accessibility.Public || + (e.DeclaredAccessibility == Accessibility.Protected && publicProtectedOnly) || + !publicOnly)); + } + + public override bool HasChildNodes (ITreeBuilder builder, object dataObject) + { + // Checking if a class has member is expensive since it requires loading the whole + // info from the db, so we always return true here. After all 99% of classes will have members + return true; + } + } +} diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/UnresolvedIdStringProvider.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/UnresolvedIdStringProvider.cs index 2319c49e92..c68aaa5d14 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/UnresolvedIdStringProvider.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/UnresolvedIdStringProvider.cs @@ -28,94 +28,111 @@ using System; using System.Text; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; +using Mono.Cecil; +using Mono.Cecil.Rocks; +using MonoDevelop.Core; namespace MonoDevelop.AssemblyBrowser { static class UnresolvedIdStringProvider { - public static string GetIdString (IUnresolvedEntity entity) + public static string GetIdString (IMemberDefinition member) { var sb = new StringBuilder (); - switch (entity.SymbolKind) { - case SymbolKind.TypeDefinition: + switch (member) { + case TypeDefinition type: sb.Append ("T:"); - AppendTypeName (sb, (IUnresolvedTypeDefinition)entity, false, 0); + AppendTypeName (sb, type, false, 0); return sb.ToString (); - case SymbolKind.Field: + case FieldDefinition field: sb.Append ("F:"); break; - case SymbolKind.Property: - case SymbolKind.Indexer: + case PropertyDefinition property: sb.Append ("P:"); break; - case SymbolKind.Event: + case EventDefinition evt: sb.Append ("E:"); break; default: sb.Append ("M:"); break; } - var member = (IUnresolvedMember)entity; - if (member.DeclaringTypeDefinition != null) { - AppendTypeName (sb, member.DeclaringTypeReference, false, 0); + if (member.DeclaringType != null) { + AppendTypeName (sb, member.DeclaringType, false, 0); sb.Append ('.'); } - if (member.IsExplicitInterfaceImplementation && member.Name.IndexOf ('.') < 0 && member.ExplicitInterfaceImplementations.Count == 1) { + + /*if (member.IsExplicitInterfaceImplementation && member.Name.IndexOf ('.') < 0 && member.ExplicitInterfaceImplementations.Count == 1) { AppendTypeName (sb, member.ExplicitInterfaceImplementations [0].DeclaringTypeReference, true, 0); sb.Append ('#'); - } + }*/ sb.Append (member.Name.Replace ('.', '#')); - var method = member as IUnresolvedMethod; - if (method != null && method.TypeParameters.Count > 0) { + var method = member as MethodDefinition; + if (method != null && method.GenericParameters.Count > 0) { sb.Append ("``"); - sb.Append (method.TypeParameters.Count); + sb.Append (method.GenericParameters.Count); } + var parameters = method?.Parameters ?? (member as PropertyDefinition)?.Parameters; - var parameterizedMember = member as IUnresolvedParameterizedMember; - if (parameterizedMember != null && parameterizedMember.Parameters.Count > 0) { - var parameters = parameterizedMember.Parameters; + if (parameters != null) { int start = 0; - if (method is DefaultUnresolvedMethod unresolved && unresolved.IsExtensionMethod) + if (method != null && method.ExplicitThis) start = 1; if (start < parameters.Count) { sb.Append ('('); for (int i = start; i < parameters.Count; i++){ if (i > start) sb.Append (','); - AppendTypeName (sb, parameters[i].Type, false, GetTypeParameterCount(entity)); + AppendTypeName (sb, parameters[i].ParameterType, false, GetTypeParameterCount(member)); } sb.Append (')'); } } - if (member.SymbolKind == SymbolKind.Operator && (member.Name == "op_Implicit" || member.Name == "op_Explicit")) { + if (method != null && method.IsSpecialName && (method.Name == "op_Implicit" || method.Name == "op_Explicit")) { sb.Append ('~'); - AppendTypeName (sb, member.ReturnType, false, 0); + AppendTypeName (sb, method.ReturnType, false, 0); } return sb.ToString (); } - static int GetTypeParameterCount(IUnresolvedEntity entity) + static int GetTypeParameterCount(IMemberDefinition entity) { switch (entity) { - case IUnresolvedTypeDefinition td: - return td.TypeParameters.Count; - case IUnresolvedMethod method: - return method.TypeParameters.Count; + case TypeDefinition td: + return td.GenericParameters.Count; + case MethodDefinition method: + return method.GenericParameters.Count; } return 0; } - static void AppendTypeName (StringBuilder sb, ITypeReference type, bool explicitInterfaceImpl, int outerTypeParameterCount) + static void AppendTypeName (StringBuilder sb, TypeReference type, bool explicitInterfaceImpl, int outerTypeParameterCount) { - switch (type) { - case TypeParameterReference tp: - sb.Append (tp.ToString()); - break; - case ArrayTypeReference array: - AppendTypeName (sb, array.ElementType, explicitInterfaceImpl, outerTypeParameterCount); + if (type is TypeDefinition td) { + if (td.DeclaringType != null) { + AppendTypeName (sb, td.DeclaringType, explicitInterfaceImpl, outerTypeParameterCount); + sb.Append (explicitInterfaceImpl ? '#' : '.'); + sb.Append (td.Name); + } else { + if (explicitInterfaceImpl) + sb.Append (td.FullName.Replace ('.', '#')); + else + sb.Append (td.FullName); + } + return; + } + if (type.IsGenericParameter) { + var gp = type as GenericParameter; + sb.Append ('`'); + sb.Append (gp.Position); + return; + } + if (type.IsArray) { + AppendTypeName (sb, type.GetElementType (), explicitInterfaceImpl, outerTypeParameterCount); sb.Append ('['); - if (array.Dimensions > 1) { - for (int i = 0; i < array.Dimensions; i++) { + var array = type.MakeArrayType (); + if (array.Rank > 1) { + for (int i = 0; i < array.Rank; i++) { if (i > 0) sb.Append (explicitInterfaceImpl ? '@' : ','); if (!explicitInterfaceImpl) @@ -123,72 +140,48 @@ namespace MonoDevelop.AssemblyBrowser } } sb.Append (']'); - break; - case PointerTypeReference ptrtr: - AppendTypeName (sb, ptrtr.ElementType, explicitInterfaceImpl, outerTypeParameterCount); + return; + } + if (type.IsPointer) { + AppendTypeName (sb, type.GetElementType (), explicitInterfaceImpl, outerTypeParameterCount); sb.Append ('*'); - break; - case ByReferenceTypeReference brtr: - AppendTypeName (sb, brtr.ElementType, explicitInterfaceImpl, outerTypeParameterCount); + return; + } + if (type.IsByReference) { + AppendTypeName (sb, type.GetElementType (), explicitInterfaceImpl, outerTypeParameterCount); sb.Append ('@'); - break; - case GetClassTypeReference gctr: - AppendClassTypeReference (sb, gctr.FullTypeName, outerTypeParameterCount); - break; - case IUnresolvedTypeDefinition td: - if (td.DeclaringTypeDefinition != null) { - AppendTypeName (sb, td.DeclaringTypeDefinition, explicitInterfaceImpl, outerTypeParameterCount); - sb.Append (explicitInterfaceImpl ? '#' : '.'); - sb.Append (td.Name); - AppendTypeParameters (sb, td.TypeParameters.Count, outerTypeParameterCount + td.DeclaringTypeDefinition.TypeParameters.Count, explicitInterfaceImpl); - } else { - if (explicitInterfaceImpl) - sb.Append (td.FullName.Replace ('.', '#')); - else - sb.Append (td.FullName); - AppendTypeParameters (sb, td.TypeParameters.Count, outerTypeParameterCount, explicitInterfaceImpl); - } - break; - case ParameterizedTypeReference ptr: - AppendTypeName (sb, ptr.GenericType, explicitInterfaceImpl, int.MaxValue); // type parameter counrt should be omitted because it's determined by the type argument list given - sb.Append ('{'); - for (int i = 0; i < ptr.TypeArguments.Count; i++) { - if (i > 0) - sb.Append(','); - AppendTypeName (sb, ptr.TypeArguments[i], explicitInterfaceImpl, outerTypeParameterCount); - } - sb.Append ('}'); - break; + return; } - } - - static void AppendClassTypeReference (StringBuilder sb, FullTypeName name, int outerTypeParameterCount) - { - if (name.IsNested) { - for (int i = 0; i < name.NestingLevel; i++) { - if (i > 0) - sb.Append ('.'); - sb.Append (name.GetNestedTypeName (i)); - } - } else { - if (!string.IsNullOrEmpty (name.TopLevelTypeName.Namespace)) { - sb.Append (name.TopLevelTypeName.Namespace); - sb.Append ('.'); - } - sb.Append (name.TopLevelTypeName.Name); + if (type.IsByReference) { + AppendClassTypeReference (sb, type, outerTypeParameterCount); + return; } - if (name.TypeParameterCount - outerTypeParameterCount > 0) { - sb.Append ('`'); - sb.Append (name.TypeParameterCount - outerTypeParameterCount); + if (type.IsGenericInstance) { + try { + var genericInstance = type.MakeGenericInstanceType (); + AppendTypeName (sb, genericInstance.ElementType, explicitInterfaceImpl, int.MaxValue); // type parameter count should be omitted because it's determined by the type argument list given + sb.Append ('{'); + for (int i = 0; i < genericInstance.GenericArguments.Count; i++) { + if (i > 0) + sb.Append (','); + AppendTypeName (sb, genericInstance.GenericArguments [i], explicitInterfaceImpl, outerTypeParameterCount); + } + sb.Append ('}'); + return; + } catch (Exception e) { + LoggingService.LogError ("Exception while creating generic instance type id string " + type.FullName, e); + } } + + sb.Append (type.FullName); } - static void AppendTypeParameters (StringBuilder sb, int typeParameterCount, int outerTypeParameterCount, bool explicitInterfaceImpl) + static void AppendClassTypeReference (StringBuilder sb, TypeReference name, int outerTypeParameterCount) { - int tpc = typeParameterCount - outerTypeParameterCount; - if (tpc > 0) { + sb.Append (name.FullName); + if (name.GenericParameters.Count - outerTypeParameterCount > 0) { sb.Append ('`'); - sb.Append (tpc); + sb.Append (name.GenericParameters.Count- outerTypeParameterCount); } } } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/Util/CSharpLanguage.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/Util/CSharpLanguage.cs new file mode 100644 index 0000000000..dcd1c6cf5b --- /dev/null +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/Util/CSharpLanguage.cs @@ -0,0 +1,398 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Resources; + +using ICSharpCode.Decompiler; +using Mono.Cecil; +using ICSharpCode.Decompiler.CSharp; +using ICSharpCode.Decompiler.CSharp.OutputVisitor; +using ICSharpCode.Decompiler.CSharp.Syntax; +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.CSharp.Transforms; + +namespace ICSharpCode.ILSpy +{ + /// <summary> + /// C# decompiler integration into ILSpy. + /// Note: if you're interested in using the decompiler without the ILSpy UI, + /// please directly use the CSharpDecompiler class. + /// </summary> + class CSharpLanguage + { + public static readonly CSharpLanguage Instance = new CSharpLanguage (); + string name = "C#"; + bool showAllMembers = false; + int transformCount = int.MaxValue; + +#if DEBUG + internal static IEnumerable<CSharpLanguage> GetDebugLanguages () + { + var decompiler = new CSharpDecompiler (ModuleDefinition.CreateModule ("Dummy", ModuleKind.Dll), new DecompilerSettings ()); + string lastTransformName = "no transforms"; + int transformCount = 0; + foreach (var transform in decompiler.AstTransforms) { + yield return new CSharpLanguage { + transformCount = transformCount, + name = "C# - " + lastTransformName, + showAllMembers = true + }; + lastTransformName = "after " + transform.GetType ().Name; + transformCount++; + } + yield return new CSharpLanguage { + name = "C# - " + lastTransformName, + showAllMembers = true + }; + } +#endif + + public string Name { + get { return name; } + } + + public string FileExtension { + get { return ".cs"; } + } + + public string ProjectFileExtension { + get { return ".csproj"; } + } + + + CSharpDecompiler CreateDecompiler (ModuleDefinition module, DecompilerSettings options) + { + CSharpDecompiler decompiler = new CSharpDecompiler (module, options); + //decompiler.CancellationToken = options.CancellationToken; + while (decompiler.AstTransforms.Count > transformCount) + decompiler.AstTransforms.RemoveAt (decompiler.AstTransforms.Count - 1); + return decompiler; + } + + void WriteCode (ITextOutput output, DecompilerSettings settings, SyntaxTree syntaxTree, IDecompilerTypeSystem typeSystem) + { + syntaxTree.AcceptVisitor (new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); + TokenWriter tokenWriter = new TextTokenWriter (output, settings, typeSystem) { FoldBraces = settings.FoldBraces, /*ExpandMemberDefinitions = settings.ExpandMemberDefinitions */}; + syntaxTree.AcceptVisitor (new CSharpOutputVisitor (tokenWriter, settings.CSharpFormattingOptions)); + } + + public void DecompileMethod (MethodDefinition method, ITextOutput output, DecompilerSettings options) + { + AddReferenceWarningMessage (method.Module.Assembly, output); + WriteCommentLine (output, TypeToString (method.DeclaringType, includeNamespace: true)); + CSharpDecompiler decompiler = CreateDecompiler (method.Module, options); + if (method.IsConstructor && !method.DeclaringType.IsValueType) { + List<IMemberDefinition> members = CollectFieldsAndCtors (method.DeclaringType, method.IsStatic); + decompiler.AstTransforms.Add (new SelectCtorTransform (decompiler.TypeSystem.Resolve (method))); + WriteCode (output, options, decompiler.Decompile (members), decompiler.TypeSystem); + } else { + WriteCode (output, options, decompiler.Decompile (method), decompiler.TypeSystem); + } + } + + class SelectCtorTransform : IAstTransform + { + readonly IMethod ctor; + readonly HashSet<ISymbol> removedSymbols = new HashSet<ISymbol> (); + + public SelectCtorTransform (IMethod ctor) + { + this.ctor = ctor; + } + + public void Run (AstNode rootNode, TransformContext context) + { + ConstructorDeclaration ctorDecl = null; + foreach (var node in rootNode.Children) { + switch (node) { + case ConstructorDeclaration ctor: + if (ctor.GetSymbol () == this.ctor) { + ctorDecl = ctor; + } else { + // remove other ctors + ctor.Remove (); + removedSymbols.Add (ctor.GetSymbol ()); + } + break; + case FieldDeclaration fd: + // Remove any fields without initializers + if (fd.Variables.All (v => v.Initializer.IsNull)) { + fd.Remove (); + removedSymbols.Add (fd.GetSymbol ()); + } + break; + } + } + if (ctorDecl?.Initializer.ConstructorInitializerType == ConstructorInitializerType.This) { + // remove all fields + foreach (var node in rootNode.Children) { + switch (node) { + case FieldDeclaration fd: + fd.Remove (); + removedSymbols.Add (fd.GetSymbol ()); + break; + } + } + } + foreach (var node in rootNode.Children) { + if (node is Comment && removedSymbols.Contains (node.GetSymbol ())) + node.Remove (); + } + } + } + + public void DecompileProperty (PropertyDefinition property, ITextOutput output, DecompilerSettings options) + { + AddReferenceWarningMessage (property.Module.Assembly, output); + WriteCommentLine (output, TypeToString (property.DeclaringType, includeNamespace: true)); + CSharpDecompiler decompiler = CreateDecompiler (property.Module, options); + WriteCode (output, options, decompiler.Decompile (property), decompiler.TypeSystem); + } + + public void DecompileField (FieldDefinition field, ITextOutput output, DecompilerSettings options) + { + AddReferenceWarningMessage (field.Module.Assembly, output); + WriteCommentLine (output, TypeToString (field.DeclaringType, includeNamespace: true)); + CSharpDecompiler decompiler = CreateDecompiler (field.Module, options); + if (field.IsLiteral) { + WriteCode (output, options, decompiler.Decompile (field), decompiler.TypeSystem); + } else { + List<IMemberDefinition> members = CollectFieldsAndCtors (field.DeclaringType, field.IsStatic); + decompiler.AstTransforms.Add (new SelectFieldTransform (decompiler.TypeSystem.Resolve (field))); + WriteCode (output, options, decompiler.Decompile (members), decompiler.TypeSystem); + } + } + + private static List<IMemberDefinition> CollectFieldsAndCtors (TypeDefinition type, bool isStatic) + { + var members = new List<IMemberDefinition> (); + foreach (var field in type.Fields) { + if (field.IsStatic == isStatic) + members.Add (field); + } + foreach (var ctor in type.Methods) { + if (ctor.IsConstructor && ctor.IsStatic == isStatic) + members.Add (ctor); + } + + return members; + } + + /// <summary> + /// Removes all top-level members except for the specified fields. + /// </summary> + sealed class SelectFieldTransform : IAstTransform + { + readonly IField field; + + public SelectFieldTransform (IField field) + { + this.field = field; + } + + public void Run (AstNode rootNode, TransformContext context) + { + foreach (var node in rootNode.Children) { + switch (node) { + case EntityDeclaration ed: + if (node.GetSymbol () != field) + node.Remove (); + break; + case Comment c: + if (c.GetSymbol () != field) + node.Remove (); + break; + } + } + } + } + + public void DecompileEvent (EventDefinition ev, ITextOutput output, DecompilerSettings options) + { + AddReferenceWarningMessage (ev.Module.Assembly, output); + WriteCommentLine (output, TypeToString (ev.DeclaringType, includeNamespace: true)); + CSharpDecompiler decompiler = CreateDecompiler (ev.Module, options); + WriteCode (output, options, decompiler.Decompile (ev), decompiler.TypeSystem); + } + + public void DecompileType (TypeDefinition type, ITextOutput output, DecompilerSettings options) + { + AddReferenceWarningMessage (type.Module.Assembly, output); + WriteCommentLine (output, TypeToString (type, includeNamespace: true)); + CSharpDecompiler decompiler = CreateDecompiler (type.Module, options); + WriteCode (output, options, decompiler.Decompile (type), decompiler.TypeSystem); + } + + public static string GetPlatformDisplayName (ModuleDefinition module) + { + switch (module.Architecture) { + case TargetArchitecture.I386: + if ((module.Attributes & ModuleAttributes.Preferred32Bit) == ModuleAttributes.Preferred32Bit) + return "AnyCPU (32-bit preferred)"; + else if ((module.Attributes & ModuleAttributes.Required32Bit) == ModuleAttributes.Required32Bit) + return "x86"; + else + return "AnyCPU (64-bit preferred)"; + case TargetArchitecture.AMD64: + return "x64"; + case TargetArchitecture.IA64: + return "Itanium"; + default: + return module.Architecture.ToString (); + } + } + + public static string GetRuntimeDisplayName (ModuleDefinition module) + { + switch (module.Runtime) { + case TargetRuntime.Net_1_0: + return ".NET 1.0"; + case TargetRuntime.Net_1_1: + return ".NET 1.1"; + case TargetRuntime.Net_2_0: + return ".NET 2.0"; + case TargetRuntime.Net_4_0: + return ".NET 4.0"; + } + return null; + } + + void AddReferenceWarningMessage (AssemblyDefinition assembly, ITextOutput output) + { + /*var loadedAssembly = MainWindow.Instance.CurrentAssemblyList.GetAssemblies ().FirstOrDefault (la => la.GetAssemblyDefinitionOrNull () == assembly); + if (loadedAssembly == null || !loadedAssembly.LoadedAssemblyReferencesInfo.HasErrors) + return; + const string line1 = "Warning: Some assembly references could not be loaded. This might lead to incorrect decompilation of some parts,"; + const string line2 = "for ex. property getter/setter access. To get optimal decompilation results, please manually add the references to the list of loaded assemblies."; + if (output is ISmartTextOutput fancyOutput) { + fancyOutput.AddUIElement (() => new StackPanel { + Margin = new Thickness (5), + Orientation = Orientation.Horizontal, + Children = { + new Image { + Width = 32, + Height = 32, + Source = Images.LoadImage(this, "Images/Warning.png") + }, + new TextBlock { + Margin = new Thickness(5, 0, 0, 0), + Text = line1 + Environment.NewLine + line2 + } + } + }); + fancyOutput.WriteLine (); + fancyOutput.AddButton (Images.ViewCode, "Show assembly load log", delegate { + MainWindow.Instance.SelectNode (MainWindow.Instance.FindTreeNode (assembly).Children.OfType<ReferenceFolderTreeNode> ().First ()); + }); + fancyOutput.WriteLine (); + } else { + WriteCommentLine (output, line1); + WriteCommentLine (output, line2); + }*/ + } + + + public string TypeToString (TypeReference type, bool includeNamespace, ICustomAttributeProvider typeAttributes = null) + { + ConvertTypeOptions options = ConvertTypeOptions.IncludeTypeParameterDefinitions; + if (includeNamespace) + options |= ConvertTypeOptions.IncludeNamespace; + + return TypeToString (options, type, typeAttributes); + } + + string TypeToString (ConvertTypeOptions options, TypeReference type, ICustomAttributeProvider typeAttributes = null) + { + AstType astType = CSharpDecompiler.ConvertType (type, typeAttributes, options); + + StringWriter w = new StringWriter (); + if (type.IsByReference) { + ParameterDefinition pd = typeAttributes as ParameterDefinition; + if (pd != null && (!pd.IsIn && pd.IsOut)) + w.Write ("out "); + else + w.Write ("ref "); + + if (astType is ComposedType && ((ComposedType)astType).PointerRank > 0) + ((ComposedType)astType).PointerRank--; + } + + astType.AcceptVisitor (new CSharpOutputVisitor (w, TypeToStringFormattingOptions)); + return w.ToString (); + } + + static readonly CSharpFormattingOptions TypeToStringFormattingOptions = FormattingOptionsFactory.CreateEmpty (); + + public string FormatPropertyName (PropertyDefinition property, bool? isIndexer) + { + if (property == null) + throw new ArgumentNullException (nameof (property)); + + if (!isIndexer.HasValue) { + isIndexer = property.IsIndexer (); + } + if (isIndexer.Value) { + var buffer = new System.Text.StringBuilder (); + var accessor = property.GetMethod ?? property.SetMethod; + if (accessor.HasOverrides) { + var declaringType = accessor.Overrides [0].DeclaringType; + buffer.Append (TypeToString (declaringType, includeNamespace: true)); + buffer.Append (@"."); + } + buffer.Append (@"this["); + bool addSeparator = false; + foreach (var p in property.Parameters) { + if (addSeparator) + buffer.Append (@", "); + else + addSeparator = true; + buffer.Append (TypeToString (p.ParameterType, includeNamespace: true)); + } + buffer.Append (@"]"); + return buffer.ToString (); + } else + return property.Name; + } + + public string FormatMethodName (MethodDefinition method) + { + if (method == null) + throw new ArgumentNullException ("method"); + + return (method.IsConstructor) ? FormatTypeName (method.DeclaringType) : method.Name; + } + + public string FormatTypeName (TypeDefinition type) + { + if (type == null) + throw new ArgumentNullException ("type"); + + return TypeToString (ConvertTypeOptions.DoNotUsePrimitiveTypeNames | ConvertTypeOptions.IncludeTypeParameterDefinitions, type); + } + + public void WriteCommentLine (ITextOutput output, string comment) + { + output.WriteLine ("// " + comment); + } + } +} diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/packages.config b/main/src/addins/MonoDevelop.AssemblyBrowser/packages.config index f9194282d2..66c14c17d2 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/packages.config +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/packages.config @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?>
-<packages>
- <package id="Mono.Cecil" version="0.10.0-beta6" targetFramework="net45" />
+<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="Mono.Cecil" version="0.10.0-beta6" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/main/src/addins/MonoDevelop.CodeMetrics/MonoDevelop.CodeMetrics.csproj b/main/src/addins/MonoDevelop.CodeMetrics/MonoDevelop.CodeMetrics.csproj index 00b9536f1f..7e2c74d859 100644 --- a/main/src/addins/MonoDevelop.CodeMetrics/MonoDevelop.CodeMetrics.csproj +++ b/main/src/addins/MonoDevelop.CodeMetrics/MonoDevelop.CodeMetrics.csproj @@ -73,16 +73,6 @@ <Name>Mono.TextEditor</Name> <Private>False</Private> </ProjectReference> - <ProjectReference Include="..\..\..\contrib\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj"> - <Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project> - <Name>ICSharpCode.NRefactory</Name> - <Private>False</Private> - </ProjectReference> - <ProjectReference Include="..\..\..\contrib\ICSharpCode.NRefactory.CSharp\ICSharpCode.NRefactory.CSharp.csproj"> - <Project>{53DCA265-3C3C-42F9-B647-F72BA678122B}</Project> - <Name>ICSharpCode.NRefactory.CSharp</Name> - <Private>False</Private> - </ProjectReference> </ItemGroup> <ItemGroup> <EmbeddedResource Include="gtk-gui\gui.stetic"> diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/Gui/ResultsEditorExtension.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/Gui/ResultsEditorExtension.cs index 8f8761b16d..0352ac3924 100644 --- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/Gui/ResultsEditorExtension.cs +++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/Gui/ResultsEditorExtension.cs @@ -347,6 +347,12 @@ namespace MonoDevelop.AnalysisCore.Gui if (!enumerator.MoveNext ()) { ext.tasks [id] = builder.ToImmutable (); ext.OnTasksUpdated (EventArgs.Empty); + // remove remaining old markers + while (oldMarkers > 0) { + editor.RemoveMarker (ext.markers [id].Dequeue ()); + oldMarkers--; + } + return false; } if (cancellationToken.IsCancellationRequested) diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs index 0b520aa618..19ab118b73 100644 --- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs +++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs @@ -325,10 +325,6 @@ namespace MonoDevelop.CodeActions Editor.CaretPositionChanged += HandleCaretPositionChanged; Editor.TextChanged += Editor_TextChanged; Editor.EndAtomicUndoOperation += Editor_EndAtomicUndoOperation; - - DocumentContext.RoslynWorkspace.Options = DocumentContext.RoslynWorkspace.Options.WithChangedOption ( - Microsoft.CodeAnalysis.SymbolSearch.SymbolSearchOptions.SuggestForTypesInNuGetPackages, LanguageNames.CSharp, true); - } void Editor_EndAtomicUndoOperation (object sender, EventArgs e) diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/CodeGenerationPanel.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/CodeGenerationPanel.cs index 36634ef0d9..5ddb914cf5 100644 --- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/CodeGenerationPanel.cs +++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/CodeGenerationPanel.cs @@ -24,6 +24,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editing; using MonoDevelop.Components; using MonoDevelop.Core; using MonoDevelop.Ide.Gui.Dialogs; @@ -53,9 +54,16 @@ namespace MonoDevelop.Refactoring CheckBox placeSystemNamespaceFirst; CheckBox separateImportDirectiveGroups; + ConfigurationProperty<bool> placeSystemNamespaceFirstProp; + ConfigurationProperty<bool> separateImportDirectiveGroupsProp; + public CodeGenerationPanelWidget (string languageName) { this.languageName = languageName; + + placeSystemNamespaceFirstProp = Ide.IdeApp.Preferences.Roslyn.For (languageName).PlaceSystemNamespaceFirst; + separateImportDirectiveGroupsProp = Ide.IdeApp.Preferences.Roslyn.For (languageName).PlaceSystemNamespaceFirst; + var hBox = new HBox (); hBox.PackStart (new Label { Markup = GettextCatalog.GetString ("Organize Usings") }); hBox.PackStart (new HSeparator (), true, true); @@ -63,14 +71,14 @@ namespace MonoDevelop.Refactoring this.PackStart (hBox); this.PackStart (placeSystemNamespaceFirst = new CheckBox (GettextCatalog.GetString ("Place 'System' directives first when sorting usings"))); this.PackStart (separateImportDirectiveGroups = new CheckBox (GettextCatalog.GetString ("Separate using groups when sorting"))); - placeSystemNamespaceFirst.Active = PropertyService.Get (languageName + ".PlaceSystemNamespaceFirst", true); - separateImportDirectiveGroups.Active = PropertyService.Get (languageName + ".SeparateImportDirectiveGroups", false); + placeSystemNamespaceFirst.Active = placeSystemNamespaceFirstProp.Value; + separateImportDirectiveGroups.Active = separateImportDirectiveGroupsProp.Value; } public void ApplyChanges () { - PropertyService.Set (languageName + ".PlaceSystemNamespaceFirst", placeSystemNamespaceFirst.Active); - PropertyService.Set (languageName + ".SeparateImportDirectiveGroups", separateImportDirectiveGroups.Active); + placeSystemNamespaceFirstProp.Value = placeSystemNamespaceFirst.Active; + separateImportDirectiveGroupsProp.Value = separateImportDirectiveGroups.Active; } } } diff --git a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MdTextViewLineCollection.MdTextViewLine.cs b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MdTextViewLineCollection.MdTextViewLine.cs index 318a16dca9..a0005cb07a 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MdTextViewLineCollection.MdTextViewLine.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MdTextViewLineCollection.MdTextViewLine.cs @@ -27,6 +27,7 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using Microsoft.VisualStudio.Language.Intellisense.Implementation; using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Formatting;
using Microsoft.VisualStudio.Text.Implementation; @@ -159,8 +160,7 @@ namespace Mono.TextEditor { var y = textEditor.LocationToPoint(textEditor.OffsetToLocation(lineSpan.Start)).Y; var loc = textEditor.PointToLocation(xCoordinate, y); - var pos = textEditor.LocationToOffset(loc); - return new SnapshotPoint(Snapshot, pos); + return Snapshot.GetSnapshotPoint (loc.Line, loc.Column); } public SnapshotPoint? GetBufferPositionFromXCoordinate(double xCoordinate) diff --git a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.cs b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.cs index 989a99f9f8..ac69266d8e 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.cs @@ -82,6 +82,7 @@ namespace Mono.TextEditor internal MonoTextEditor (TextDocument doc, ITextEditorOptions options, EditMode initialMode) { + this.Direction = TextDirection.Ltr; uiThread = Thread.CurrentThread; GtkWorkarounds.FixContainerLeak (this); WidgetFlags |= WidgetFlags.NoWindow; diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/MDUtils.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/MDUtils.cs index cd005a24e3..1fbdfa3895 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/MDUtils.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/MDUtils.cs @@ -224,5 +224,45 @@ namespace Microsoft.VisualStudio.Language.Intellisense.Implementation public const string XmlLiteralProcessingInstruction = "xml literal - processing instruction";
public const string XmlLiteralText = "xml literal - text";
}
+
+ /// <summary>
+ /// Gets a snapshot point out of a monodevelop line/column pair or null.
+ /// </summary>
+ /// <returns>The snapshot point or null if the coordinate is invalid.</returns>
+ /// <param name="snapshot">The snapshot.</param>
+ /// <param name="line">Line number (1 based).</param>
+ /// <param name="column">Column number (1 based).</param>
+ public static SnapshotPoint? GetSnapshotPoint (this ITextSnapshot snapshot, int line, int column)
+ {
+ if (TryGetSnapshotPoint (snapshot, line, column, out var snapshotPoint))
+ return snapshotPoint;
+ return null;
+ }
+
+ /// <summary>
+ /// Tries to get a snapshot point out of a monodevelop line/column pair.
+ /// </summary>
+ /// <returns><c>true</c>, if get snapshot point was set, <c>false</c> otherwise.</returns>
+ /// <param name="snapshot">The snapshot.</param>
+ /// <param name="line">Line number (1 based).</param>
+ /// <param name="column">Column number (1 based).</param>
+ /// <param name="snapshotPoint">The snapshot point if return == true.</param>
+ public static bool TryGetSnapshotPoint (this ITextSnapshot snapshot, int line, int column, out SnapshotPoint snapshotPoint)
+ {
+ if (line < 1 || line > snapshot.LineCount) {
+ snapshotPoint = default (SnapshotPoint);
+ return false;
+ }
+
+ var lineSegment = snapshot.GetLineFromLineNumber (line - 1);
+ if (column < 1 || column > lineSegment.Length) {
+ snapshotPoint = default (SnapshotPoint);
+ return false;
+ }
+
+ snapshotPoint = new SnapshotPoint (snapshot, lineSegment.Start.Position + column - 1);
+ return true;
+ }
}
+
}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TagBasedSyntaxHighlighting.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TagBasedSyntaxHighlighting.cs index 282eccdaac..b479ef5883 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TagBasedSyntaxHighlighting.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TagBasedSyntaxHighlighting.cs @@ -1,7 +1,7 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// +//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+//
using System;
using System.Linq;
using System.Collections.Generic;
@@ -24,306 +24,361 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Tagging;
using Microsoft.VisualStudio.Utilities;
using Microsoft.VisualStudio.Text.Editor;
-using static Microsoft.VisualStudio.Language.Intellisense.Implementation.MDUtils; -using Microsoft.VisualStudio.Language.StandardClassification; -using MonoDevelop.Core.Text; - +using static Microsoft.VisualStudio.Language.Intellisense.Implementation.MDUtils;
+using Microsoft.VisualStudio.Language.StandardClassification;
+using MonoDevelop.Core.Text;
+using MonoDevelop.Ide.Tasks;
+
namespace Microsoft.VisualStudio.Platform
-{ - [Export (typeof (ITagBasedSyntaxHighlightingFactory))] - internal sealed class TagBasedSyntaxHighlightingFactory : ITagBasedSyntaxHighlightingFactory - { - public ISyntaxHighlighting CreateSyntaxHighlighting (ITextView textView) - { - return new TagBasedSyntaxHighlighting (textView, null); - } - - public ISyntaxHighlighting CreateSyntaxHighlighting (ITextView textView, string defaultScope) - { - return new TagBasedSyntaxHighlighting (textView, defaultScope); - } - } - - internal sealed class TagBasedSyntaxHighlighting : ISyntaxHighlighting - { +{
+ [Export (typeof (ITagBasedSyntaxHighlightingFactory))]
+ internal sealed class TagBasedSyntaxHighlightingFactory : ITagBasedSyntaxHighlightingFactory
+ {
+ public ISyntaxHighlighting CreateSyntaxHighlighting (ITextView textView)
+ {
+ return new TagBasedSyntaxHighlighting (textView, null);
+ }
+
+ public ISyntaxHighlighting CreateSyntaxHighlighting (ITextView textView, string defaultScope)
+ {
+ return new TagBasedSyntaxHighlighting (textView, defaultScope);
+ }
+ }
+
+ internal sealed class TagBasedSyntaxHighlighting : ISyntaxHighlighting
+ {
private ITextView textView { get; }
private IAccurateClassifier classifier { get; set; }
- readonly Dictionary<string, ScopeStack> classificationMap; - private MonoDevelop.Ide.Editor.ITextDocument textDocument { get; } - - internal TagBasedSyntaxHighlighting (ITextView textView, string defaultScope) - { - this.textView = textView; - this.textDocument = textView.GetTextEditor (); + readonly Dictionary<string, ScopeStack> classificationMap;
+ private MonoDevelop.Ide.Editor.ITextDocument textDocument { get; }
+
+ internal TagBasedSyntaxHighlighting (ITextView textView, string defaultScope)
+ {
+ this.textView = textView;
+ this.textDocument = textView.GetTextEditor ();
if (defaultScope != null)
classificationMap = GetClassificationMap (defaultScope);
}
- - public Task<HighlightedLine> GetHighlightedLineAsync (IDocumentLine line, CancellationToken cancellationToken) - { - ITextSnapshotLine snapshotLine = (line as Mono.TextEditor.TextDocument.DocumentLineFromTextSnapshotLine)?.Line; - if ((this.classifier == null) || (snapshotLine == null)) { - return Task.FromResult (new HighlightedLine (line, new [] { new ColoredSegment (0, line.Length, ScopeStack.Empty) })); - } - List<ColoredSegment> coloredSegments = new List<ColoredSegment> (); - - SnapshotSpan snapshotSpan = new SnapshotSpan(textView.TextBuffer.CurrentSnapshot, snapshotLine.Extent.Span); - int start = snapshotSpan.Start.Position; - int end = snapshotSpan.End.Position; - - IList<ClassificationSpan> classifications = this.classifier.GetClassificationSpans (snapshotSpan); - - int lastClassifiedOffsetEnd = start; +
+ public Task<HighlightedLine> GetHighlightedLineAsync (IDocumentLine line, CancellationToken cancellationToken)
+ {
+ ITextSnapshotLine snapshotLine = (line as Mono.TextEditor.TextDocument.DocumentLineFromTextSnapshotLine)?.Line;
+ if ((this.classifier == null) || (snapshotLine == null)) {
+ return Task.FromResult (new HighlightedLine (line, new [] { new ColoredSegment (0, line.Length, ScopeStack.Empty) }));
+ }
+ List<ColoredSegment> coloredSegments = new List<ColoredSegment> ();
+
+ SnapshotSpan snapshotSpan = new SnapshotSpan(textView.TextBuffer.CurrentSnapshot, snapshotLine.Extent.Span);
+ int start = snapshotSpan.Start.Position;
+ int end = snapshotSpan.End.Position;
+
+ IList<ClassificationSpan> classifications = this.classifier.GetClassificationSpans (snapshotSpan);
+
+ int lastClassifiedOffsetEnd = start;
ScopeStack scopeStack;
- foreach (ClassificationSpan curSpan in classifications) { - if (curSpan.Span.Start > lastClassifiedOffsetEnd) { - scopeStack = new ScopeStack (EditorThemeColors.Foreground); - ColoredSegment whitespaceSegment = new ColoredSegment (lastClassifiedOffsetEnd - start, curSpan.Span.Start - lastClassifiedOffsetEnd, scopeStack); - coloredSegments.Add (whitespaceSegment); + foreach (ClassificationSpan curSpan in classifications) {
+ if (curSpan.Span.Start > lastClassifiedOffsetEnd) {
+ scopeStack = new ScopeStack (EditorThemeColors.Foreground);
+ ColoredSegment whitespaceSegment = new ColoredSegment (lastClassifiedOffsetEnd - start, curSpan.Span.Start - lastClassifiedOffsetEnd, scopeStack);
+ coloredSegments.Add (whitespaceSegment);
+ }
+
+ scopeStack = GetScopeStackFromClassificationType (curSpan.ClassificationType);
+ if (scopeStack.Peek ().StartsWith ("comment", StringComparison.Ordinal)) {
+ ScanAndAddComment (coloredSegments, start, scopeStack, curSpan);
+ } else {
+ var curColoredSegment = new ColoredSegment (curSpan.Span.Start - start, curSpan.Span.Length, scopeStack);
+ coloredSegments.Add (curColoredSegment);
+ }
+
+ lastClassifiedOffsetEnd = curSpan.Span.End;
+ }
+
+ if (end > lastClassifiedOffsetEnd) {
+ scopeStack = new ScopeStack (EditorThemeColors.Foreground);
+ ColoredSegment whitespaceSegment = new ColoredSegment (lastClassifiedOffsetEnd - start, end - lastClassifiedOffsetEnd, scopeStack);
+ coloredSegments.Add (whitespaceSegment);
+ }
+
+ return Task.FromResult(new HighlightedLine (line, coloredSegments));
+ }
+ #region Tag Comment Scanning
+
+ void ScanAndAddComment (List<ColoredSegment> coloredSegments, int startOffset, ScopeStack commentScopeStack, ClassificationSpan classificationSpan)
+ {
+ int lastClassifiedOffset = classificationSpan.Span.Start;
+ try {
+ // Scan comments for tag highlighting
+ var text = textView.TextSnapshot.GetText (classificationSpan.Span);
+ int idx = 0, oldIdx = 0;
+
+ while ((idx = FindNextCommentTagIndex (text, idx, out string commentTag)) >= 0) {
+ var headSpanLength = idx - oldIdx;
+ if (headSpanLength > 0) {
+ var headSegment = new ColoredSegment (lastClassifiedOffset - startOffset, headSpanLength, commentScopeStack);
+ lastClassifiedOffset += headSpanLength;
+ coloredSegments.Add (headSegment);
+ }
+ var highlightSegment = new ColoredSegment (lastClassifiedOffset - startOffset, commentTag.Length, commentScopeStack.Push ("markup.other"));
+ coloredSegments.Add (highlightSegment);
+ idx += commentTag.Length;
+ lastClassifiedOffset += commentTag.Length;
+ oldIdx = idx;
+ }
+ } catch (Exception e) {
+ LoggingService.LogError ("Error while scanning comment tags.", e);
+ }
+ int tailSpanLength = classificationSpan.Span.End - lastClassifiedOffset;
+ if (tailSpanLength > 0) {
+ var tailSpan = new ColoredSegment (lastClassifiedOffset - startOffset, tailSpanLength, commentScopeStack);
+ coloredSegments.Add (tailSpan);
+ }
+ }
+
+ static int FindNextCommentTagIndex (string text, int startIndex, out string commentTag)
+ {
+ var foundIndex = -1;
+ commentTag = null;
+ foreach (var tag in CommentTag.SpecialCommentTags) {
+ var i = text.IndexOf (tag.Tag, startIndex, StringComparison.OrdinalIgnoreCase);
+ if (i < 0)
+ continue;
+ if (i < foundIndex || foundIndex < 0) {
+ foundIndex = i;
+ commentTag = tag.Tag;
+ }
+ }
+ return foundIndex;
+ }
+
+ #endregion
+
+ public async Task<ScopeStack> GetScopeStackAsync (int offset, CancellationToken cancellationToken)
+ {
+ var line = textDocument.GetLineByOffset (offset);
+ var highligthedLine = await GetHighlightedLineAsync (line, cancellationToken).ConfigureAwait (false);
+ offset -= line.Offset;
+ foreach (var segment in highligthedLine.Segments) {
+ if (segment.Offset <= offset && segment.EndOffset >= offset)
+ return segment.ScopeStack;
+ }
+ return ScopeStack.Empty;
+ }
+
+ private EventHandler<LineEventArgs> _highlightingStateChanged;
+ public event EventHandler<LineEventArgs> HighlightingStateChanged {
+ add {
+ lock (this) {
+ _highlightingStateChanged += value;
}
- scopeStack = GetScopeStackFromClassificationType (curSpan.ClassificationType); - ColoredSegment curColoredSegment = new ColoredSegment (curSpan.Span.Start - start, curSpan.Span.Length, scopeStack); - coloredSegments.Add (curColoredSegment); - - lastClassifiedOffsetEnd = curSpan.Span.End; - } - - if (end > lastClassifiedOffsetEnd) { - scopeStack = new ScopeStack (EditorThemeColors.Foreground); - ColoredSegment whitespaceSegment = new ColoredSegment (lastClassifiedOffsetEnd - start, end - lastClassifiedOffsetEnd, scopeStack); - coloredSegments.Add (whitespaceSegment); - } - - return Task.FromResult(new HighlightedLine (line, coloredSegments)); - } - - public async Task<ScopeStack> GetScopeStackAsync (int offset, CancellationToken cancellationToken) - { - var line = textDocument.GetLineByOffset (offset); - var highligthedLine = await GetHighlightedLineAsync (line, cancellationToken).ConfigureAwait (false); - offset -= line.Offset; - foreach (var segment in highligthedLine.Segments) { - if (segment.Offset <= offset && segment.EndOffset >= offset) - return segment.ScopeStack; - } - return ScopeStack.Empty; - } - - private EventHandler<LineEventArgs> _highlightingStateChanged; - public event EventHandler<LineEventArgs> HighlightingStateChanged { - add { - lock (this) { - _highlightingStateChanged += value; - } - if (this.classifier == null) {
- this.classifier = PlatformCatalog.Instance.ViewClassifierAggregatorService.GetClassifier (this.textView) as IAccurateClassifier; - this.classifier.ClassificationChanged += this.OnClassificationChanged; - } - } - - remove { - bool dispose = false; - lock (this) { - _highlightingStateChanged -= value; - dispose = _highlightingStateChanged == null; - } - - if (dispose && (this.classifier != null)) { - this.classifier.ClassificationChanged -= this.OnClassificationChanged; - (this.classifier as IDisposable)?.Dispose (); - this.classifier = null; - } - } - } - - private void OnClassificationChanged (object sender, ClassificationChangedEventArgs args) - { - var handler = _highlightingStateChanged; - if (handler != null) { - foreach (Mono.TextEditor.MdTextViewLineCollection.MdTextViewLine line in textView.TextViewLines) { - if (line.Start.Position > args.ChangeSpan.End.Position || line.End.Position < args.ChangeSpan.Start) - continue; - var oldSegments = line.layoutWrapper.HighlightedLine.Segments; - var newSegments = GetHighlightedLineAsync (line.line, CancellationToken.None).Result.Segments; - if (oldSegments.Count != newSegments.Count) { - handler (this, new LineEventArgs (line.line)); - continue; - } - for (int i = 0; i < oldSegments.Count; i++) { - if (newSegments [i].ColorStyleKey != oldSegments [i].ColorStyleKey) { - handler (this, new LineEventArgs (line.line)); - break; - } - } - } - } + this.classifier = PlatformCatalog.Instance.ViewClassifierAggregatorService.GetClassifier (this.textView) as IAccurateClassifier;
+ this.classifier.ClassificationChanged += this.OnClassificationChanged;
+ }
+ }
+
+ remove {
+ bool dispose = false;
+ lock (this) {
+ _highlightingStateChanged -= value;
+ dispose = _highlightingStateChanged == null;
+ }
+
+ if (dispose && (this.classifier != null)) {
+ this.classifier.ClassificationChanged -= this.OnClassificationChanged;
+ (this.classifier as IDisposable)?.Dispose ();
+ this.classifier = null;
+ }
+ }
}
- - Dictionary<IClassificationType, ScopeStack> classificationTypeToScopeCache = new Dictionary<IClassificationType, ScopeStack> (); - static ScopeStack defaultScopeStack = new ScopeStack (EditorThemeColors.Foreground); -
- private ScopeStack GetScopeStackFromClassificationType (IClassificationType classificationType) - { - if (classificationTypeToScopeCache.TryGetValue (classificationType, out var cachedScope)) - return cachedScope; - ScopeStack scope = null; - void ProcessClassificationType(IClassificationType classification) - { - foreach (var baseType in classification.BaseTypes) { - ProcessClassificationType (baseType); - } - //This comparision with Identifier and Keyword is very hacky - //what we are doing here is, making sure anything has greater priorty over - //this two Identifer/Keyword, I came to this two from - //https://github.com/dotnet/roslyn/blob/88d1bd1/src/EditorFeatures/Core.Wpf/Classification/ClassificationTypeFormatDefinitions.cs - //Which orders different classifications by priority... - //This is needed so Semantical classification for Interface or Class is greater then Identifier from Syntactical classifeer - if (scope != null && (classification.Classification == PredefinedClassificationTypeNames.Identifier || classification.Classification == PredefinedClassificationTypeNames.Keyword)) - return; - if (classificationMap != null && classificationMap.TryGetValue (classification.Classification, out var mappedScope)) { - scope = mappedScope; - return; - } - var styleName = GetStyleNameFromClassificationName (classification.Classification); - if (styleName == null) - return; - scope = new ScopeStack (styleName); - } - ProcessClassificationType (classificationType); +
+ private void OnClassificationChanged (object sender, ClassificationChangedEventArgs args)
+ {
+ var handler = _highlightingStateChanged;
+ if (handler != null) {
+ foreach (Mono.TextEditor.MdTextViewLineCollection.MdTextViewLine line in textView.TextViewLines) {
+ if (line.Start.Position > args.ChangeSpan.End.Position || line.End.Position < args.ChangeSpan.Start)
+ continue;
+ var oldSegments = line.layoutWrapper.HighlightedLine.Segments;
+ var newSegments = GetHighlightedLineAsync (line.line, CancellationToken.None).Result.Segments;
+ if (oldSegments.Count != newSegments.Count) {
+ handler (this, new LineEventArgs (line.line));
+ continue;
+ }
+ for (int i = 0; i < oldSegments.Count; i++) {
+ if (newSegments [i].ColorStyleKey != oldSegments [i].ColorStyleKey) {
+ handler (this, new LineEventArgs (line.line));
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ Dictionary<IClassificationType, ScopeStack> classificationTypeToScopeCache = new Dictionary<IClassificationType, ScopeStack> ();
+ static ScopeStack defaultScopeStack = new ScopeStack (EditorThemeColors.Foreground);
+
+ private ScopeStack GetScopeStackFromClassificationType (IClassificationType classificationType)
+ {
+ if (classificationTypeToScopeCache.TryGetValue (classificationType, out var cachedScope))
+ return cachedScope;
+ ScopeStack scope = null;
+ void ProcessClassificationType(IClassificationType classification)
+ {
+ foreach (var baseType in classification.BaseTypes) {
+ ProcessClassificationType (baseType);
+ }
+ //This comparision with Identifier and Keyword is very hacky
+ //what we are doing here is, making sure anything has greater priorty over
+ //this two Identifer/Keyword, I came to this two from
+ //https://github.com/dotnet/roslyn/blob/88d1bd1/src/EditorFeatures/Core.Wpf/Classification/ClassificationTypeFormatDefinitions.cs
+ //Which orders different classifications by priority...
+ //This is needed so Semantical classification for Interface or Class is greater then Identifier from Syntactical classifeer
+ if (scope != null && (classification.Classification == PredefinedClassificationTypeNames.Identifier || classification.Classification == PredefinedClassificationTypeNames.Keyword))
+ return;
+ if (classificationMap != null && classificationMap.TryGetValue (classification.Classification, out var mappedScope)) {
+ scope = mappedScope;
+ return;
+ }
+ var styleName = GetStyleNameFromClassificationName (classification.Classification);
+ if (styleName == null)
+ return;
+ scope = new ScopeStack (styleName);
+ }
+ ProcessClassificationType (classificationType);
return classificationTypeToScopeCache [classificationType] = scope ?? defaultScopeStack;
- } - - private string GetStyleNameFromClassificationName (string classificationName) - { - string styleName = null; - - // MONO: TODO: get this from the EditorFormat? - switch (classificationName) { - // MONO: TODO: Make each language MEF export this knowledge? - - // CSS Entries - case "CSS Comment": - styleName = "comment.block.css"; - break; - case "CSS Keyword": - styleName = "keyword.other.css"; - break; - case "CSS Selector": - styleName = "entity.name.tag.css"; - break; - case "CSS Property Name": - styleName = "support.type.property-name.css"; - break; - case "CSS Property Value": - styleName = "support.constant.property-value.css"; - break; - case "CSS String Value": - styleName = "string.quoted.double.css"; - break; - - // HTML Entries - case "HTML Attribute Name": - styleName = "entity.other.attribute-name.html"; - break; - case "HTML Attribute Value": - styleName = "string.unquoted.html"; - break; - case "HTML Comment": - styleName = "comment.block.html"; - break; - case "HTML Element Name": - styleName = "entity.name.tag.html"; - break; - case "HTML Entity": - styleName = "constant.character.entity.html"; - break; - case "HTML Operator": - styleName = "punctuation.separator.key-value.html"; - break; - case "HTML Server-Side Script": - //styleName = "punctuation.section.embedded.begin"; // suggested by mike, does nothing - //styleName = "punctuation.section.embedded.begin.cs"; // suggested by mike, does nothing - styleName = "meta.preprocessor.source.cs"; // TODO: Find a name to use here - //styleName = style.HtmlServerSideScript.Name; - break; - case "HTML Tag Delimiter": - styleName = "punctuation.definition.tag.begin.html"; - break; - case "RazorCode": - //styleName = style.RazorCode.Name; - break; - case "RazorTagHelperAttribute": - styleName = "markup.bold"; - break; - case "RazorTagHelperElement": - styleName = "markup.bold"; - break; - - // JSON Entries - case "operator": - styleName = "meta.structure.dictionary.value.json"; - break; - case "string": - styleName = "string.quoted.double.json"; - break; - case "keyword": - styleName = "constant.language.json"; - break; - case "number": - styleName = "constant.numeric.json"; - break; - case "comment": - styleName = "comment.block.json"; - break; - case "JSON Property Name": - styleName = "support.type.property-name.json"; - break; - - // LESS Entries - case "LessCssVariableDeclaration": - styleName = "variable.other.less"; - break; - case "LessCssVariableReference": - styleName = "variable.other.less"; - break; - case "LessCssNamespaceReference": - styleName = "variable.other.less"; - break; - case "LessCssMixinReference": - styleName = "variable.other.less"; - break; - case "LessCssMixinDeclaration": - styleName = "variable.other.less"; - break; - case "LessCssKeyword": - styleName = "punctuation.definition.keyword.css"; - break; - - // Scss Entries - case "ScssMixinReference": - styleName = "variable.other.less"; - break; - case "ScssMixinDeclaration": - styleName = "variable.other.less"; - break; - case "ScssVariableDeclaration": - styleName = "variable.other.less"; - break; - case "ScssVariableReference": - styleName = "variable.other.less"; - break; - default: - // If the stylename looks like a textmate style, just use it - if (classificationName.IndexOf ('.') >= 0) { - styleName = classificationName; - } - - break; - } - - return styleName; + }
+
+ private string GetStyleNameFromClassificationName (string classificationName)
+ {
+ string styleName = null;
+
+ // MONO: TODO: get this from the EditorFormat?
+ switch (classificationName) {
+ // MONO: TODO: Make each language MEF export this knowledge?
+
+ // CSS Entries
+ case "CSS Comment":
+ styleName = "comment.block.css";
+ break;
+ case "CSS Keyword":
+ styleName = "keyword.other.css";
+ break;
+ case "CSS Selector":
+ styleName = "entity.name.tag.css";
+ break;
+ case "CSS Property Name":
+ styleName = "support.type.property-name.css";
+ break;
+ case "CSS Property Value":
+ styleName = "support.constant.property-value.css";
+ break;
+ case "CSS String Value":
+ styleName = "string.quoted.double.css";
+ break;
+
+ // HTML Entries
+ case "HTML Attribute Name":
+ styleName = "entity.other.attribute-name.html";
+ break;
+ case "HTML Attribute Value":
+ styleName = "string.unquoted.html";
+ break;
+ case "HTML Comment":
+ styleName = "comment.block.html";
+ break;
+ case "HTML Element Name":
+ styleName = "entity.name.tag.html";
+ break;
+ case "HTML Entity":
+ styleName = "constant.character.entity.html";
+ break;
+ case "HTML Operator":
+ styleName = "punctuation.separator.key-value.html";
+ break;
+ case "HTML Server-Side Script":
+ //styleName = "punctuation.section.embedded.begin"; // suggested by mike, does nothing
+ //styleName = "punctuation.section.embedded.begin.cs"; // suggested by mike, does nothing
+ styleName = "meta.preprocessor.source.cs"; // TODO: Find a name to use here
+ //styleName = style.HtmlServerSideScript.Name;
+ break;
+ case "HTML Tag Delimiter":
+ styleName = "punctuation.definition.tag.begin.html";
+ break;
+ case "RazorCode":
+ //styleName = style.RazorCode.Name;
+ break;
+ case "RazorTagHelperAttribute":
+ styleName = "markup.bold";
+ break;
+ case "RazorTagHelperElement":
+ styleName = "markup.bold";
+ break;
+
+ // JSON Entries
+ case "operator":
+ styleName = "meta.structure.dictionary.value.json";
+ break;
+ case "string":
+ styleName = "string.quoted.double.json";
+ break;
+ case "keyword":
+ styleName = "constant.language.json";
+ break;
+ case "number":
+ styleName = "constant.numeric.json";
+ break;
+ case "comment":
+ styleName = "comment.block.json";
+ break;
+ case "JSON Property Name":
+ styleName = "support.type.property-name.json";
+ break;
+
+ // LESS Entries
+ case "LessCssVariableDeclaration":
+ styleName = "variable.other.less";
+ break;
+ case "LessCssVariableReference":
+ styleName = "variable.other.less";
+ break;
+ case "LessCssNamespaceReference":
+ styleName = "variable.other.less";
+ break;
+ case "LessCssMixinReference":
+ styleName = "variable.other.less";
+ break;
+ case "LessCssMixinDeclaration":
+ styleName = "variable.other.less";
+ break;
+ case "LessCssKeyword":
+ styleName = "punctuation.definition.keyword.css";
+ break;
+
+ // Scss Entries
+ case "ScssMixinReference":
+ styleName = "variable.other.less";
+ break;
+ case "ScssMixinDeclaration":
+ styleName = "variable.other.less";
+ break;
+ case "ScssVariableDeclaration":
+ styleName = "variable.other.less";
+ break;
+ case "ScssVariableReference":
+ styleName = "variable.other.less";
+ break;
+ default:
+ // If the stylename looks like a textmate style, just use it
+ if (classificationName.IndexOf ('.') >= 0) {
+ styleName = classificationName;
+ }
+
+ break;
+ }
+
+ return styleName;
}
static ScopeStack MakeScope (ScopeStack defaultScope, string scope)
@@ -398,10 +453,10 @@ namespace Microsoft.VisualStudio.Platform classificationMapCache = classificationMapCache.SetItem (scope, result);
return result;
- } - - public void Dispose () - { - } + }
+
+ public void Dispose ()
+ {
+ }
}
}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/Counter.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/Counter.cs index 26f173eb98..9b6a88c8d6 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/Counter.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/Counter.cs @@ -202,7 +202,7 @@ namespace MonoDevelop.Core.Instrumentation } } - internal int StoreValue (string message, TimeCounter timer, IDictionary<string, string> metadata) + internal int StoreValue (string message, ITimeCounter timer, IDictionary<string, string> metadata) { DateTime now = DateTime.Now; if (resolution.Ticks != 0) { @@ -371,6 +371,53 @@ namespace MonoDevelop.Core.Instrumentation } } + + public class Counter<T>: Counter where T : CounterMetadata, new() + { + internal Counter (string name, CounterCategory category): base (name, category) + { + } + + public void Inc (string message, T metadata) + { + Inc (1, message, metadata.Properties); + } + + public void Inc (T metadata) + { + Inc (1, null, metadata.Properties); + } + + public void Inc (int n, string message, T metadata) + { + Inc (n, message, metadata.Properties); + } + + public void Dec (string message, T metadata) + { + Dec (1, message, metadata.Properties); + } + + public void Dec (T metadata) + { + Dec (1, null, metadata.Properties); + } + + public void Dec (int n, string message, T metadata) + { + Dec (n, message, metadata.Properties); + } + + public void SetValue (int value, T metadata) + { + SetValue (value, null, metadata.Properties); + } + + public void SetValue (int value, string message, T metadata) + { + SetValue (value, message, metadata.Properties); + } + } [Serializable] public struct CounterValue @@ -437,7 +484,12 @@ namespace MonoDevelop.Core.Instrumentation } public IDictionary<string, string> Metadata { - get { return metadata; } + get { + // If the value is for a timer, metadata will be stored in the traces list. + // That's because metadata may be allocated after CounterValue has been + // created (timer metadata can be set while timing is in progress). + return metadata ?? traces?.Metadata; + } } public TimeSpan Duration { diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/InstrumentationConsumer.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/InstrumentationConsumer.cs index 8e0574fe64..344a786516 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/InstrumentationConsumer.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/InstrumentationConsumer.cs @@ -27,15 +27,34 @@ using System; namespace MonoDevelop.Core.Instrumentation { + /// <summary> + /// An instrumentation consumer can consume values generated by counters + /// </summary> [Mono.Addins.TypeExtensionPoint] public abstract class InstrumentationConsumer { + /// <summary> + /// Returns true if this consumer can handle the provided counter + /// </summary> + /// <returns><c>true</c>, if the counter is supported, <c>false</c> otherwise.</returns> + /// <param name="counter">The counter</param> public abstract bool SupportsCounter (Counter counter); + /// <summary> + /// Invoked when a new value is registered for a counter. It won't be invoked for timers. + /// </summary> + /// <param name="counter">The counter</param> + /// <param name="value">The value</param> public virtual void ConsumeValue (Counter counter, CounterValue value) { } + /// <summary> + /// Invoked when a new timer is started. + /// </summary> + /// <returns>An IDisposable object that is disposed when the timing operation ends.</returns> + /// <param name="counter">The timer counter</param> + /// <param name="value">The value</param> public virtual IDisposable BeginTimer (TimerCounter counter, CounterValue value) { return null; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/InstrumentationService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/InstrumentationService.cs index 78dc2bca5b..f2563a708f 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/InstrumentationService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/InstrumentationService.cs @@ -244,8 +244,18 @@ namespace MonoDevelop.Core.Instrumentation return CreateCounter (name, category, logMessages, id, false); } + public static Counter<T> CreateCounter<T> (string name, string category = null, bool logMessages = false, string id = null) where T : CounterMetadata, new() + { + return (Counter<T>) CreateCounter<T> (name, category, logMessages, id, false); + } + static Counter CreateCounter (string name, string category, bool logMessages, string id, bool isTimer) { + return CreateCounter<CounterMetadata> (name, category, logMessages, id, isTimer); + } + + static Counter CreateCounter<T> (string name, string category, bool logMessages, string id, bool isTimer) where T:CounterMetadata, new() + { if (name == null) throw new ArgumentNullException ("name", "Counters must have a Name"); @@ -261,7 +271,7 @@ namespace MonoDevelop.Core.Instrumentation categories.Add (cat); } - Counter c = isTimer ? new TimerCounter (name, cat) : new Counter (name, cat); + var c = isTimer ? new TimerCounter<T> (name, cat) : (Counter) new Counter<T> (name, cat); c.Id = id; c.LogMessages = logMessages; cat.AddCounter (c); @@ -326,6 +336,15 @@ namespace MonoDevelop.Core.Instrumentation return c; } + public static TimerCounter<T> CreateTimerCounter<T> (string name, string category = null, double minSeconds = 0, bool logMessages = false, string id = null) where T:CounterMetadata, new() + { + var c = (TimerCounter<T>) CreateCounter<T> (name, category, logMessages, id, true); + c.DisplayMode = CounterDisplayMode.Line; + c.LogMessages = logMessages; + c.MinSeconds = minSeconds; + return c; + } + public static IEnumerable<Counter> GetCounters () { lock (counters) { diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/TimeCounter.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/TimeCounter.cs index 2d65244a8c..dae3f29112 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/TimeCounter.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/TimeCounter.cs @@ -27,6 +27,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Threading; namespace MonoDevelop.Core.Instrumentation { @@ -34,10 +35,27 @@ namespace MonoDevelop.Core.Instrumentation { void Trace (string message); void End (); + TimeSpan Duration { get; } } - class DummyTimerCounter: ITimeTracker + public interface ITimeTracker<T>: IDisposable, ITimeTracker where T : CounterMetadata { + T Metadata { get; } + } + + interface ITimeCounter: ITimeTracker + { + void AddHandlerTracker (IDisposable t); + TimerTraceList TraceList { get; } + } + + class DummyTimerCounter<T>: ITimeTracker<T> where T : CounterMetadata + { + public DummyTimerCounter (T metadata) + { + Metadata = metadata; + } + public void Trace (string message) { } @@ -49,9 +67,13 @@ namespace MonoDevelop.Core.Instrumentation public void Dispose () { } + + public T Metadata { get; private set; } + + public TimeSpan Duration { get; } } - class TimeCounter: ITimeTracker + class TimeCounter<T>: ITimeTracker<T>, ITimeCounter where T:CounterMetadata, new() { Stopwatch stopWatch = new Stopwatch (); TimerTraceList traceList; @@ -59,15 +81,35 @@ namespace MonoDevelop.Core.Instrumentation TimerCounter counter; object linkedTrackers; long lastTraceTime; + T metadata; + CancellationToken cancellationToken; - internal TimeCounter (TimerCounter counter) + internal TimeCounter (TimerCounter counter, T metadata, CancellationToken cancellationToken) { this.counter = counter; - if (counter.Enabled) + this.metadata = metadata; + if (counter.Enabled || metadata != null) { + // Store metadata in the traces list. The corresponding CounterValue will get whatever + // metadata is assigned there traceList = new TimerTraceList (); + traceList.Metadata = metadata?.Properties; + } + this.cancellationToken = cancellationToken; Begin (); } + public T Metadata { + get { + if (metadata == null) { + metadata = new T (); + if (traceList == null) + traceList = new TimerTraceList (); + traceList.Metadata = metadata.Properties; + } + return metadata; + } + } + public void AddHandlerTracker (IDisposable t) { if (linkedTrackers == null) @@ -80,7 +122,7 @@ namespace MonoDevelop.Core.Instrumentation ((List<IDisposable>)linkedTrackers).Add (t); } - internal TimerTraceList TraceList { + TimerTraceList ITimeCounter.TraceList { get { return this.traceList; } } @@ -117,6 +159,10 @@ namespace MonoDevelop.Core.Instrumentation } stopWatch.Stop (); + Duration = stopWatch.Elapsed; + + if (metadata != null && cancellationToken != CancellationToken.None && cancellationToken.IsCancellationRequested) + metadata.Result = CounterResult.UserCancel; if (counter.LogMessages) { var time = stopWatch.ElapsedMilliseconds; @@ -145,6 +191,8 @@ namespace MonoDevelop.Core.Instrumentation { End (); } + + public TimeSpan Duration { get; private set; } } [Serializable] @@ -153,6 +201,10 @@ namespace MonoDevelop.Core.Instrumentation public TimerTrace FirstTrace; public TimeSpan TotalTime; public int ValueIndex; + + // Timer metadata is stored here, since it may change while the timer is alive. + // CounterValue will take the metadata from here. + public IDictionary<string, string> Metadata; } [Serializable] diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/TimerCounter.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/TimerCounter.cs index 561c3037d8..464294944f 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/TimerCounter.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Instrumentation/TimerCounter.cs @@ -26,42 +26,44 @@ using System; using System.Collections.Generic; +using System.Threading; +using System.Globalization; namespace MonoDevelop.Core.Instrumentation { [Serializable] - public class TimerCounter: Counter + public class TimerCounter : Counter { [NonSerialized] - TimeCounter lastTimer; + ITimeCounter lastTimer; double minSeconds; TimeSpan totalTime; int totalCountWithTime; TimeSpan minTime = TimeSpan.MaxValue; TimeSpan maxTime; - - public TimerCounter (string name, CounterCategory category): base (name, category) + + public TimerCounter (string name, CounterCategory category) : base (name, category) { } public override string ToString () { - return string.Format ("[TimerCounter: Name={0} Id={1} Category={2} MinSeconds={3}, TotalTime={4}, AverageTime={5}, MinTime={6}, MaxTime={7}, CountWithDuration={8}]",Name, Id, Category, MinSeconds, TotalTime, AverageTime, MinTime, MaxTime, CountWithDuration); + return string.Format ("[TimerCounter: Name={0} Id={1} Category={2} MinSeconds={3}, TotalTime={4}, AverageTime={5}, MinTime={6}, MaxTime={7}, CountWithDuration={8}]", Name, Id, Category, MinSeconds, TotalTime, AverageTime, MinTime, MaxTime, CountWithDuration); } public double MinSeconds { get { return this.minSeconds; } set { this.minSeconds = value; } } - + public TimeSpan TotalTime { get { return totalTime; } } - + public TimeSpan AverageTime { get { return totalCountWithTime > 0 ? new TimeSpan (totalTime.Ticks / totalCountWithTime) : TimeSpan.FromTicks (0); } } - + public TimeSpan MinTime { get { return totalCountWithTime > 0 ? this.minTime : TimeSpan.Zero; } } @@ -69,11 +71,11 @@ namespace MonoDevelop.Core.Instrumentation public TimeSpan MaxTime { get { return this.maxTime; } } - + public int CountWithDuration { get { return totalCountWithTime; } } - + internal void AddTime (TimeSpan time) { lock (values) { @@ -85,7 +87,7 @@ namespace MonoDevelop.Core.Instrumentation maxTime = time; } } - + public override void Trace (string message) { if (Enabled) { @@ -103,12 +105,12 @@ namespace MonoDevelop.Core.Instrumentation InstrumentationService.LogMessage (message); } } - + public ITimeTracker BeginTiming () { return BeginTiming (null, null); } - + public ITimeTracker BeginTiming (string message) { return BeginTiming (message, null); @@ -121,17 +123,23 @@ namespace MonoDevelop.Core.Instrumentation public ITimeTracker BeginTiming (string message, IDictionary<string, string> metadata) { - ITimeTracker timer; + return BeginTiming (message, metadata != null ? new CounterMetadata (metadata) : null, CancellationToken.None); + } + + internal ITimeTracker<T> BeginTiming<T> (string message, T metadata, CancellationToken cancellationToken) where T : CounterMetadata, new() + { + ITimeTracker<T> timer; if (!Enabled && !LogMessages) { - timer = dummyTimer; + timer = new DummyTimerCounter<T> (metadata); } else { - var c = new TimeCounter (this); + var c = new TimeCounter<T> (this, metadata, cancellationToken); if (Enabled) { lock (values) { - timer = lastTimer = c; + lastTimer = c; + timer = c; count++; totalCount++; - int i = StoreValue (message, lastTimer, metadata); + int i = StoreValue (message, lastTimer, metadata?.Properties); lastTimer.TraceList.ValueIndex = i; } } else { @@ -139,7 +147,8 @@ namespace MonoDevelop.Core.Instrumentation InstrumentationService.LogMessage (message); else InstrumentationService.LogMessage ("START: " + Name); - timer = lastTimer = c; + lastTimer = c; + timer = c; } } return timer; @@ -152,8 +161,136 @@ namespace MonoDevelop.Core.Instrumentation lastTimer = null; } } - - static ITimeTracker dummyTimer = new DummyTimerCounter (); } + + public class TimerCounter<T> : TimerCounter where T : CounterMetadata, new() + { + public TimerCounter (string name, CounterCategory category) : base (name, category) + { + } + + new public ITimeTracker<T> BeginTiming () + { + return base.BeginTiming<T> (null, null, CancellationToken.None); + } + + public ITimeTracker<T> BeginTiming (CancellationToken cancellationToken) + { + return base.BeginTiming<T> (null, null, cancellationToken); + } + + public ITimeTracker<T> BeginTiming (T metadata) + { + return base.BeginTiming<T> (null, metadata, CancellationToken.None); + } + + public ITimeTracker<T> BeginTiming (T metadata, CancellationToken cancellationToken) + { + return base.BeginTiming<T> (null, metadata, cancellationToken); + } + + new public ITimeTracker<T> BeginTiming (string message) + { + return base.BeginTiming<T> (message, null, CancellationToken.None); + } + + public ITimeTracker<T> BeginTiming (string message, CancellationToken cancellationToken) + { + return base.BeginTiming<T> (message, null, cancellationToken); + } + + public ITimeTracker<T> BeginTiming (string message, T metadata) + { + return base.BeginTiming<T> (message, metadata, CancellationToken.None); + } + + public ITimeTracker<T> BeginTiming (string message, T metadata, CancellationToken cancellationToken) + { + return base.BeginTiming<T> (message, metadata, cancellationToken); + } + } + + public class CounterMetadata + { + IDictionary<string, string> properties; + + internal protected IDictionary<string, string> Properties => properties; + + public CounterMetadata () + { + properties = new Dictionary<string, string> (); + } + + public CounterMetadata (IDictionary<string, string> properties) + { + this.properties = properties; + } + + public CounterResult Result { + get { + if (!properties.TryGetValue ("Result", out var result) || !Enum.TryParse (result, out CounterResult res)) + return CounterResult.Unspecified; + return res; + } + set { + if (value == CounterResult.Unspecified) + properties.Remove ("Result"); + else + properties ["Result"] = value.ToString (); + } + } + + public void SetUserFault () + { + Result = CounterResult.UserFault; + } + + public void SetFailure () + { + Result = CounterResult.Failure; + } + + public void SetSuccess () + { + Result = CounterResult.Success; + } + + public void SetUserCancel () + { + Result = CounterResult.UserCancel; + } + + protected void SetProperty (string name, string value) + { + properties [name] = value; + } + + protected void SetProperty (string name, object value) + { + properties [name] = Convert.ToString (value, CultureInfo.InvariantCulture); + } + + protected string GetProperty (string name) + { + properties.TryGetValue (name, out var result); + return result; + } + + protected T GetProperty<T> (string name) + { + properties.TryGetValue (name, out var result); + return (T) Convert.ChangeType (result, typeof (T), CultureInfo.InvariantCulture); + } + } + + public enum CounterResult + { + Unspecified, + Success, + Failure, + UserCancel, + UserFault + } + } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj index 5c7623c6fa..f1850ee414 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj @@ -764,6 +764,8 @@ <Compile Include="MonoDevelop.Core\SharedPools.cs" /> <Compile Include="MonoDevelop.Core.Collections\LookupTable.cs" /> <Compile Include="MonoDevelop.Core.Assemblies\FrameworkInfo.cs" /> + <Compile Include="MonoDevelop.Core\SynchronizationContextTaskScheduler.cs" /> + <Compile Include="MonoDevelop.Core\ConfigurationProperty.cs" /> </ItemGroup> <ItemGroup> <None Include="Makefile.am" /> diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ConfigurationProperty.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ConfigurationProperty.cs new file mode 100644 index 0000000000..49f755311c --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ConfigurationProperty.cs @@ -0,0 +1,115 @@ +// +// PropertyService.ConfigurationProperty.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; + +namespace MonoDevelop.Core +{ + /// <summary> + /// The Property wrapper wraps a global property service value as an easy to use object. + /// </summary> + public abstract class ConfigurationProperty<T> + { + public T Value { + get => OnGetValue (); + set => OnSetValue (value); + } + + /// <summary> + /// Set the property to the specified value. + /// </summary> + /// <param name='newValue'> + /// The new value. + /// </param> + /// <returns> + /// true, if the property has changed, false otherwise. + /// </returns> + public bool Set (T newValue) => OnSetValue (newValue); + + public static implicit operator T (ConfigurationProperty<T> watch) => watch.Value; + + protected abstract T OnGetValue (); + + protected abstract bool OnSetValue (T value); + + protected void OnChanged () => Changed?.Invoke (this, EventArgs.Empty); + + public event EventHandler Changed; + } + + class CoreConfigurationProperty<T> : ConfigurationProperty<T> + { + T value; + public string PropertyName { get; } + + public CoreConfigurationProperty (string name, T defaultValue, string oldName = null) + { + PropertyName = name ?? throw new ArgumentNullException (nameof(name)); + + // Migrate the property from oldName to name. + if (!string.IsNullOrEmpty (oldName) && PropertyService.HasValue (oldName)) { + // Migrate the old value if the new one is not set. + if (!PropertyService.HasValue (PropertyName)) { + var oldValue = PropertyService.Get<T> (oldName); + PropertyService.Set (PropertyName, oldValue); + } + PropertyService.Set (oldName, null); + } + + value = PropertyService.Get (PropertyName, defaultValue); + } + + protected override T OnGetValue () => value; + + protected override bool OnSetValue (T value) + { + if (EqualityComparer<T>.Default.Equals (this.value, value)) + return false; + + this.value = value; + PropertyService.Set (PropertyName, value); + OnChanged (); + return true; + } + } + + class ObsoleteConfigurationProperty<T> : ConfigurationProperty<T> + { + readonly T value; + + public ObsoleteConfigurationProperty (T value) => this.value = value; + protected override T OnGetValue () => value; + protected override bool OnSetValue (T value) => false; + } + + public abstract class ConfigurationProperty + { + public static ConfigurationProperty<T> Create<T> (string propertyName, T defaultValue, string oldName = null) + => new CoreConfigurationProperty<T> (propertyName, defaultValue, oldName); + + public static ConfigurationProperty<T> CreateObsolete<T> (T value) => new ObsoleteConfigurationProperty<T> (value); + } +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/PropertyService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/PropertyService.cs index 534cd8766b..63085ce898 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/PropertyService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/PropertyService.cs @@ -27,130 +27,15 @@ // using System; -using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; namespace MonoDevelop.Core { - /// <summary> - /// The Property wrapper wraps a global property service value as an easy to use object. - /// </summary> - public abstract class ConfigurationProperty<T> - { - public T Value { - get { return OnGetValue (); } - set { OnSetValue (value); } - } - - /// <summary> - /// Set the property to the specified value. - /// </summary> - /// <param name='newValue'> - /// The new value. - /// </param> - /// <returns> - /// true, if the property has changed, false otherwise. - /// </returns> - public bool Set (T newValue) - { - return OnSetValue (newValue); - } - - public static implicit operator T (ConfigurationProperty<T> watch) - { - return watch.Value; - } - - protected abstract T OnGetValue (); - - protected abstract bool OnSetValue (T value); - - protected void OnChanged () - { - Changed?.Invoke (this, EventArgs.Empty); - } - - public event EventHandler Changed; - } - - class CoreConfigurationProperty<T>: ConfigurationProperty<T> - { - T value; - readonly string propertyName; - - public CoreConfigurationProperty (string name, T defaultValue, string oldName = null) - { - propertyName = name; - - if (!string.IsNullOrEmpty (oldName) && PropertyService.HasValue (oldName)) { - if (!PropertyService.HasValue (name)) { - value = PropertyService.Get<T> (oldName); - PropertyService.Set (name, value); - } - PropertyService.Set (oldName, null); - } - - value = PropertyService.Get (name, defaultValue); - } - - protected override T OnGetValue () - { - return value; - } - - protected override bool OnSetValue (T value) - { - if (!EqualityComparer<T>.Default.Equals (this.value, value)) { - this.value = value; - PropertyService.Set (propertyName, value); - OnChanged (); - return true; - } - return false; - } - } - - class ObsoleteConfigurationProperty<T> : ConfigurationProperty<T> - { - T value; - - public ObsoleteConfigurationProperty (T value) - { - this.value = value; - } - - protected override T OnGetValue () - { - return value; - } - - protected override bool OnSetValue (T value) - { - return false; - } - } - - public abstract class ConfigurationProperty - { - public static ConfigurationProperty<T> Create<T> (string propertyName, T defaultValue, string oldName = null) - { - return new CoreConfigurationProperty<T> (propertyName, defaultValue, oldName); - } - - public static ConfigurationProperty<T> CreateObsolete<T> (T value) - { - return new ObsoleteConfigurationProperty<T> (value); - } - } - public static class PropertyService { - public static ConfigurationProperty<T> Wrap<T> (string property, T defaultValue) - { - return new CoreConfigurationProperty<T> (property, defaultValue); - } + public static ConfigurationProperty<T> Wrap<T> (string property, T defaultValue) => new CoreConfigurationProperty<T> (property, defaultValue); //force the static class to intialize internal static void Initialize () diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Runtime.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Runtime.cs index 652e6dea93..7e07fd5e2e 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Runtime.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Runtime.cs @@ -85,7 +85,8 @@ namespace MonoDevelop.Core Platform.Initialize (); - mainThread = Thread.CurrentThread; + mainThread = mainThread ?? Thread.CurrentThread; + // Set a default sync context if (SynchronizationContext.Current == null) { defaultSynchronizationContext = new SynchronizationContext (); @@ -298,24 +299,15 @@ namespace MonoDevelop.Core set { if (mainSynchronizationContext != null && value != null) throw new InvalidOperationException ("The main synchronization context has already been set"); + + mainThread = Thread.CurrentThread; mainSynchronizationContext = value; - taskScheduler = null; + taskScheduler = new SynchronizationContextTaskScheduler (value); } } - static TaskScheduler taskScheduler; - public static TaskScheduler MainTaskScheduler { - get { - if (taskScheduler == null) - RunInMainThread (() => { - if (taskScheduler == null) - taskScheduler = TaskScheduler.FromCurrentSynchronizationContext (); - }).Wait (); - - return taskScheduler; - } - } + public static TaskScheduler MainTaskScheduler => taskScheduler; /// <summary> /// Runs an action in the main thread (usually the UI thread). The method returns a task, so it can be awaited. diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/SynchronizationContextTaskScheduler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/SynchronizationContextTaskScheduler.cs new file mode 100644 index 0000000000..585470e096 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/SynchronizationContextTaskScheduler.cs @@ -0,0 +1,57 @@ +// +// SynchronizationContextTaskScheduler.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MonoDevelop.Core +{ + // Based on CoreCLR's implementation of the TaskScheduler they return from TaskScheduler.FromCurrentSynchronizationContext + class SynchronizationContextTaskScheduler : TaskScheduler + { + readonly SendOrPostCallback _postCallback; + readonly SynchronizationContext _synchronizationContext; + + internal SynchronizationContextTaskScheduler (SynchronizationContext synchronizationContext) + { + _postCallback = new SendOrPostCallback (PostCallback); + _synchronizationContext = synchronizationContext ?? throw new ArgumentNullException (nameof (synchronizationContext)); + } + + public override int MaximumConcurrencyLevel => 1; + + protected override void QueueTask (Task task) => _synchronizationContext.Post (_postCallback, task); + + protected override bool TryExecuteTaskInline (Task task, bool taskWasPreviouslyQueued) => + SynchronizationContext.Current == _synchronizationContext && TryExecuteTask (task); + + protected override IEnumerable<Task> GetScheduledTasks () => null; + + void PostCallback (object obj) => TryExecuteTask ((Task)obj); + } +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs index 80adbf3201..416d7fa120 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs @@ -542,12 +542,8 @@ namespace MonoDevelop.Projects // pre-load the results with the current list of files in the project var results = new List<ProjectFile> (); - var buildActions = GetBuildActions ().Where (a => a != "Folder" && a != "--").ToArray (); - - var config = configuration != null ? GetConfiguration (configuration) : null; - var pri = await CreateProjectInstaceForConfigurationAsync (config?.Name, config?.Platform, false); - foreach (var it in pri.EvaluatedItems.Where (i => buildActions.Contains (i.Name))) - results.Add (CreateProjectFile (it)); + var evaluatedItems = await GetEvaluatedSourceFiles (configuration); + results.AddRange (evaluatedItems); // add in any compile items that we discover from running the CoreCompile dependencies var evaluatedCompileItems = await GetCompileItemsFromCoreCompileDependenciesAsync (monitor, configuration); @@ -557,6 +553,49 @@ namespace MonoDevelop.Projects return results.ToArray (); } + object evaluatedSourceFilesLock = new object (); + string evaluatedSourceFilesConfiguration; + TaskCompletionSource<ImmutableArray<ProjectFile>> evaluatedSourceFilesTask; + + async Task<ImmutableArray<ProjectFile>> GetEvaluatedSourceFiles (ConfigurationSelector configuration) + { + bool startTask = false; + TaskCompletionSource<ImmutableArray<ProjectFile>> currentTask = null; + var config = configuration != null ? GetConfiguration (configuration) : null; + + lock (evaluatedSourceFilesLock) { + if (evaluatedSourceFilesTask == null || evaluatedSourceFilesConfiguration != config?.Id) { + // The configuration changed or query not yet done + evaluatedSourceFilesConfiguration = config?.Id; + evaluatedSourceFilesTask = new TaskCompletionSource<ImmutableArray<ProjectFile>> (); + startTask = true; + } + currentTask = evaluatedSourceFilesTask; + } + + if (startTask) { + var buildActions = GetBuildActions ().Where (a => a != "Folder" && a != "--").ToArray (); + var results = ImmutableArray.CreateBuilder<ProjectFile> (); + + var pri = await CreateProjectInstaceForConfigurationAsync (config?.Name, config?.Platform, false); + foreach (var it in pri.EvaluatedItems.Where (i => buildActions.Contains (i.Name))) + results.Add (CreateProjectFile (it)); + + currentTask.SetResult (results.ToImmutable ()); + } + + return await currentTask.Task; + } + + protected override Task OnClearCachedData () + { + lock (evaluatedSourceFilesLock) { + evaluatedSourceFilesConfiguration = null; + evaluatedSourceFilesTask = null; + } + return base.OnClearCachedData (); + } + /// <summary> /// When the MSBuild imports in a project change we need to let the type system know so /// it can update its source files. A NuGet package may contain only MSBuild targets diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/StockIcons.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/StockIcons.addin.xml index 69b9659733..1285018219 100644 --- a/main/src/core/MonoDevelop.Ide/ExtensionModel/StockIcons.addin.xml +++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/StockIcons.addin.xml @@ -377,7 +377,7 @@ <StockIcon stockid="md-status-search" animation="res:status-searching-1-16.png;res:status-searching-2-16.png;res:status-searching-3-16.png;res:status-searching-4-16.png" size="Menu" /> <StockIcon stockid="md-status-connecting" animation="res:status-connecting-1-16.png;res:status-connecting-2-16.png;res:status-connecting-3-16.png;res:status-connecting-4-16.png;res:status-connecting-5-16.png;res:status-connecting-6-16.png" size="Menu" /> <StockIcon stockid="md-status-device-deploying" animation="res:status-device-deploying-1-16.png;res:status-device-deploying-2-16.png;res:status-device-deploying-3-16.png;res:status-device-deploying-4-16.png;res:status-device-deploying-5-16.png;res:status-device-deploying-6-16.png" size="Menu" /> - <StockIcon stockid="md-status-waiting" animation="res:status-waiting-1-16.png;res:status-waiting-2-16.png;res:status-waiting-3-16.png;res:status-waiting-4-16.png;res:status-waiting-5-16.png" size="Menu" /> + <StockIcon stockid="md-status-waiting" animation="res:status-waiting-1-16.png;40;res:status-waiting-2-16.png;40;res:status-waiting-3-16.png;40;res:status-waiting-4-16.png;40;res:status-waiting-5-16.png;40" size="Menu" /> <StockIcon stockid="md-status-steady" resource="status-ready-16.png" size="Menu" /> <StockIcon stockid="md-status-success" resource="status-success-16.png" size="Menu" /> <StockIcon stockid="md-status-warning" resource="status-warning-16.png" size="Menu" /> @@ -391,9 +391,9 @@ <StockIcon stockid="md-status-instrumentation" resource="status-instrumentation-16.png" size="Menu" /> <!-- Spinners --> - <StockIcon stockid="md-spinner-14" animation="res:spinner-14-1.png;60;res:spinner-14-2.png;60;res:spinner-14-3.png;60;res:spinner-14-4.png;60;res:spinner-14-5.png;60" size="Menu" /> - <StockIcon stockid="md-spinner-16" animation="res:spinner-16-1.png;60;res:spinner-16-2.png;60;res:spinner-16-3.png;60;res:spinner-16-4.png;60;res:spinner-16-5.png;60" size="Menu" /> - <StockIcon stockid="md-spinner-18" animation="res:spinner-18-1.png;60;res:spinner-18-2.png;60;res:spinner-18-3.png;60;res:spinner-18-4.png;60;res:spinner-18-5.png;60" size="Button" /> + <StockIcon stockid="md-spinner-14" animation="res:spinner-14-1.png;40;res:spinner-14-2.png;40;res:spinner-14-3.png;40;res:spinner-14-4.png;40;res:spinner-14-5.png;40" size="Menu" /> + <StockIcon stockid="md-spinner-16" animation="res:spinner-16-1.png;40;res:spinner-16-2.png;40;res:spinner-16-3.png;40;res:spinner-16-4.png;40;res:spinner-16-5.png;40" size="Menu" /> + <StockIcon stockid="md-spinner-18" animation="res:spinner-18-1.png;40;res:spinner-18-2.png;40;res:spinner-18-3.png;40;res:spinner-18-4.png;40;res:spinner-18-5.png;40" size="Button" /> <!-- Overlays --> <StockIcon stockid="md-internal-class" icon="res:element-class-16.png|res:element-visibility-internal-diamond-overlay-16.png" size="Menu" /> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebook.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebook.cs index eac94c4102..1f50fa9164 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebook.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebook.cs @@ -30,6 +30,8 @@ using Gtk; using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; +using System.IO; using MonoDevelop.Ide; using MonoDevelop.Components.AtkCocoaHelper; using MonoDevelop.Core; @@ -239,28 +241,57 @@ namespace MonoDevelop.Components.DockNotebook tabStrip.InitSize (); } - void OnDragDataReceived (object o, Gtk.DragDataReceivedArgs args) + async void OnDragDataReceived (object o, Gtk.DragDataReceivedArgs args) { if (args.Info != (uint) TargetList.UriList) return; string fullData = System.Text.Encoding.UTF8.GetString (args.SelectionData.Data); + var loadWorkspaceItems = !IsInsideTabStrip (args.X, args.Y); + var files = new List<Ide.Gui.FileOpenInformation> (); + foreach (string individualFile in fullData.Split ('\n')) { string file = individualFile.Trim (); if (file.StartsWith ("file://")) { var filePath = new FilePath (file); + if (filePath.IsDirectory) { + if (!loadWorkspaceItems) // skip directories when not loading solutions + continue; + filePath = Directory.EnumerateFiles (filePath).FirstOrDefault (p => Services.ProjectService.IsWorkspaceItemFile (p)); + } + if (!filePath.IsNullOrEmpty) // skip empty paths + files.Add (new Ide.Gui.FileOpenInformation (filePath, null, 0, 0, Ide.Gui.OpenDocumentOptions.DefaultInternal) { DockNotebook = this }); + } + } + if (files.Count > 0) { + if (loadWorkspaceItems) { try { - if (Services.ProjectService.IsWorkspaceItemFile (filePath)) - IdeApp.Workspace.OpenWorkspaceItem(filePath); - else - IdeApp.Workbench.OpenDocument (filePath, null, -1, -1, MonoDevelop.Ide.Gui.OpenDocumentOptions.Default, null, null, this); + IdeApp.OpenFiles (files); } catch (Exception e) { - MonoDevelop.Core.LoggingService.LogError ("unable to open file {0} exception was :\n{1}", file, e.ToString()); + LoggingService.LogError ($"Failed to open dropped files", e); + } + } else { // open workspace items as files + foreach (var file in files) { + try { + await IdeApp.Workbench.OpenDocument (file).ConfigureAwait (false); + } catch (Exception e) { + LoggingService.LogError ($"unable to open file {file}", e); + } } } } } + + bool IsInsideTabStrip (int pointerX, int pointerY) + { + if (tabStrip?.IsRealized != true) + return false; + int tabX, tabY; + TranslateCoordinates (tabStrip, pointerX, pointerY, out tabX, out tabY); + return tabStrip.Allocation.Contains (new Point (tabX, tabY)); + } + public DockNotebookContainer Container { get { var container = (DockNotebookContainer)Parent; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionStatistics.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionStatistics.cs new file mode 100644 index 0000000000..737946aaa0 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionStatistics.cs @@ -0,0 +1,100 @@ +// +// CompletionStatistics.cs +// +// Author: +// Matt Ward <matt.ward@microsoft.com> +// +// Copyright (c) 2018 Microsoft +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; + +namespace MonoDevelop.Ide.Editor.Extension +{ + class CompletionStatistics + { + TimeSpan maxTime; + TimeSpan minTime = TimeSpan.MaxValue; + TimeSpan totalTime; + TimeSpan? firstTime; + int timingsCount; + int failureCount; + int cancelCount; + int successCount; + + public void OnSuccess (TimeSpan duration) + { + successCount++; + AddTime (duration); + } + + public void OnFailure (TimeSpan duration) + { + failureCount++; + AddTime (duration); + } + + public void OnUserCanceled (TimeSpan duration) + { + cancelCount++; + AddTime (duration); + } + + void AddTime (TimeSpan duration) + { + if (duration > maxTime) { + maxTime = duration; + } + + if (duration < minTime) { + minTime = duration; + } + + if (!firstTime.HasValue) { + firstTime = duration; + } + + totalTime += duration; + timingsCount++; + } + + public void Report () + { + if (timingsCount == 0) { + // No timings recorded. + return; + } + + var metadata = new Dictionary<string, string> (); + + var average = totalTime.TotalMilliseconds / timingsCount; + metadata ["AverageDuration"] = average.ToString (); + metadata ["FirstDuration"] = firstTime.Value.TotalMilliseconds.ToString (); + metadata ["MaximumDuration"] = maxTime.TotalMilliseconds.ToString (); + metadata ["MinimumDuration"] = minTime.TotalMilliseconds.ToString (); + metadata ["FailureCount"] = failureCount.ToString (); + metadata ["SuccessCount"] = successCount.ToString (); + metadata ["CancelCount"] = cancelCount.ToString (); + + Counters.CodeCompletionStats.Inc (metadata); + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs index 76d02a6bfa..2fd232eb21 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs @@ -37,6 +37,7 @@ using MonoDevelop.Ide.Editor; using System.Threading.Tasks; using System.Threading; using Gtk; +using MonoDevelop.Core.Instrumentation; namespace MonoDevelop.Ide.Editor.Extension { @@ -71,6 +72,8 @@ namespace MonoDevelop.Ide.Editor.Extension public class CompletionTextEditorExtension : TextEditorExtension { + CompletionStatistics completionStats = new CompletionStatistics (); + internal protected CodeCompletionContext CurrentCompletionContext { get; set; @@ -191,11 +194,10 @@ namespace MonoDevelop.Ide.Editor.Extension completionTokenSrc = new CancellationTokenSource (); var caretOffset = Editor.CaretOffset; var token = completionTokenSrc.Token; + ITimeTracker timer = null; - var metadata = new Dictionary<string, string> (); - metadata ["Result"] = "Success"; try { - Counters.ProcessCodeCompletion.BeginTiming (metadata); + timer = Counters.ProcessCodeCompletion.BeginTiming (); var task = DoHandleCodeCompletionAsync (CurrentCompletionContext, new CompletionTriggerInfo (CompletionTriggerReason.CharTyped, descriptor.KeyChar), token); if (task != null) { // Show the completion window in two steps. The call to PrepareShowWindow creates the window but @@ -228,25 +230,28 @@ namespace MonoDevelop.Ide.Editor.Extension CurrentCompletionContext = null; } } finally { + Counters.ProcessCodeCompletion.EndTiming (); if (token.IsCancellationRequested) { - metadata ["Result"] = "UserCancel"; + completionStats.OnUserCanceled (timer.Duration); + } else { + completionStats.OnSuccess (timer.Duration); } - Counters.ProcessCodeCompletion.EndTiming (); } }, Runtime.MainTaskScheduler); } else { CurrentCompletionContext = null; Counters.ProcessCodeCompletion.EndTiming (); + completionStats.OnSuccess (timer.Duration); } } catch (TaskCanceledException) { - metadata ["Result"] = "UserCancel"; Counters.ProcessCodeCompletion.EndTiming (); + completionStats.OnUserCanceled (timer.Duration); } catch (AggregateException) { - metadata ["Result"] = "Failure"; Counters.ProcessCodeCompletion.EndTiming (); + completionStats.OnFailure (timer.Duration); } catch { - metadata ["Result"] = "Failure"; Counters.ProcessCodeCompletion.EndTiming (); + completionStats.OnFailure (timer.Duration); throw; } } @@ -270,10 +275,9 @@ namespace MonoDevelop.Ide.Editor.Extension var caretOffset = Editor.CaretOffset; var token = completionTokenSrc.Token; - var metadata = new Dictionary<string, string> (); - metadata ["Result"] = "Success"; + ITimeTracker timer = null; try { - Counters.ProcessCodeCompletion.BeginTiming (metadata); + timer = Counters.ProcessCodeCompletion.BeginTiming (); var task = DoHandleCodeCompletionAsync (CurrentCompletionContext, new CompletionTriggerInfo (CompletionTriggerReason.BackspaceOrDeleteCommand, deleteOrBackspaceTriggerChar), token); if (task != null) { // Show the completion window in two steps. The call to PrepareShowWindow creates the window but @@ -311,10 +315,12 @@ namespace MonoDevelop.Ide.Editor.Extension CurrentCompletionContext = null; } } finally { + Counters.ProcessCodeCompletion.EndTiming (); if (token.IsCancellationRequested) { - metadata ["Result"] = "UserCancel"; + completionStats.OnUserCanceled (timer.Duration); + } else { + completionStats.OnSuccess (timer.Duration); } - Counters.ProcessCodeCompletion.EndTiming (); } }, Runtime.MainTaskScheduler); } else { @@ -322,15 +328,15 @@ namespace MonoDevelop.Ide.Editor.Extension } } catch (TaskCanceledException) { CurrentCompletionContext = null; - metadata ["Result"] = "UserCancel"; Counters.ProcessCodeCompletion.EndTiming (); + completionStats.OnUserCanceled (timer.Duration); } catch (AggregateException) { CurrentCompletionContext = null; - metadata ["Result"] = "Failure"; Counters.ProcessCodeCompletion.EndTiming (); + completionStats.OnFailure (timer.Duration); } catch { - metadata ["Result"] = "Failure"; Counters.ProcessCodeCompletion.EndTiming (); + completionStats.OnFailure (timer.Duration); throw; } } @@ -448,10 +454,10 @@ namespace MonoDevelop.Ide.Editor.Extension CurrentCompletionContext = CompletionWidget.CreateCodeCompletionContext (cpos); CurrentCompletionContext.TriggerWordLength = wlen; - var metadata = new Dictionary<string, string> (); - metadata ["Result"] = "Success"; + ITimeTracker timer = null; + bool failure = false; try { - Counters.ProcessCodeCompletion.BeginTiming (metadata); + timer = Counters.ProcessCodeCompletion.BeginTiming (); completionList = await DoHandleCodeCompletionAsync (CurrentCompletionContext, new CompletionTriggerInfo (reason), token); if (completionList != null && completionList.TriggerWordStart >= 0) { CurrentCompletionContext.TriggerOffset = completionList.TriggerWordStart; @@ -461,10 +467,15 @@ namespace MonoDevelop.Ide.Editor.Extension CurrentCompletionContext = null; } } catch (Exception) { - metadata ["Result"] = "Failure"; + failure = true; throw; } finally { Counters.ProcessCodeCompletion.EndTiming (); + if (failure) { + completionStats.OnFailure (timer.Duration); + } else { + completionStats.OnSuccess (timer.Duration); + } } } @@ -765,6 +776,7 @@ namespace MonoDevelop.Ide.Editor.Extension Editor.FocusLost -= HandleFocusOutEvent; // document.Editor.Paste -= HandlePaste; Deinitialize(); + completionStats.Report (); } base.Dispose (); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DefaultSourceEditorOptions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DefaultSourceEditorOptions.cs index 100b891b59..33b446b246 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DefaultSourceEditorOptions.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DefaultSourceEditorOptions.cs @@ -271,7 +271,7 @@ namespace MonoDevelop.Ide.Editor this.OnChanged (EventArgs.Empty); } - void UpdateStylePolicy (MonoDevelop.Ide.Gui.Content.TextStylePolicy currentPolicy) + internal void UpdateStylePolicy (MonoDevelop.Ide.Gui.Content.TextStylePolicy currentPolicy) { defaultEolMarker = TextStylePolicy.GetEolMarker (currentPolicy.EolMarker); tabsToSpaces = currentPolicy.TabsToSpaces; // PropertyService.Get ("TabsToSpaces", false); @@ -281,6 +281,14 @@ namespace MonoDevelop.Ide.Editor removeTrailingWhitespaces = currentPolicy.RemoveTrailingWhitespace; //PropertyService.Get ("RemoveTrailingWhitespaces", true); } + internal DefaultSourceEditorOptions Create () + { + var result = (DefaultSourceEditorOptions)MemberwiseClone (); + result.Changed = null; + return result; + } + + [Obsolete("Will be removed in future versions.")] public DefaultSourceEditorOptions WithTextStyle (MonoDevelop.Ide.Gui.Content.TextStylePolicy policy) { if (policy == null) @@ -293,6 +301,10 @@ namespace MonoDevelop.Ide.Editor internal void SetContext (ICodingConventionContext context) { + if (this.context == context) + return; + if (this.context != null) + this.context.CodingConventionsChangedAsync -= UpdateContextOptions; this.context = context; context.CodingConventionsChangedAsync += UpdateContextOptions; UpdateContextOptions (null, null); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditorConfigService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditorConfigService.cs index 1f4ee97422..e0e59ad2fc 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditorConfigService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditorConfigService.cs @@ -39,33 +39,38 @@ namespace MonoDevelop.Ide.Editor public readonly static string MaxLineLengthConvention = "max_line_length"; readonly static object contextCacheLock = new object (); readonly static ICodingConventionsManager codingConventionsManager = CodingConventionsManagerFactory.CreateCodingConventionsManager (new ConventionsFileManager()); - static ImmutableDictionary<string, ICodingConventionContext> contextCache = ImmutableDictionary<string, ICodingConventionContext>.Empty; + static ImmutableDictionary<string, Task<ICodingConventionContext>> contextCache = ImmutableDictionary<string, Task<ICodingConventionContext>>.Empty; - public async static Task<ICodingConventionContext> GetEditorConfigContext (string fileName, CancellationToken token = default (CancellationToken)) + public static Task<ICodingConventionContext> GetEditorConfigContext (string fileName, CancellationToken token = default (CancellationToken)) { if (!File.Exists (fileName)) - return null; - if (contextCache.TryGetValue (fileName, out ICodingConventionContext result)) - return result; - try { - result = await codingConventionsManager.GetConventionContextAsync (fileName, token); - } catch (OperationCanceledException) { - } catch (Exception e) { - LoggingService.LogError ("Error while getting coding conventions,", e); - } - if (result == null) - return null; + return TaskUtil.Default<ICodingConventionContext> (); lock (contextCacheLock) { + if (contextCache.TryGetValue (fileName, out Task<ICodingConventionContext> result)) + return result; + try { + result = codingConventionsManager.GetConventionContextAsync (fileName, token); + } catch (OperationCanceledException) { + } catch (Exception e) { + LoggingService.LogError ("Error while getting coding conventions,", e); + } + if (result == null) + return TaskUtil.Default<ICodingConventionContext> (); contextCache = contextCache.SetItem (fileName, result); return result; - } + } } - public static void RemoveEditConfigContext (string fileName) + public static async Task RemoveEditConfigContext (string fileName) { + Task<ICodingConventionContext> ctx; lock (contextCacheLock) { - contextCache = contextCache.Remove (fileName); + if (!contextCache.TryGetValue (fileName, out ctx)) + return; + contextCache = contextCache.Remove(fileName); } + if (ctx != null) + (await ctx).Dispose (); } class ConventionsFileManager : IFileWatcher @@ -75,8 +80,54 @@ namespace MonoDevelop.Ide.Editor public event ConventionsFileChangedAsyncEventHandler ConventionFileChanged; public event ContextFileMovedAsyncEventHandler ContextFileMoved; + public ConventionsFileManager () + { + FileService.FileChanged += FileService_FileChanged; + FileService.FileRemoved += FileService_FileRemoved; + FileService.FileMoved += FileService_FileMoved; + FileService.FileRenamed += FileService_FileMoved; + } + + void FileService_FileMoved (object sender, FileCopyEventArgs e) + { + foreach (var file in e) { + if (watchers.TryGetValue (file.SourceFile, out FileSystemWatcher watcher)) { + ContextFileMoved?.Invoke (this, new ContextFileMovedEventArgs (file.SourceFile, file.TargetFile)); + watcher.Dispose (); + watchers.Remove (file.SourceFile); + StartWatching (file.TargetFile.FileName, file.TargetFile.ParentDirectory); + } + } + } + + void FileService_FileChanged (object sender, FileEventArgs e) + { + foreach (var file in e) { + if (watchers.TryGetValue (file.FileName, out FileSystemWatcher watcher)) { + ConventionFileChanged?.Invoke (this, new ConventionsFileChangeEventArgs (watcher.Filter, watcher.Path, ChangeType.FileModified)); + } + } + } + + void FileService_FileRemoved (object sender, FileEventArgs e) + { + lock (watchers) { + foreach (var file in e) { + if (watchers.TryGetValue (file.FileName, out FileSystemWatcher watcher)) { + ConventionFileChanged?.Invoke (this, new ConventionsFileChangeEventArgs (watcher.Filter, watcher.Path, ChangeType.FileDeleted)); + watcher.Dispose (); + watchers.Remove (file.FileName); + } + } + } + } + public void Dispose () { + FileService.FileMoved -= FileService_FileMoved; + FileService.FileRenamed -= FileService_FileMoved; + FileService.FileRemoved -= FileService_FileRemoved; + FileService.FileChanged -= FileService_FileChanged; lock (watchers) { foreach (var kv in watchers) kv.Value.Dispose (); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs index c720e8daf2..3678dd9ba6 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs @@ -75,16 +75,20 @@ namespace MonoDevelop.Ide.Editor DefaultSourceEditorOptions.Instance.Changed += UpdateTextEditorOptions; textEditorImpl.ViewContent.ContentNameChanged += ViewContent_ContentNameChanged; textEditorImpl.ViewContent.DirtyChanged += ViewContent_DirtyChanged; - + textEditor.Options = DefaultSourceEditorOptions.Instance.Create (); } - protected override void OnContentNameChanged () + protected override async void OnContentNameChanged () { base.OnContentNameChanged (); if (ContentName != textEditorImpl.ContentName && !string.IsNullOrEmpty (textEditorImpl.ContentName)) AutoSave.RemoveAutoSaveFile (textEditorImpl.ContentName); - if (textEditorImpl.ContentName != null) + if (textEditorImpl.ContentName != null && textEditorImpl.ContentName != this.ContentName) { EditorConfigService.RemoveEditConfigContext (textEditorImpl.ContentName); + var context = await EditorConfigService.GetEditorConfigContext (textEditor.FileName, default (CancellationToken)); + if (context != null) + ((DefaultSourceEditorOptions)textEditor.Options).SetContext (context); + } textEditorImpl.ContentName = this.ContentName; if (this.WorkbenchWindow?.Document != null) textEditor.InitializeExtensionChain (this.WorkbenchWindow.Document); @@ -163,22 +167,20 @@ namespace MonoDevelop.Ide.Editor else policyContainer = MonoDevelop.Projects.Policies.PolicyService.DefaultPolicies; var currentPolicy = policyContainer.Get<TextStylePolicy> (mimeTypes); - policyContainer.PolicyChanged += HandlePolicyChanged; + ((DefaultSourceEditorOptions)textEditor.Options).UpdateStylePolicy (currentPolicy); var context = await EditorConfigService.GetEditorConfigContext (textEditor.FileName, default (CancellationToken)); if (context == null) return; - var options = DefaultSourceEditorOptions.Instance.WithTextStyle (currentPolicy); - options.SetContext (context); - textEditor.Options = options; + ((DefaultSourceEditorOptions)textEditor.Options).SetContext (context); } void HandlePolicyChanged (object sender, MonoDevelop.Projects.Policies.PolicyChangedEventArgs args) { var mimeTypes = DesktopService.GetMimeTypeInheritanceChain (textEditor.MimeType); var currentPolicy = policyContainer.Get<TextStylePolicy> (mimeTypes); - textEditor.Options = DefaultSourceEditorOptions.Instance.WithTextStyle (currentPolicy); + ((DefaultSourceEditorOptions)textEditor.Options).UpdateStylePolicy (currentPolicy); } void CancelDocumentParsedUpdate () diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/ExtensibleTreeView.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/ExtensibleTreeView.cs index 33934e68b4..4208f2476d 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/ExtensibleTreeView.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/ExtensibleTreeView.cs @@ -631,9 +631,14 @@ namespace MonoDevelop.Ide.Gui.Components public ITreeBuilder AddChild (object nodeObject) { + return AddChild (nodeObject, true); + } + + public ITreeBuilder AddChild (object nodeObject, bool expanded) + { TreeBuilder builder = new TreeBuilder (this); builder.AddChild (nodeObject, true); - builder.Expanded = true; + builder.Expanded = expanded; InitialSelection (); return builder; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DefaultWorkbench.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DefaultWorkbench.cs index d1266ba242..b81902074e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DefaultWorkbench.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DefaultWorkbench.cs @@ -219,6 +219,8 @@ namespace MonoDevelop.Ide.Gui IdeApp.CommandService.SetRootWindow (this); DockNotebook.NotebookChanged += NotebookPagesChanged; + Drag.DestSet (this, DestDefaults.All, targetEntryTypes, Gdk.DragAction.Copy); + Accessible.SetIsMainWindow (true); } @@ -1100,7 +1102,8 @@ namespace MonoDevelop.Ide.Gui internal void ShowPopup (DockNotebook notebook, int tabIndex, Gdk.EventButton evt) { notebook.CurrentTabIndex = tabIndex; - IdeApp.CommandService.ShowContextMenu (notebook, evt, "/MonoDevelop/Ide/ContextMenu/DocumentTab"); + var entrySet = IdeApp.CommandService.CreateCommandEntrySet ("/MonoDevelop/Ide/ContextMenu/DocumentTab"); + IdeApp.CommandService.ShowContextMenu (notebook, evt, entrySet, notebook.CurrentTab.Content); } internal void OnTabsReordered (DockNotebookTab widget, int oldPlacement, int newPlacement) @@ -1510,6 +1513,57 @@ namespace MonoDevelop.Ide.Gui } #endregion + + #region DnD + + enum TargetList + { + UriList = 100 + } + + static TargetEntry [] targetEntryTypes = { new TargetEntry ("text/uri-list", 0, (uint)TargetList.UriList) }; + + + protected override bool OnDragMotion (Gdk.DragContext context, int x, int y, uint time_) + { + // Bring this window to the front. Otherwise, the drop may end being done in another window that overlaps this one + if (!Platform.IsWindows) + Present (); + return base.OnDragMotion (context, x, y, time_); + } + + protected override void OnDragDataReceived (Gdk.DragContext context, int x, int y, SelectionData selection_data, uint info, uint time_) + { + if (info != (uint)TargetList.UriList) + return; + string fullData = System.Text.Encoding.UTF8.GetString (selection_data.Data); + + var files = new List<FileOpenInformation> (); + + foreach (string individualFile in fullData.Split ('\n')) { + string file = individualFile.Trim (); + if (!file.StartsWith ("file://", StringComparison.Ordinal)) + continue; + + var filePath = new FilePath (file); + if (filePath.IsDirectory) + filePath = Directory.EnumerateFiles (filePath).FirstOrDefault (p => Services.ProjectService.IsWorkspaceItemFile (p)); + if (!filePath.IsNullOrEmpty) + files.Add (new FileOpenInformation (filePath, null, 0, 0, OpenDocumentOptions.DefaultInternal)); + } + + if (files.Count > 0) { + try { + IdeApp.OpenFiles (files); + } catch (Exception e) { + LoggingService.LogError ($"Failed to open dropped files", e); + } + } + + base.OnDragDataReceived (context, x, y, selection_data, info, time_); + } + + #endregion } class PadActivationHandler: CommandHandler diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices.Options/MonoDevelopGlobalOptionPersister.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices.Options/MonoDevelopGlobalOptionPersister.cs new file mode 100644 index 0000000000..109de58edf --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices.Options/MonoDevelopGlobalOptionPersister.cs @@ -0,0 +1,244 @@ +// +// RoamingMonoDevelopProfileOptionPersister.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Xml.Linq; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Options.Providers; +using Roslyn.Utilities; +using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; +using MonoDevelop.Core; +using System.Runtime.Serialization; +using System.ComponentModel; +using MonoDevelop.Ide.Gui.Content; +using MonoDevelop.Projects.Policies; + +namespace MonoDevelop.Ide.RoslynServices.Options +{ + /// <summary> + /// Handles options persisting and bridging between roslyn and MonoDevelop. + /// </summary> + [Export (typeof (IOptionPersister))] + sealed class MonoDevelopGlobalOptionPersister : IOptionPersister, IDisposable + { + readonly IGlobalOptionService globalOptionService; + readonly RoslynPreferences preferences; + + Dictionary<IOption, Func<TextStylePolicy, object>> mapping = new Dictionary<IOption, Func<TextStylePolicy, object>> { + { FormattingOptions.UseTabs, policy => !policy.TabsToSpaces }, + { FormattingOptions.TabSize, policy => policy.TabWidth }, + { FormattingOptions.IndentationSize, policy => policy.IndentWidth }, + { FormattingOptions.NewLine, policy => policy.GetEolMarker () }, + }; + + [ImportingConstructor] + public MonoDevelopGlobalOptionPersister (IGlobalOptionService globalOptionService) : this (globalOptionService, null) + { + } + + internal MonoDevelopGlobalOptionPersister (IGlobalOptionService globalOptionService, RoslynPreferences preferences) + { + Contract.ThrowIfNull (globalOptionService); + this.globalOptionService = globalOptionService; + + this.preferences = preferences ?? IdeApp.Preferences.Roslyn; + + PropertyService.PropertyChanged += OnPropertyChanged; + } + + void OnPropertyChanged (object sender, Core.PropertyChangedEventArgs args) + { + Refresh (args.Key, args.NewValue); + } + + void IDisposable.Dispose () + { + PropertyService.PropertyChanged -= OnPropertyChanged; + } + + public bool TryFetch (OptionKey optionKey, out object value) + { + // Policy mapping to roslyn options + if (mapping.TryGetValue (optionKey.Option, out var policyValueGetter)) { + // TODO: Handle policy value changes? + + value = policyValueGetter (optionKey.GetTextStylePolicy ()); + return true; + } + + // Property bindings + if (preferences.TryGet (optionKey, out var storageKey, out var serializedValue)) { + MonitorChanges (storageKey, optionKey); + try { + value = Deserialize (serializedValue, optionKey.Option.Type); + return true; + } catch (Exception ex) { + LoggingService.LogError ($"Failed to deserialize type: {optionKey.Option.Type} value: {serializedValue}", ex); + } + } + + //use this for checking for options we could be handling + //PrintOptionKey(optionKey); + + value = null; + return false; + } + + public bool TryPersist (OptionKey optionKey, object value) + { + // Property bindings + if (preferences.TryGetUpdater (optionKey, out string storageKey, out var updater)) { + MonitorChanges (storageKey, optionKey); + var serializedValue = Serialize (value, optionKey.Option.Type); + updater (serializedValue); + return true; + } + + return false; + } + + readonly Dictionary<string, List<OptionKey>> _optionsToMonitorForChanges = new Dictionary<string, List<OptionKey>> (); + + public void MonitorChanges (string propertyName, OptionKey optionKey) + { + // We're about to fetch the value, so make sure that if it changes we'll know about it + lock (_optionsToMonitorForChanges) { + var optionKeysToMonitor = _optionsToMonitorForChanges.GetOrAdd (propertyName, _ => new List<OptionKey> ()); + + if (!optionKeysToMonitor.Contains (optionKey)) + optionKeysToMonitor.Add (optionKey); + } + } + + public void Refresh (string propertyName, object newValue) + { + lock (_optionsToMonitorForChanges) { + if (!_optionsToMonitorForChanges.TryGetValue (propertyName, out var optionsToRefresh)) + return; + + foreach (var optionToRefresh in optionsToRefresh) { + globalOptionService.RefreshOption (optionToRefresh, Deserialize (newValue, optionToRefresh.Option.Type)); + } + } + } + + static void PrintOptionKey (OptionKey optionKey) + { + Console.WriteLine ($"Name '{optionKey.Option.Name}' Language '{optionKey.Language}' LanguageSpecific'{optionKey.Option.IsPerLanguage}'"); + + var locations = optionKey.Option.StorageLocations; + if (locations.IsDefault) { + return; + } + + foreach (var loc in locations) { + switch (loc) { + case RoamingProfileStorageLocation roaming: + Console.WriteLine ($" roaming: {roaming.GetKeyNameForLanguage (optionKey.Language)}"); + break; + case LocalUserProfileStorageLocation local: + Console.WriteLine ($" local: {local.KeyName}"); + break; + case EditorConfigStorageLocation<int> edconf: + Console.WriteLine ($" editorconfig: {edconf.KeyName}"); + break; + case EditorConfigStorageLocation<string> edconf: + Console.WriteLine ($" editorconfig: {edconf.KeyName}"); + break; + case EditorConfigStorageLocation<bool> edconf: + Console.WriteLine ($" editorconfig: {edconf.KeyName}"); + break; + case EditorConfigStorageLocation<Microsoft.CodeAnalysis.CodeStyle.CodeStyleOption<bool>> edconf: + Console.WriteLine ($" editorconfig: {edconf.KeyName}"); + break; + default: + Console.WriteLine ($" unknown: {loc.GetType ()}"); + break; + } + } + } + + #region Serialization + static object Serialize (object value, Type optionType) + { + // We store these as strings, so serialize + if (value is ICodeStyleOption codeStyleOption) + return codeStyleOption.ToXElement ().ToString (); + + if (optionType == typeof (NamingStylePreferences) && value is NamingStylePreferences valueToSerialize) + return valueToSerialize.CreateXElement ().ToString (); + + return value; + } + + static object Deserialize (object value, Type optionType) + { + if (optionType.IsEnum && value != null && optionType.IsEnumDefined (value)) + return Enum.ToObject (optionType, value); + + if (optionType == typeof (CodeStyleOption<bool>)) + return DeserializeCodeStyleOption (value, x => CodeStyleOption<bool>.FromXElement (x)); + + if (optionType == typeof (CodeStyleOption<ExpressionBodyPreference>)) + return DeserializeCodeStyleOption (value, x => CodeStyleOption<ExpressionBodyPreference>.FromXElement (x)); + + if (optionType == typeof (NamingStylePreferences)) + return DeserializeCodeStyleOption (value, x => NamingStylePreferences.FromXElement (x)); + + if (optionType == typeof (bool)) { + // TypeScript used to store some booleans as integers. We now handle them properly for legacy sync scenarios. + if (value is int intValue) + return intValue != 0; + + if (value is long longValue) + return longValue != 0; + } + + if (optionType == typeof (bool?)) { + // code uses object to hold onto any value which will use boxing on value types. + // see boxing on nullable types - https://msdn.microsoft.com/en-us/library/ms228597.aspx + if (!(value is bool) && value != null) + throw new SerializationException (); + } else if (value != null && optionType != value.GetType ()) { + // We got something back different than we expected, so fail to deserialize + throw new SerializationException (); + } + + return value; + + object DeserializeCodeStyleOption<T> (object v, Func<XElement, T> transform) => transform (XElement.Parse ((string)v)); + } + #endregion + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices.Options/OptionsExtensions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices.Options/OptionsExtensions.cs new file mode 100644 index 0000000000..2a9ce1494e --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices.Options/OptionsExtensions.cs @@ -0,0 +1,60 @@ +// +// OptionsExtensions.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Linq; +using Microsoft.CodeAnalysis.Options; +using MonoDevelop.Ide.Gui.Content; +using MonoDevelop.Projects.Policies; + +namespace MonoDevelop.Ide.RoslynServices.Options +{ + static class OptionsExtensions + { + public static string GetPropertyName (this OptionKey optionKey) + { + // Prevent NRE being thrown on iteration. + if (optionKey.Option.StorageLocations.IsDefaultOrEmpty) + return null; + + foreach (var storageLocation in optionKey.Option.StorageLocations) { + if (storageLocation is RoamingProfileStorageLocation roamingLocation) + return roamingLocation.GetKeyNameForLanguage (optionKey.Language); + if (storageLocation is LocalUserProfileStorageLocation userLocation) + return userLocation.KeyName; + } + return null; + } + + public static TextStylePolicy GetTextStylePolicy (this OptionKey optionKey) + { + var mimeChain = DesktopService.GetMimeTypeInheritanceChainForRoslynLanguage (optionKey.Language); + if (mimeChain == null) { + throw new Exception ($"Unknown Roslyn language {optionKey.Language}"); + } + return PolicyService.GetDefaultPolicy<TextStylePolicy> (mimeChain); + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices.Options/RoslynPreferences.Properties.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices.Options/RoslynPreferences.Properties.cs new file mode 100644 index 0000000000..cc5e399e99 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices.Options/RoslynPreferences.Properties.cs @@ -0,0 +1,170 @@ +// +// RoslynPreferences.Bound.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Xml.Linq; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; +using Microsoft.CodeAnalysis.Options; +using MonoDevelop.Core; + +namespace MonoDevelop.Ide.RoslynServices.Options +{ + partial class RoslynPreferences + { + // Holds the mapping of key -> property updater + readonly Dictionary<string, Action<object>> wrapMap = new Dictionary<string, Action<object>> (); + + internal bool TryGet (OptionKey key, out string propertyKey, out object value) + { + // Check for roaming/profile properties + propertyKey = key.GetPropertyName (); + if (propertyKey != null) { + var defaultValue = key.Option.DefaultValue; + if (TryGetSerializationMethods<object> (key, out var serializer, out var deserializer)) + defaultValue = serializer (defaultValue); + + value = PropertyService.Get (propertyKey, defaultValue); + return true; + } + + value = default (object); + return false; + } + + internal bool TryGetUpdater (OptionKey key, out string propertyKey, out Action<object> updater) { + propertyKey = key.GetPropertyName (); + if (propertyKey != null) + return wrapMap.TryGetValue (propertyKey, out updater); + + updater = null; + return false; + } + + /// <summary> + /// Wrap the specified <see cref="IOption"/> using its default value, optionally migrating an old <see cref="ConfigurationProperty{T}"/>. + /// This is a two-way binding of a property, both update sources (roslyn, IDE) update the other. + /// The key for the property is computed by the underlying option's <see cref="OptionStorageLocation"/>s. + /// </summary> + /// <returns>The configuration property.</returns> + /// <param name="optionKey">Roslyn option.</param> + /// <param name="monodevelopPropertyName">The property name to migrate from.</param> + /// <typeparam name="T">The property type</typeparam> + public ConfigurationProperty<T> Wrap<T> (OptionKey optionKey, string monodevelopPropertyName = null) + => Wrap (optionKey, (T)optionKey.Option.DefaultValue, monodevelopPropertyName); + + /// <summary> + /// Wrap the specified <see cref="IOption"/> using its default value, optionally migrating an old <see cref="ConfigurationProperty{T}"/>. + /// This is a two-way binding of a property, both update sources (roslyn, IDE) update the other. + /// The key for the property is computed by the underlying option's <see cref="OptionStorageLocation"/>s. + /// </summary> + /// <returns>The configuration property.</returns> + /// <param name="optionKey">Roslyn option.</param> + /// <param name="defaultValue">The overridden default value.</param> + /// <param name="monodevelopPropertyName">The property name to migrate from.</param> + /// <typeparam name="T">The property type</typeparam> + public ConfigurationProperty<T> Wrap<T> (OptionKey optionKey, T defaultValue, string monodevelopPropertyName = null) + { + var name = optionKey.GetPropertyName (); + + ConfigurationProperty<T> result; + + // It is unfortunate, but roslyn has a special serialization mechanism, so use that. + if (TryGetSerializationMethods<T> (optionKey, out var serializer, out var deserializer)) { + var prop = new WrappedConfigurationProperty<T> (name, monodevelopPropertyName, defaultValue, serializer, deserializer); + wrapMap.Add (name, value => prop.SetSerializedValue ((string)value)); + result = prop; + } else { + result = ConfigurationProperty.Create (name, defaultValue, monodevelopPropertyName); + wrapMap.Add (name, value => result.Value = (T)value); + } + + return result; + } + + static bool TryGetSerializationMethods<T> (OptionKey optionKey, out Func<T, string> serializer, out Func<string, T> deserializer) + { + // It is unfortunate, but roslyn has a special serialization mechanism, so use that. + if (IsOfGenericType (optionKey.Option.Type, typeof (CodeStyleOption<>))) { + var fromXElement = optionKey.Option.Type.GetMethod ("FromXElement", BindingFlags.Public | BindingFlags.Static); + + serializer = value => ((ICodeStyleOption)value)?.ToXElement ().ToString (); + deserializer = serializedValue => serializedValue != null ? (T)fromXElement.Invoke (null, new object [] { XElement.Parse (serializedValue) }) : default(T); + return true; + } + + if (typeof (NamingStylePreferences) == optionKey.Option.Type) { + serializer = value => ((NamingStylePreferences)(object)value)?.CreateXElement ().ToString (); + deserializer = serializedValue => serializedValue != null ? (T)(object)NamingStylePreferences.FromXElement (XElement.Parse (serializedValue)) : default(T); + return true; + } + + serializer = null; + deserializer = null; + return false; + } + + static bool IsOfGenericType (Type type, Type genericType) + { + while (type != null && type != typeof (object)) { + var cur = type.IsGenericType ? type.GetGenericTypeDefinition () : type; + if (genericType == cur) + return true; + + type = type.BaseType; + } + return false; + } + + class WrappedConfigurationProperty<T> : ConfigurationProperty<T> + { + readonly ConfigurationProperty<string> underlying; + readonly Func<T, string> serializer; + readonly Func<string, T> deserializer; + + public WrappedConfigurationProperty (string name, string monodevelopPropertyName, T defaultValue, Func<T, string> serializer, Func<string, T> deserializer) + { + this.serializer = serializer; + this.deserializer = deserializer; + + underlying = ConfigurationProperty.Create(name, serializer (defaultValue), monodevelopPropertyName); + } + + protected sealed override T OnGetValue () => deserializer (underlying.Value); + protected sealed override bool OnSetValue (T value) + { + underlying.Value = serializer (value); + return true; + } + + internal void SetSerializedValue (string value) + { + underlying.Value = value; + } + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices.Options/RoslynPreferences.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices.Options/RoslynPreferences.cs new file mode 100644 index 0000000000..4d06cae67e --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices.Options/RoslynPreferences.cs @@ -0,0 +1,83 @@ +// +// RoslynPreferences.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Completion; +using Microsoft.CodeAnalysis.Editor.Options; +using Microsoft.CodeAnalysis.Editor.Implementation.TodoComments; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Options; +using MonoDevelop.Core; +using MonoDevelop.Ide.Composition; +using Roslyn.Utilities; + +namespace MonoDevelop.Ide.RoslynServices.Options +{ + sealed partial class RoslynPreferences + { + internal PerLanguagePreferences CSharp => languageConfigs [LanguageNames.CSharp]; + public PerLanguagePreferences For (string languageName) => languageConfigs [languageName]; + + // Preferences migrated from the IDE to roslyn keys are held here. + internal const string globalKey = "MonoDevelop.RoslynPreferences"; + + readonly Dictionary<string, PerLanguagePreferences> languageConfigs = new Dictionary<string, PerLanguagePreferences> (); + + internal RoslynPreferences () + { + foreach (var language in RoslynService.AllLanguages) + languageConfigs.Add (language, new PerLanguagePreferences (language, this)); + } + + public class PerLanguagePreferences + { + public readonly ConfigurationProperty<bool> PlaceSystemNamespaceFirst; + public readonly ConfigurationProperty<bool> SeparateImportDirectiveGroups; + public readonly ConfigurationProperty<bool> SuggestForTypesInNuGetPackages; + + internal PerLanguagePreferences (string language, RoslynPreferences preferences) + { + PlaceSystemNamespaceFirst = preferences.Wrap<bool> ( + new OptionKey (Microsoft.CodeAnalysis.Editing.GenerationOptions.PlaceSystemNamespaceFirst, language), + language + ".PlaceSystemNamespaceFirst" + ); + + SeparateImportDirectiveGroups = preferences.Wrap<bool> ( + new OptionKey (Microsoft.CodeAnalysis.Editing.GenerationOptions.SeparateImportDirectiveGroups, language), + language + ".SeparateImportDirectiveGroups" + ); + + SuggestForTypesInNuGetPackages = preferences.Wrap ( + new OptionKey (Microsoft.CodeAnalysis.SymbolSearch.SymbolSearchOptions.SuggestForTypesInNuGetPackages, language), + true + ); + } + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices/RoslynService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices/RoslynService.cs new file mode 100644 index 0000000000..ee7c983216 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices/RoslynService.cs @@ -0,0 +1,65 @@ +// +// RoslynService.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Utilities; +using MonoDevelop.Core; +using MonoDevelop.Ide.RoslynServices.Options; + +namespace MonoDevelop.Ide.RoslynServices +{ + class RoslynService + { + internal RoslynService () + { + // Initialize Option Persisters + } + + internal static IEnumerable<string> AllLanguages { + get { + yield return LanguageNames.CSharp; + yield return LanguageNames.FSharp; + yield return LanguageNames.VisualBasic; + } + } + + internal static void Initialize () + { + Runtime.AssertMainThread (); + + // Initialize Roslyn foreground thread data. + ForegroundThreadAffinitizedObject.CurrentForegroundThreadData = new ForegroundThreadData ( + Thread.CurrentThread, + Runtime.MainTaskScheduler, + ForegroundThreadDataInfo.CreateDefault (ForegroundThreadDataKind.ForcedByPackageInitialize) + ); + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplateWizard.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplateWizard.cs index de44190d15..08ff2a23ab 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplateWizard.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplateWizard.cs @@ -47,6 +47,13 @@ namespace MonoDevelop.Ide.Templates get { return 1; } } + public event EventHandler TotalPagesChanged; + + protected void OnTotalPagesChanged () + { + TotalPagesChanged?.Invoke (this, EventArgs.Empty); + } + ProjectCreateParameters parameters; public ProjectCreateParameters Parameters { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplateWizardProvider.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplateWizardProvider.cs index 8210231d86..c6a9db43f5 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplateWizardProvider.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplateWizardProvider.cs @@ -35,6 +35,7 @@ namespace MonoDevelop.Ide.Templates {
class TemplateWizardProvider
{
+ TemplateWizard currentWizard;
WizardPage currentWizardPage;
List<WizardPage> cachedWizardPages = new List<WizardPage> ();
@@ -43,7 +44,19 @@ namespace MonoDevelop.Ide.Templates public bool IsFirstPage { get; private set; }
public bool IsLastPage { get; private set; }
public int CurrentPageNumber { get; private set; }
- public TemplateWizard CurrentWizard { get; private set; }
+
+ public TemplateWizard CurrentWizard {
+ get { return currentWizard; }
+ private set {
+ if (currentWizard != null) {
+ currentWizard.TotalPagesChanged -= OnTotalPagesChanged;
+ }
+ currentWizard = value;
+ if (currentWizard != null) {
+ currentWizard.TotalPagesChanged += OnTotalPagesChanged;
+ }
+ }
+ }
public WizardPage CurrentWizardPage {
get { return currentWizardPage; }
@@ -81,6 +94,19 @@ namespace MonoDevelop.Ide.Templates }
}
+ void OnTotalPagesChanged (object sender, EventArgs e) + {
+ // Note: if the user goes back and changes values in a previous page,
+ // it's possible for the number of wizard pages to change. Remove any
+ // pages beyond the current page from the cache.
+ for (int i = cachedWizardPages.Count; i > CurrentPageNumber; i--) {
+ cachedWizardPages[i - 1].Dispose ();
+ cachedWizardPages.RemoveAt (i - 1);
+ }
+
+ IsLastPage = (CurrentPageNumber == CurrentWizard.TotalPages); + }
+
public bool MoveToFirstPage (SolutionTemplate template, ProjectCreateParameters parameters)
{
Reset ();
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/GlobalOptionPersister.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/GlobalOptionPersister.cs deleted file mode 100644 index 406c8cbb7c..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/GlobalOptionPersister.cs +++ /dev/null @@ -1,123 +0,0 @@ -// -// GlobalOptionPersister.cs -// -// Author: -// Mikayla Hutchinson <m.j.hutchinson@gmail.com> -// -// Copyright (c) 2017 Microsoft Corp. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Composition; -using System.Linq; -using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Options.Providers; -using MonoDevelop.Projects.Policies; -using MonoDevelop.Ide.Gui.Content; - -namespace MonoDevelop.Ide.TypeSystem -{ - [Export(typeof(IOptionPersister))] - class GlobalOptionPersister : IOptionPersister - { - public bool TryFetch (OptionKey optionKey, out object value) - { - if (optionKey.Option == FormattingOptions.UseTabs) { - value = !GetTextPolicyForLanguage (optionKey.Language).TabsToSpaces; - return true; - } - - if (optionKey.Option == FormattingOptions.TabSize) { - value = GetTextPolicyForLanguage (optionKey.Language).TabWidth; - return true; - } - - if (optionKey.Option == FormattingOptions.IndentationSize) { - value = GetTextPolicyForLanguage (optionKey.Language).IndentWidth; - return true; - } - - if (optionKey.Option == FormattingOptions.NewLine) { - value = GetTextPolicyForLanguage (optionKey.Language).GetEolMarker (); - return true; - } - - //use this for checking for options we could be handling - //PrintOptionKey(optionKey); - - value = null; - return false; - } - - TextStylePolicy GetTextPolicyForLanguage (string language) - { - var mimeChain = DesktopService.GetMimeTypeInheritanceChainForRoslynLanguage (language); - if (mimeChain == null) { - throw new Exception ($"Unknown Roslyn language {language}"); - } - return PolicyService.GetDefaultPolicy<TextStylePolicy> (mimeChain); - } - - public bool TryPersist (OptionKey optionKey, object value) - { - return false; - } - - static void PrintOptionKey (OptionKey optionKey) - { - Console.WriteLine ($"Name '{optionKey.Option.Name}' Language '{optionKey.Language}' LanguageSpecific'{optionKey.Option.IsPerLanguage}'"); - - var locations = optionKey.Option.StorageLocations; - if(locations.IsDefault) { - return; - } - - foreach (var loc in locations) { - switch (loc) { - case RoamingProfileStorageLocation roaming: - Console.WriteLine ($" roaming: {roaming.GetKeyNameForLanguage (optionKey.Language)}"); - break; - case LocalUserProfileStorageLocation local: - Console.WriteLine ($" local: {local.KeyName}"); - break; - case EditorConfigStorageLocation<int> edconf: - Console.WriteLine ($" editorconfig: {edconf.KeyName}"); - break; - case EditorConfigStorageLocation<string> edconf: - Console.WriteLine ($" editorconfig: {edconf.KeyName}"); - break; - case EditorConfigStorageLocation<bool> edconf: - Console.WriteLine ($" editorconfig: {edconf.KeyName}"); - break; - case EditorConfigStorageLocation<Microsoft.CodeAnalysis.CodeStyle.CodeStyleOption<bool>> edconf: - Console.WriteLine ($" editorconfig: {edconf.KeyName}"); - break; - default: - Console.WriteLine ($" unknown: {loc.GetType ()}"); - break; - } - } - } - - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj index cef6be3fb5..6c47dd5da2 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj @@ -9527,7 +9527,6 @@ <Compile Include="MonoDevelop.Ide.CodeCompletion\CompletionTriggerReason.cs" /> <Compile Include="MonoDevelop.Ide.CodeCompletion\CompletionSelectionStatus.cs" /> <Compile Include="MonoDevelop.Ide.TypeSystem\EditorNotificationServiceFactory.cs" /> - <Compile Include="MonoDevelop.Ide.TypeSystem\GlobalOptionPersister.cs" /> <Compile Include="MonoDevelop.Ide.TypeSystem\TextPolicyDocumentOptionsProvider.cs" /> <Compile Include="MonoDevelop.Ide.Templates\MicrosoftTemplateEngineProjectTemplatingProvider.cs" /> <Compile Include="MonoDevelop.Ide.Templates\MicrosoftTemplateEngineSolutionTemplate.cs" /> @@ -9643,6 +9642,12 @@ <Compile Include="MonoDevelop.Ide.RoslynServices\MonoDevelopWorkspaceCacheService.cs" /> <Compile Include="MonoDevelop.Components.MainToolbar\RoslynSearchCategory.cs" /> <Compile Include="MonoDevelop.Components.MainToolbar\RoslynSearchCategory.DeclaredSymbolInfoResult.cs" /> + <Compile Include="MonoDevelop.Ide.RoslynServices\RoslynService.cs" /> + <Compile Include="MonoDevelop.Ide.RoslynServices.Options\MonoDevelopGlobalOptionPersister.cs" /> + <Compile Include="MonoDevelop.Ide.RoslynServices.Options\RoslynPreferences.cs" /> + <Compile Include="MonoDevelop.Ide.RoslynServices.Options\OptionsExtensions.cs" /> + <Compile Include="MonoDevelop.Ide.RoslynServices.Options\RoslynPreferences.Properties.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Extension\CompletionStatistics.cs" /> </ItemGroup> <ItemGroup> <None Include="Makefile.am" /> @@ -9691,6 +9696,7 @@ <Folder Include="MonoDevelop.Ide.EditorConfig\" /> <Folder Include="Gui\" /> <Folder Include="MonoDevelop.Ide.RoslynServices\" /> + <Folder Include="MonoDevelop.Ide.RoslynServices.Options\" /> </ItemGroup> <ItemGroup> <Content Include="gtkrc"> @@ -9715,6 +9721,7 @@ <InternalsVisibleTo Include="MonoDevelop.Debugger" /> <InternalsVisibleTo Include="MonoDevelop.DesignerSupport" /> <InternalsVisibleTo Include="MonoDevelop.Ide.Tests" /> + <InternalsVisibleTo Include="IdeUnitTests" /> <InternalsVisibleTo Include="MonoDevelop.SourceEditor" /> <InternalsVisibleTo Include="Xamarin.OSXEditor" /> <InternalsVisibleTo Include="MonoDevelop.Refactoring" /> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs index 505890829c..ee735c6bba 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs @@ -49,6 +49,8 @@ using MonoDevelop.Ide.TypeSystem; using MonoDevelop.Ide.Extensions; using MonoDevelop.Ide.Templates; using System.Threading.Tasks; +using MonoDevelop.Ide.RoslynServices.Options; +using MonoDevelop.Ide.RoslynServices; namespace MonoDevelop.Ide { @@ -61,7 +63,7 @@ namespace MonoDevelop.Ide static CommandManager commandService; static IdeServices ideServices; static RootWorkspace workspace; - static IdePreferences preferences; + readonly static IdePreferences preferences; public const int CurrentRevision = 5; @@ -142,9 +144,7 @@ namespace MonoDevelop.Ide get { return ideServices; } } - public static IdePreferences Preferences { - get { return preferences; } - } + public static IdePreferences Preferences => preferences; public static bool IsInitialized { get { @@ -339,7 +339,11 @@ namespace MonoDevelop.Ide } var filteredFiles = new List<FileOpenInformation> (); - bool closeCurrent = true; + + Gdk.ModifierType mtype = Components.GtkWorkarounds.GetCurrentKeyModifiers (); + bool closeCurrent = !mtype.HasFlag (Gdk.ModifierType.ControlMask); + if (Platform.IsMac && closeCurrent) + closeCurrent = !mtype.HasFlag (Gdk.ModifierType.Mod2Mask); foreach (var file in files) { if (Services.ProjectService.IsWorkspaceItemFile (file.FileName) || @@ -631,5 +635,7 @@ namespace MonoDevelop.Ide public TemplatingService TemplatingService { get { return templatingService.Value; } } + + internal RoslynService RoslynService { get; } = new RoslynService (); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdePreferences.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdePreferences.cs index c223d859b1..829da09705 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdePreferences.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdePreferences.cs @@ -33,6 +33,7 @@ using MonoDevelop.Ide.Fonts; using MonoDevelop.Ide.Editor; using MonoDevelop.Ide.Editor.Highlighting; using System.Linq; +using MonoDevelop.Ide.RoslynServices.Options; namespace MonoDevelop.Ide { @@ -66,8 +67,10 @@ namespace MonoDevelop.Ide public class IdePreferences { + internal RoslynPreferences Roslyn { get; } internal IdePreferences () { + Roslyn = new RoslynPreferences (); } public readonly ConfigurationProperty<bool> EnableInstrumentation = Runtime.Preferences.EnableInstrumentation; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs index 96dd3c1e75..95148cd8d3 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs @@ -39,6 +39,9 @@ using System.Text; using System.Text.RegularExpressions; using System.Linq; +using Microsoft.CodeAnalysis.Utilities; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; + using Mono.Unix; using Mono.Addins; @@ -144,7 +147,10 @@ namespace MonoDevelop.Ide // Set a synchronization context for the main gtk thread SynchronizationContext.SetSynchronizationContext (DispatchService.SynchronizationContext); Runtime.MainSynchronizationContext = SynchronizationContext.Current; - + + // Initialize Roslyn's synchronization context + RoslynServices.RoslynService.Initialize (); + AddinManager.AddinLoadError += OnAddinError; var startupInfo = new StartupInfo (args); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Services.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Services.cs index 6d1a6f0cdf..565d4a1797 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Services.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Services.cs @@ -64,6 +64,7 @@ namespace MonoDevelop.Ide internal static TimerCounter CompositionCache = InstrumentationService.CreateTimerCounter ("MEF Composition From Cache", "IDE", id: "Ide.Startup.Composition.Cache"); internal static TimerCounter CompositionSave = InstrumentationService.CreateTimerCounter ("MEF Composition Save", "IDE", id: "Ide.CompositionSave"); internal static TimerCounter ProcessCodeCompletion = InstrumentationService.CreateTimerCounter ("Process Code Completion", "IDE", id: "Ide.ProcessCodeCompletion", logMessages:false); + internal static Counter CodeCompletionStats = InstrumentationService.CreateCounter ("Code Completion Statistics", "IDE", id:"Ide.CodeCompletionStatistics"); internal static class ParserService { public static TimerCounter FileParsed = InstrumentationService.CreateTimerCounter ("File parsed", "Parser Service"); diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices.Options/MockOptionService.cs b/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices.Options/MockOptionService.cs new file mode 100644 index 0000000000..1273dfbe85 --- /dev/null +++ b/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices.Options/MockOptionService.cs @@ -0,0 +1,141 @@ +// +// MockOptionService.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Options; + +namespace MonoDevelop.Ide.RoslynServices.Options +{ + class MockOptionService : IGlobalOptionService, IOptionService + { + IOptionPersister persister; + object _gate = new object (); + HashSet<IOption> _options = new HashSet<IOption> (); + ImmutableDictionary<OptionKey, object> _currentValues = ImmutableDictionary.Create<OptionKey, object> (); + + public void RegisterPersister (IOptionPersister persister) => this.persister = persister; + + public void RegisterOption (IOption option) => _options.Add (option); + + public IEnumerable<IOption> GetRegisteredOptions () => _options; + + public T GetOption<T> (Option<T> option) => (T)GetOption (new OptionKey (option, language: null)); + + public T GetOption<T> (PerLanguageOption<T> option, string language) => (T)GetOption (new OptionKey (option, language)); + + public bool GetOptionInternal (OptionKey optionKey, out object value) + { + return _currentValues.TryGetValue (optionKey, out value); + } + + public object GetOption (OptionKey optionKey) + { + lock (_gate) { + if (GetOptionInternal (optionKey, out var value)) + return value; + + value = LoadOptionFromSerializerOrGetDefault (optionKey); + _currentValues = _currentValues.Add (optionKey, value); + return value; + } + } + + object LoadOptionFromSerializerOrGetDefault (OptionKey optionKey) + { + if (persister.TryFetch (optionKey, out var deserializedValue)) + return deserializedValue; + + return optionKey.Option.DefaultValue; + } + + public void SetOptions (OptionSet optionSet) + { + var workspaceOptionSet = (WorkspaceOptionSet)optionSet; + + var changedOptions = new List<OptionChangedEventArgs> (); + + lock (_gate) { + foreach (var optionKey in workspaceOptionSet.GetAccessedOptions ()) { + var setValue = optionSet.GetOption (optionKey); + var currentValue = GetOption (optionKey); + + if (object.Equals (currentValue, setValue)) { + // Identical, so nothing is changing + continue; + } + + // The value is actually changing, so update + changedOptions.Add (new OptionChangedEventArgs (optionKey, setValue)); + + _currentValues = _currentValues.SetItem (optionKey, setValue); + + persister.TryPersist (optionKey, setValue); + } + } + + // Outside of the lock, raise the events on our task queue. + RaiseEvents (changedOptions); + } + + public void RefreshOption (OptionKey optionKey, object newValue) + { + lock (_gate) { + if (_currentValues.TryGetValue (optionKey, out var oldValue)) { + if (object.Equals (oldValue, newValue)) { + // Value is still the same, no reason to raise events + return; + } + } + + _currentValues = _currentValues.SetItem (optionKey, newValue); + } + + RaiseEvents (new List<OptionChangedEventArgs> { new OptionChangedEventArgs (optionKey, newValue) }); + } + + void RaiseEvents (List<OptionChangedEventArgs> changedOptions) + { + var optionChanged = OptionChanged; + if (optionChanged != null) { + foreach (var changedOption in changedOptions) { + optionChanged (this, changedOption); + } + } + } + + public OptionSet GetOptions () => new WorkspaceOptionSet (this); + + public void RegisterDocumentOptionsProvider (IDocumentOptionsProvider documentOptionsProvider) => throw new NotImplementedException (); + + public Task<OptionSet> GetUpdatedOptionSetForDocumentAsync (Document document, OptionSet optionSet, CancellationToken cancellationToken) => throw new NotImplementedException (); + + public event EventHandler<OptionChangedEventArgs> OptionChanged; + } +} diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices.Options/MonoDevelopOptionPersisterIntegrationTests.cs b/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices.Options/MonoDevelopOptionPersisterIntegrationTests.cs new file mode 100644 index 0000000000..cec196cdb5 --- /dev/null +++ b/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices.Options/MonoDevelopOptionPersisterIntegrationTests.cs @@ -0,0 +1,50 @@ +// +// RoamingMonoDevelopProfileOptionPersister.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// 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.Linq; +using System.Threading.Tasks; +using NUnit.Framework; +using Microsoft.CodeAnalysis.Options; +using MonoDevelop.Ide.Composition; + +namespace MonoDevelop.Ide.RoslynServices.Options +{ + [TestFixture] + public class MonoDevelopOptionPersisterIntegrationTests : IdeTestBase + { + [Test] + public async Task ServiceIsRegistered () + { + if (!IdeApp.IsInitialized) + IdeApp.Initialize (new Core.ProgressMonitor ()); + + await CompositionManager.InitializeAsync (); + + var values = CompositionManager.GetExportedValues<IOptionPersister> (); + + var service = values.OfType<MonoDevelopGlobalOptionPersister> ().Single (); + } + } +} diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices.Options/MonoDevelopOptionPersisterTests.cs b/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices.Options/MonoDevelopOptionPersisterTests.cs new file mode 100644 index 0000000000..131054e120 --- /dev/null +++ b/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices.Options/MonoDevelopOptionPersisterTests.cs @@ -0,0 +1,313 @@ +// +// RoamingMonoDevelopProfileOptionPersisterTests.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Linq; +using System.Threading.Tasks; +using NUnit.Framework; +using Microsoft.CodeAnalysis.Options; +using MonoDevelop.Ide.Composition; +using MonoDevelop.Core; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; +using System.Collections.Immutable; +using System.Threading; + +namespace MonoDevelop.Ide.RoslynServices.Options +{ + [TestFixture] + public class MonoDevelopOptionPersisterTests : OptionsTestBase + { + public abstract class SerializationTestCase : IOption + { + public readonly object Value; + public readonly object SerializedValue; + public readonly bool Success; + + SerializationTestCase (object value, object serializedValue, bool success, Type type) + { + Value = value; + SerializedValue = serializedValue; + Success = success; + Type = type; + } + + internal abstract OptionKey Wrap (RoslynPreferences preferences); + + // Mostly pass through. + public static SerializationTestCase Ok<T> (T value) => new SerializationTestCaseT<T> (value, value, true, typeof (T)); + + // Serialized value is transformed. + public static SerializationTestCase Ok<T> (T value, object serializedValue) => new SerializationTestCaseT<T> (value, serializedValue, true, typeof (T)); + + // It fails, whatever happens. + public static SerializationTestCase Fail<T> (object value, object serializedValue) => new SerializationTestCaseT<T> (value, serializedValue, false, typeof (T)); + + public string Feature => "feature"; + public string Name => "name"; + public Type Type { get; } + public object DefaultValue => Type.IsValueType ? Activator.CreateInstance (Type) : null; + public bool IsPerLanguage => false; + public ImmutableArray<OptionStorageLocation> StorageLocations { get; } = ImmutableArray.Create<OptionStorageLocation> (new RoamingProfileStorageLocation ("feature.C#")); + + class SerializationTestCaseT<T> : SerializationTestCase + { + public SerializationTestCaseT (object value, object serializedValue, bool success, Type type) : base (value, serializedValue, success, type) + { + } + + internal override OptionKey Wrap (RoslynPreferences preferences) + { + var optionKey = new OptionKey (this); + preferences.Wrap<T> (optionKey); + return optionKey; + } + } + } + + static CodeStyleOption<bool> boolOption = + new CodeStyleOption<bool> (true, NotificationOption.Suggestion); + static CodeStyleOption<ExpressionBodyPreference> enumOption = + new CodeStyleOption<ExpressionBodyPreference> (ExpressionBodyPreference.WhenOnSingleLine, NotificationOption.Suggestion); + static NamingStylePreferences namingOption = NamingStylePreferences.Default; + + SerializationTestCase [] SerializationTestCases = { + // Passing test cases + SerializationTestCase.Ok (true), + SerializationTestCase.Ok (PlatformID.Unix), + SerializationTestCase.Ok (boolOption, boolOption.ToXElement ().ToString ()), + SerializationTestCase.Ok (enumOption, enumOption.ToXElement ().ToString ()), + SerializationTestCase.Ok (namingOption, namingOption.CreateXElement ().ToString ()), + SerializationTestCase.Ok ("string"), + }; + + [TestCaseSource (nameof (SerializationTestCases))] + public void TestSerialization (SerializationTestCase testCase) + { + var (preferences, persister) = Setup (); + + using (persister) { + var optionKey = testCase.Wrap (preferences); + + var property = optionKey.GetPropertyName (); + + // Try persisting it. + Assert.AreEqual (testCase.Success, persister.TryPersist (optionKey, testCase.Value)); + if (!testCase.Success) { + Assert.IsFalse (PropertyService.HasValue (property)); + return; + } + + // See if we can deserialize it back as-is + Assert.AreEqual (testCase.SerializedValue, PropertyService.Get<object> (property)); + Assert.AreEqual (testCase.Success, persister.TryFetch (optionKey, out var deserializedValue)); + Assert.AreEqual (testCase.Value, deserializedValue); + } + } + + SerializationTestCase [] DeserializationTestCases = { + SerializationTestCase.Ok(true, 3), + SerializationTestCase.Ok(true, 2L), + SerializationTestCase.Ok(false, 0), + SerializationTestCase.Ok(false, 0L), + SerializationTestCase.Ok<bool?>(true, true), + SerializationTestCase.Ok<bool?>(false, false), + SerializationTestCase.Ok<bool?>(null, null), + SerializationTestCase.Fail<PlatformID>(-1, 1.0), + SerializationTestCase.Fail<PlatformID> (-1, ulong.MaxValue), + SerializationTestCase.Ok(namingOption, namingOption.CreateXElement ().ToString()), + SerializationTestCase.Ok(boolOption, boolOption.ToXElement ().ToString()), + SerializationTestCase.Ok(enumOption, enumOption.ToXElement ().ToString()), + }; + + [TestCaseSource (nameof (DeserializationTestCases))] + public void TestDeserialization (SerializationTestCase testCase) + { + var (preferences, persister) = Setup (); + + using (persister) { + var optionKey = testCase.Wrap (preferences); + var property = optionKey.GetPropertyName (); + + // Set the value and deserialize it + PropertyService.Set (property, testCase.SerializedValue); + Assert.AreEqual (testCase.Success, persister.TryFetch (optionKey, out var deserialized)); + if (!testCase.Success) { + Assert.IsNull (deserialized); + return; + } + + Assert.AreEqual (testCase.Value, deserialized); + } + } + + [TestCaseSource (nameof(allKinds))] + public void TestFetchPersistString (StorageLocationKind kind) + { + TestFetchPersist (kind, "a", "b"); + } + + [TestCaseSource (nameof (allKinds))] + public void TestFetchPersistCodeOption (StorageLocationKind kind) + { + var value1 = new CodeStyleOption<bool> (true, NotificationOption.Suggestion); + var value2 = new CodeStyleOption<bool> (false, NotificationOption.Error); + TestFetchPersist (kind, value1, value2, CodeStyleOption<bool>.Default); + } + + [TestCaseSource (nameof (allKinds))] + public void TestFetchPersistNamingStyle (StorageLocationKind kind) + { + var defaultValue = NamingStylePreferences.Default; + var value1 = new NamingStylePreferences ( + defaultValue.SymbolSpecifications, + defaultValue.NamingStyles.WhereAsArray (x => x.Prefix == "I"), + defaultValue.NamingRules + ); + var value2 = new NamingStylePreferences ( + defaultValue.SymbolSpecifications, + defaultValue.NamingStyles.WhereAsArray (x => x.Prefix != "I"), + defaultValue.NamingRules + ); + + TestFetchPersist (kind, value1, value2, defaultValue); + } + + void TestFetchPersist<T> (StorageLocationKind kind, T value1, T value2, T defaultValue = default(T)) + { + var (preferences, persister) = Setup (); + bool shouldPersist = kind != StorageLocationKind.None; + + using (persister) { + foreach (var optionKey in GetOptionKeys (kind, defaultValue)) { + string propertyName = optionKey.GetPropertyName (); + shouldPersist = propertyName != null; + ConfigurationProperty<T> wrap = null; + if (propertyName != null) + wrap = preferences.Wrap<T> (optionKey); + + // Fetch, no items. + AssertFetch (optionKey, optionKey.Option.DefaultValue); + + // Persist with no item set + AssertPersist (optionKey, value1, wrap); + if (!shouldPersist) + continue; + + // Fetch after persist + AssertFetch (optionKey, value1); + + // Check if setting config property persists. + wrap.Value = value2; + AssertFetch (optionKey, value2); + + // Set null. + AssertPersist (optionKey, null, wrap); + AssertFetch (optionKey, defaultValue); + } + } + + void AssertFetch (OptionKey optionKey, object expectedValue) + { + // Check that we can grab the value and it is grabbed properly + Assert.AreEqual (shouldPersist, persister.TryFetch (optionKey, out var value)); + Assert.AreEqual (shouldPersist ? expectedValue : null, value); + } + + void AssertPersist (OptionKey optionKey, object value, ConfigurationProperty<T> wrap) + { + // Check that it either got persisted, on an option with a roaming profile + // or that it got discarded + Assert.AreEqual (shouldPersist, persister.TryPersist (optionKey, value)); + if (shouldPersist) + Assert.AreEqual (value, wrap.Value); + } + } + + [Test] + public void TestPropertiesModification () + { + var (preferences, persister, optionService) = SetupOptions (); + + using (persister) { + int optionChangedCount = 0; + optionService.OptionChanged += (o, e) => optionChangedCount++; + + var option = GetOption (defaultValue: "c"); + var propName = new OptionKey (option).GetPropertyName (); + PropertyService.Set (propName, "a"); + + Assert.AreEqual (0, optionChangedCount); + + // Notifications happen after the option is first queried. + Assert.AreEqual ("a", optionService.GetOption (option)); + bool changed = false; + optionService.OptionChanged += (o, e) => { + Assert.AreEqual ("b", e.Value); + Assert.AreEqual (option, e.Option); + changed = true; + }; + + PropertyService.Set (propName, "b"); + Assert.AreEqual (true, changed); + } + } + + [Test] + public void TestRoslynPropertyWrapAndMonitor () + { + var (preferences, persister, optionService) = SetupOptions (); + + using (persister) { + foreach (var option in GetOptionKeys<string> ()) { + var prop = preferences.Wrap (option, default (string)); + + // Initial values + Assert.AreEqual (null, prop.Value); + Assert.AreEqual (false, optionService.GetOptionInternal (option, out var tempValue)); + Assert.AreEqual (null, tempValue); + + // Check without monitor. + prop.Value = "a"; + Assert.AreEqual ("a", prop.Value); + Assert.AreEqual (false, optionService.GetOptionInternal (option, out tempValue)); + Assert.AreEqual (null, tempValue); + + // Check when monitoring. + persister.MonitorChanges (option.GetPropertyName (), option); + prop.Value = "b"; + Assert.AreEqual ("b", prop.Value); + Assert.AreEqual ("b", optionService.GetOption (option)); + + // Check option changes reflect in property + optionService.SetOptions (optionService.GetOptions ().WithChangedOption (option, "c")); + Assert.AreEqual ("c", prop.Value); + Assert.AreEqual ("c", optionService.GetOption (option)); + } + } + } + } +} diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices.Options/OptionsExtensionsTests.cs b/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices.Options/OptionsExtensionsTests.cs new file mode 100644 index 0000000000..dfe03c37ff --- /dev/null +++ b/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices.Options/OptionsExtensionsTests.cs @@ -0,0 +1,42 @@ +// +// OptionsExtensionsTests.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// 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 NUnit.Framework; + +namespace MonoDevelop.Ide.RoslynServices.Options +{ + public class OptionsExtensionsTests : OptionsTestBase + { + [TestCaseSource (nameof(allKinds))] + public void TestToPropertyNameOption (StorageLocationKind kind) + { + var optionKeys = GetOptionKeys<bool> (kind); + foreach (var option in optionKeys) { + string expected = GetExpectedPropertyName (kind, option.Language); + Assert.AreEqual (expected, option.GetPropertyName ()); + } + } + } +} diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices.Options/OptionsTestBase.cs b/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices.Options/OptionsTestBase.cs new file mode 100644 index 0000000000..10a11aa28c --- /dev/null +++ b/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices.Options/OptionsTestBase.cs @@ -0,0 +1,145 @@ +// +// OptionsTestHelper.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System;
+using System.Collections.Generic;
+using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Options; +using MonoDevelop.Core; +using NUnit.Framework; + +namespace MonoDevelop.Ide.RoslynServices.Options +{ + public enum StorageLocationKind + { + None, + Roaming, + UserProfile + } + + public abstract class OptionsTestBase + { + protected StorageLocationKind[] allKinds = { + StorageLocationKind.None, + StorageLocationKind.Roaming, + StorageLocationKind.UserProfile + }; + + const string featureRoaming = "feature.test"; + const string featureRoamingLanguage = "feature.%LANGUAGE%.test"; + const string featureUserProfile = @"Some\Feature"; + + [SetUp] + public void BaseSetUp() + { + PropertyService.Set (GetExpectedPropertyName (StorageLocationKind.Roaming), null); + PropertyService.Set (GetExpectedPropertyName (StorageLocationKind.UserProfile), null); + + foreach (var language in RoslynService.AllLanguages) + PropertyService.Set (GetExpectedPropertyName (StorageLocationKind.Roaming, language), null); + } + + OptionStorageLocation GetLocation (StorageLocationKind kind, bool isPerLanguage = false) + { + switch (kind) + { + case StorageLocationKind.None: + return null; + case StorageLocationKind.Roaming: + return new RoamingProfileStorageLocation (isPerLanguage ? featureRoamingLanguage : featureRoaming); + case StorageLocationKind.UserProfile: + return new LocalUserProfileStorageLocation (featureUserProfile); + } + throw new NotImplementedException (); + } + + protected Option<T> GetOption<T> (StorageLocationKind kind = StorageLocationKind.Roaming, T defaultValue = default (T)) + { + var option = new Option<T> ("feature option", "name", defaultValue, GetLocation (kind)); + // We need a way to mock property service in tests + var propName = new OptionKey (option).GetPropertyName (); + if (propName != null) + PropertyService.Set (propName, null); + + return option; + } + + protected PerLanguageOption<T> GetPerLanguageOption<T> (StorageLocationKind kind = StorageLocationKind.Roaming, T defaultValue = default (T), string language = null) + { + var option = new PerLanguageOption<T> ("feature language option", "name", defaultValue, GetLocation (kind, true)); + + var propName = new OptionKey (option, language).GetPropertyName (); + if (propName != null) + PropertyService.Set (propName, null); + + return option; + } + + protected string GetExpectedPropertyName (StorageLocationKind kind, string language = null) + { + switch (kind) + { + case StorageLocationKind.None: + return null; + case StorageLocationKind.Roaming: + if (language == null) + return featureRoaming; + + string substituteLanguageName = language == LanguageNames.CSharp ? "CSharp" : + language == LanguageNames.VisualBasic ? "VisualBasic" : + language; + + return featureRoamingLanguage.Replace ("%LANGUAGE%", substituteLanguageName); + case StorageLocationKind.UserProfile: + return featureUserProfile; + } + throw new NotImplementedException (); + } + + protected IEnumerable<OptionKey> GetOptionKeys<T> (StorageLocationKind kind = StorageLocationKind.Roaming, T defaultValue = default(T)) + { + yield return GetOption (kind, defaultValue); + + if (kind != StorageLocationKind.UserProfile) + yield return new OptionKey (GetPerLanguageOption (kind, defaultValue, LanguageNames.CSharp), LanguageNames.CSharp); + } + + internal (RoslynPreferences, MonoDevelopGlobalOptionPersister) Setup () + { + var (preferences, persister, _) = SetupOptions (); + return (preferences, persister); + } + + internal (RoslynPreferences, MonoDevelopGlobalOptionPersister, MockOptionService) SetupOptions () + { + var preferences = new RoslynPreferences (); + var optionsService = new MockOptionService (); + var persister = new MonoDevelopGlobalOptionPersister (optionsService, preferences); + optionsService.RegisterPersister (persister); + + return (preferences, persister, optionsService); + } + } +} diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices.Options/RoslynPreferencesTests.cs b/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices.Options/RoslynPreferencesTests.cs new file mode 100644 index 0000000000..afe112b1b1 --- /dev/null +++ b/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices.Options/RoslynPreferencesTests.cs @@ -0,0 +1,59 @@ +// +// RoslynPreferencesTests.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// 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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Options; +using MonoDevelop.Core; +using NUnit.Framework; + +namespace MonoDevelop.Ide.RoslynServices.Options +{ + [TestFixture] + public class RoslynPreferencesTests : OptionsTestBase + { + [Test] + public void TestRoslynPropertyMigration () + { + var preferences = new RoslynPreferences (); + + const string mdKey = "TEST_ROSLYN_MIGRATION_KEY"; + + foreach (var option in GetOptionKeys<bool> ()) { + PropertyService.Set (mdKey, true); + var prop = preferences.Wrap (option, default(bool), mdKey); + + Assert.AreEqual (true, PropertyService.Get<bool> (option.GetPropertyName ())); + Assert.AreEqual (true, prop.Value); + + Assert.AreEqual (false, PropertyService.HasValue (mdKey)); + + prop.Value = false; + Assert.AreEqual (false, PropertyService.Get<bool> (option.GetPropertyName ())); + Assert.AreEqual (false, prop.Value); + } + } + } +} diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices/RoslynServiceTests.cs b/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices/RoslynServiceTests.cs new file mode 100644 index 0000000000..d8bfd4dc88 --- /dev/null +++ b/main/tests/Ide.Tests/MonoDevelop.Ide.RoslynServices/RoslynServiceTests.cs @@ -0,0 +1,80 @@ +// +// RoslynService.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// 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 NUnit.Framework; +using Microsoft.CodeAnalysis.Utilities; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using MonoDevelop.Core; +using System.Threading; +using System.Threading.Tasks; + +namespace MonoDevelop.Ide.RoslynServices +{ + [TestFixture] + public class RoslynServiceTests : IdeTestBase + { + [TestCase (false, false)] + [TestCase (true, false)] + [TestCase (true, true)] + public async Task TestForegroundThread (bool initAgain, bool inBg) + { + if (initAgain) { + if (inBg) { + await Task.Run (() => { + Assert.Throws<InvalidOperationException> (() => RoslynService.Initialize ()); + }); + } + RoslynService.Initialize (); + } + + var obj = new ForegroundThreadAffinitizedObject (false); + + // FIXME: Roslyn does not about Xwt Synchronization context. + //Assert.AreEqual (ForegroundThreadDataKind.MonoDevelopGtk, obj.ForegroundKind); + Assert.AreEqual (Runtime.MainTaskScheduler, obj.ForegroundTaskScheduler); + Assert.IsTrue (obj.IsForeground ()); + + await Task.Run (() => { + Assert.IsFalse (obj.IsForeground ()); + }); + + int x = 0; + await obj.InvokeBelowInputPriority (() => { + Assert.IsTrue (obj.IsForeground ()); + x++; + }); + + Assert.AreEqual (1, x); + + await Task.Run (() => obj.InvokeBelowInputPriority (() => { + Assert.IsTrue (obj.IsForeground ()); + x++; + })); + + Assert.AreEqual (2, x); + } + } +} diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide.Tests.csproj b/main/tests/Ide.Tests/MonoDevelop.Ide.Tests.csproj index 92d8e22d4e..ae45a825b9 100644 --- a/main/tests/Ide.Tests/MonoDevelop.Ide.Tests.csproj +++ b/main/tests/Ide.Tests/MonoDevelop.Ide.Tests.csproj @@ -69,6 +69,11 @@ <Reference Include="System.Collections.Immutable"> <HintPath>..\..\build\bin\System.Collections.Immutable.dll</HintPath> </Reference> + <Reference Include="Microsoft.CodeAnalysis.EditorFeatures"> + <HintPath>..\..\build\bin\Microsoft.CodeAnalysis.EditorFeatures.dll</HintPath> + <Private>False</Private> + </Reference> + <Reference Include="System.Xml.Linq" /> </ItemGroup> <ItemGroup> <Compile Include="ProjectTemplateTests.cs" /> @@ -119,13 +124,8 @@ <Compile Include="MonoDevelop.SourceEditor\DebugTooltipTests.cs" /> <Compile Include="MonoDevelop.SourceEditor\JSonIndentEngineTests.cs" /> <Compile Include="MonoDevelop.SourceEditor\SourceEditorTests.cs" /> - <Compile Include="IdeTestBase.cs" /> <Compile Include="MonoDevelop.Ide\UserPreferencesTests.cs" /> <Compile Include="MonoDevelop.Ide.Projects\ResourcesTests.cs" /> - <Compile Include="MonoDevelop.Ide.Gui\TestViewContent.cs" /> - <Compile Include="MonoDevelop.Ide.Gui\TestWorkbenchWindow.cs" /> - <Compile Include="MonoDevelop.Ide.Gui\TestDocument.cs" /> - <Compile Include="TypeSystemServiceTestExtensions.cs" /> <Compile Include="MonoDevelop.Ide.Editor\SkipCharSessionTests.cs" /> <Compile Include="MonoDevelop.Components\PangoUtilTests.cs" /> <Compile Include="MonoDevelop.Ide.Projects\DotNetCoreProjectTests.cs" /> @@ -136,10 +136,17 @@ <Compile Include="MonoDevelop.Ide.Tasks\CommentTasksProviderTests.cs" /> <Compile Include="MonoDevelop.Ide.Tasks\CommentTasksProviderTests.Controller.cs" /> <Compile Include="MonoDevelop.Ide.Composition\CompositionManager.CachingTests.cs" /> - <Compile Include="TextEditorExtensionTestBase.cs" /> <Compile Include="MonoDevelop.Ide.RoslynServices\MonoDevelopProjectCacheHostServiceTests.cs" /> <Compile Include="MonoDevelop.Ide\RoslynSearchCategoryTests.cs" /> <Compile Include="MonoDevelop.Ide.Editor\CodeSnippetTests.cs" /> + <Compile Include="MonoDevelop.Ide.RoslynServices\RoslynServiceTests.cs" /> + <Compile Include="MonoDevelop.Ide.RoslynServices.Options\OptionsExtensionsTests.cs" /> + <Compile Include="MonoDevelop.Ide.RoslynServices.Options\RoslynPreferencesTests.cs" /> + <Compile Include="MonoDevelop.Ide.RoslynServices.Options\OptionsTestBase.cs" /> + <Compile Include="MonoDevelop.Ide.RoslynServices.Options\MonoDevelopOptionPersisterIntegrationTests.cs" /> + <Compile Include="MonoDevelop.Ide.RoslynServices.Options\MockOptionService.cs" /> + <Compile Include="MonoDevelop.Ide.RoslynServices.Options\MonoDevelopOptionPersisterTests.cs" /> + <Compile Include="TypeForwarders.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup> @@ -196,6 +203,10 @@ <Name>MonoDevelop.SourceEditor</Name> <Private>False</Private> </ProjectReference> + <ProjectReference Include="..\IdeUnitTests\IdeUnitTests.csproj"> + <Project>{F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}</Project> + <Name>IdeUnitTests</Name> + </ProjectReference> </ItemGroup> <ItemGroup> <Folder Include="MonoDevelop.Ide\" /> @@ -209,6 +220,7 @@ <Folder Include="MonoDevelop.Ide.Tasks\" /> <Folder Include="MonoDevelop.Ide.Composition\" /> <Folder Include="MonoDevelop.Ide.RoslynServices\" /> + <Folder Include="MonoDevelop.Ide.RoslynServices.Options\" /> </ItemGroup> <ItemGroup> <None Include="packages.config" /> diff --git a/main/tests/Ide.Tests/TypeForwarders.cs b/main/tests/Ide.Tests/TypeForwarders.cs new file mode 100644 index 0000000000..1a9b20bef0 --- /dev/null +++ b/main/tests/Ide.Tests/TypeForwarders.cs @@ -0,0 +1,36 @@ +// +// AssemblyInfo.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Runtime.CompilerServices; + +[assembly: TypeForwardedTo (typeof (MonoDevelop.Ide.RoslynTestBase))] +[assembly: TypeForwardedTo (typeof (MonoDevelop.Ide.IdeTestBase))] +[assembly: TypeForwardedTo (typeof (MonoDevelop.Ide.TextEditorExtensionTestBase))] +[assembly: TypeForwardedTo (typeof (MonoDevelop.Ide.TypeSystemServiceTestExtensions))] + +[assembly: TypeForwardedTo (typeof (MonoDevelop.Ide.Gui.TestDocument))] +[assembly: TypeForwardedTo (typeof (MonoDevelop.Ide.Gui.TestViewContent))] +[assembly: TypeForwardedTo (typeof (MonoDevelop.Ide.Gui.TestWorkbenchWindow))]
\ No newline at end of file diff --git a/main/tests/Ide.Tests/IdeTestBase.cs b/main/tests/IdeUnitTests/IdeTestBase.cs index df6ac9a5e7..4e441b985d 100644 --- a/main/tests/Ide.Tests/IdeTestBase.cs +++ b/main/tests/IdeUnitTests/IdeTestBase.cs @@ -23,18 +23,16 @@ // 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 MonoDevelop.Core; -using MonoDevelop.Ide; using UnitTests; namespace MonoDevelop.Ide { - public class IdeTestBase: TestBase + public class IdeTestBase: RoslynTestBase { protected override void InternalSetup(string rootDir) { base.InternalSetup(rootDir); + Xwt.Application.Initialize(Xwt.ToolkitType.Gtk); Gtk.Application.Init(); DesktopService.Initialize(); diff --git a/main/tests/IdeUnitTests/IdeUnitTests.csproj b/main/tests/IdeUnitTests/IdeUnitTests.csproj new file mode 100644 index 0000000000..c5fa4163d6 --- /dev/null +++ b/main/tests/IdeUnitTests/IdeUnitTests.csproj @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="..\..\MonoDevelop.props" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}</ProjectGuid> + <OutputType>Library</OutputType> + <RootNamespace>IdeUnitTests</RootNamespace> + <AssemblyName>IdeUnitTests</AssemblyName> + <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>..\..\build\tests</OutputPath> + <DefineConstants>DEBUG;</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <Optimize>true</Optimize> + <DebugType>pdbonly</DebugType> + <OutputPath>..\..\build\tests</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + <DebugSymbols>true</DebugSymbols> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" /> + </ItemGroup> + <ItemGroup> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="IdeTestBase.cs" /> + <Compile Include="TextEditorExtensionTestBase.cs" /> + <Compile Include="TypeSystemServiceTestExtensions.cs" /> + <Compile Include="TestDocument.cs" /> + <Compile Include="TestViewContent.cs" /> + <Compile Include="TestWorkbenchWindow.cs" /> + <Compile Include="RoslynTestBase.cs" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\src\core\MonoDevelop.Core\MonoDevelop.Core.csproj"> + <Project>{7525BB88-6142-4A26-93B9-A30C6983390A}</Project> + <Name>MonoDevelop.Core</Name> + <Private>False</Private> + </ProjectReference> + <ProjectReference Include="..\..\src\core\MonoDevelop.Ide\MonoDevelop.Ide.csproj"> + <Project>{27096E7F-C91C-4AC6-B289-6897A701DF21}</Project> + <Name>MonoDevelop.Ide</Name> + <Private>False</Private> + </ProjectReference> + <ProjectReference Include="..\..\external\mono-addins\Mono.Addins\Mono.Addins.csproj"> + <Project>{91DD5A2D-9FE3-4C3C-9253-876141874DAD}</Project> + <Name>Mono.Addins</Name> + <Private>False</Private> + </ProjectReference> + <ProjectReference Include="..\UnitTests\UnitTests.csproj"> + <Project>{1497D0A8-AFF1-4938-BC22-BE79B358BA5B}</Project> + <Name>UnitTests</Name> + <Private>False</Private> + </ProjectReference> + <ProjectReference Include="..\..\external\xwt\Xwt\Xwt.csproj"> + <Project>{92494904-35FA-4DC9-BDE9-3A3E87AC49D3}</Project> + <Name>Xwt</Name> + <Private>False</Private> + </ProjectReference> + <ProjectReference Include="..\..\external\nrefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj"> + <Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project> + <Name>ICSharpCode.NRefactory</Name> + <Private>False</Private> + </ProjectReference> + <ProjectReference Include="..\..\external\guiunit\src\framework\GuiUnit_NET_4_5.csproj"> + <Project>{D12F0F7B-8DE3-43EC-BA49-41052D065A9B}</Project> + <Name>GuiUnit_NET_4_5</Name> + </ProjectReference> + </ItemGroup> + <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> +</Project>
\ No newline at end of file diff --git a/main/contrib/ICSharpCode.Decompiler/Makefile.am b/main/tests/IdeUnitTests/Makefile.am index c9cc87438f..c9cc87438f 100755..100644 --- a/main/contrib/ICSharpCode.Decompiler/Makefile.am +++ b/main/tests/IdeUnitTests/Makefile.am diff --git a/main/tests/IdeUnitTests/Properties/AssemblyInfo.cs b/main/tests/IdeUnitTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..ac498c32e8 --- /dev/null +++ b/main/tests/IdeUnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,49 @@ +// +// AssemblyInfo.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// 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.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle ("IdeUnitTests")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("Microsoft")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("Microsoft Inc.")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] diff --git a/main/tests/IdeUnitTests/RoslynTestBase.cs b/main/tests/IdeUnitTests/RoslynTestBase.cs new file mode 100644 index 0000000000..7d72c2a3a1 --- /dev/null +++ b/main/tests/IdeUnitTests/RoslynTestBase.cs @@ -0,0 +1,39 @@ +// +// RoslynTestBase.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// 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 UnitTests; + +namespace MonoDevelop.Ide +{ + public class RoslynTestBase : TestBase + { + protected override void InternalSetup (string rootDir) + { + RoslynServices.RoslynService.Initialize (); + base.InternalSetup (rootDir); + } + } +} diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide.Gui/TestDocument.cs b/main/tests/IdeUnitTests/TestDocument.cs index 6d0a9728ef..6d0a9728ef 100644 --- a/main/tests/Ide.Tests/MonoDevelop.Ide.Gui/TestDocument.cs +++ b/main/tests/IdeUnitTests/TestDocument.cs diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide.Gui/TestViewContent.cs b/main/tests/IdeUnitTests/TestViewContent.cs index 26a9340fb5..26a9340fb5 100644 --- a/main/tests/Ide.Tests/MonoDevelop.Ide.Gui/TestViewContent.cs +++ b/main/tests/IdeUnitTests/TestViewContent.cs diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide.Gui/TestWorkbenchWindow.cs b/main/tests/IdeUnitTests/TestWorkbenchWindow.cs index a8e15f4e76..a8e15f4e76 100644 --- a/main/tests/Ide.Tests/MonoDevelop.Ide.Gui/TestWorkbenchWindow.cs +++ b/main/tests/IdeUnitTests/TestWorkbenchWindow.cs diff --git a/main/tests/Ide.Tests/TextEditorExtensionTestBase.cs b/main/tests/IdeUnitTests/TextEditorExtensionTestBase.cs index ace5ba9e17..ace5ba9e17 100644 --- a/main/tests/Ide.Tests/TextEditorExtensionTestBase.cs +++ b/main/tests/IdeUnitTests/TextEditorExtensionTestBase.cs diff --git a/main/tests/Ide.Tests/TypeSystemServiceTestExtensions.cs b/main/tests/IdeUnitTests/TypeSystemServiceTestExtensions.cs index e18132b6ae..e18132b6ae 100644 --- a/main/tests/Ide.Tests/TypeSystemServiceTestExtensions.cs +++ b/main/tests/IdeUnitTests/TypeSystemServiceTestExtensions.cs diff --git a/main/tests/MacPlatform.Tests/MacPlatform.Tests.csproj b/main/tests/MacPlatform.Tests/MacPlatform.Tests.csproj index 18f6000545..0a43876f95 100644 --- a/main/tests/MacPlatform.Tests/MacPlatform.Tests.csproj +++ b/main/tests/MacPlatform.Tests/MacPlatform.Tests.csproj @@ -75,15 +75,17 @@ <ProjectReference Include="..\UnitTests\UnitTests.csproj"> <Project>{1497D0A8-AFF1-4938-BC22-BE79B358BA5B}</Project> <Name>UnitTests</Name> + <Private>False</Private> </ProjectReference> <ProjectReference Include="..\MonoDevelop.Core.Tests\MonoDevelop.Core.Tests.csproj"> <Project>{FDA43CAA-1C2A-4593-8601-3E2EE06D9E03}</Project> <Name>MonoDevelop.Core.Tests</Name> <Private>False</Private> </ProjectReference> - <ProjectReference Include="..\Ide.Tests\MonoDevelop.Ide.Tests.csproj"> - <Project>{73D4CC8B-BAB9-4A29-841B-F25C6311F067}</Project> - <Name>MonoDevelop.Ide.Tests</Name> + <ProjectReference Include="..\IdeUnitTests\IdeUnitTests.csproj"> + <Project>{F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}</Project> + <Name>IdeUnitTests</Name> + <Private>False</Private> </ProjectReference> </ItemGroup> </Project> diff --git a/main/tests/Makefile.am b/main/tests/Makefile.am index 1083883726..f9cf5c33fe 100644 --- a/main/tests/Makefile.am +++ b/main/tests/Makefile.am @@ -1,6 +1,6 @@ include $(top_srcdir)/xbuild.include -SUBDIRS=UnitTests MacPlatform.Tests UserInterfaceTests TestRunner Ide.Tests MonoDevelop.CSharpBinding.Tests WindowsPlatform.Tests MonoDevelop.Core.Tests MonoDevelop.Core.Tests.Addin MonoDevelop.Refactoring.Tests +SUBDIRS=UnitTests IdeUnitTests MacPlatform.Tests UserInterfaceTests TestRunner Ide.Tests MonoDevelop.CSharpBinding.Tests WindowsPlatform.Tests MonoDevelop.Core.Tests MonoDevelop.Core.Tests.Addin MonoDevelop.Refactoring.Tests MONO=mono$(SGEN_SUFFIX) RUN_TEST=$(MDTOOL_RUN) run-md-tests @@ -13,6 +13,7 @@ TEST_PROJECTS_MAC = \ MacPlatform.Tests/MacPlatform.Tests.csproj TEST_PROJECTS_COMMON = \ + IdeUnitTests/IdeUnitTests.csproj \ UnitTests/UnitTests.csproj \ $(top_srcdir)/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Tests/MonoDevelop.Debugger.Tests.csproj @@ -193,4 +194,4 @@ EXTRA_DIST = \ test-projects/console-project/ConsoleProject/Properties/AssemblyInfo.cs \ test-projects/console-project/ConsoleProject/Program.cs \ test-projects/console-project/ConsoleProject.sln -
\ No newline at end of file + diff --git a/main/tests/MonoDevelop.CSharpBinding.Tests/MonoDevelop.CSharpBinding.Tests.csproj b/main/tests/MonoDevelop.CSharpBinding.Tests/MonoDevelop.CSharpBinding.Tests.csproj index fbe81cbd45..8c9bb5c29c 100644 --- a/main/tests/MonoDevelop.CSharpBinding.Tests/MonoDevelop.CSharpBinding.Tests.csproj +++ b/main/tests/MonoDevelop.CSharpBinding.Tests/MonoDevelop.CSharpBinding.Tests.csproj @@ -199,6 +199,11 @@ <Name>Mono.Debugging</Name> <Private>False</Private> </ProjectReference> + <ProjectReference Include="..\IdeUnitTests\IdeUnitTests.csproj"> + <Project>{F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}</Project> + <Name>IdeUnitTests</Name> + <Private>False</Private> + </ProjectReference> </ItemGroup> <ItemGroup> </ItemGroup> diff --git a/main/tests/MonoDevelop.CSharpBinding.Tests/MonoDevelop.CSharpBinding/FoldingParserTests.cs b/main/tests/MonoDevelop.CSharpBinding.Tests/MonoDevelop.CSharpBinding/FoldingParserTests.cs index 2f5673949b..8c67810951 100644 --- a/main/tests/MonoDevelop.CSharpBinding.Tests/MonoDevelop.CSharpBinding/FoldingParserTests.cs +++ b/main/tests/MonoDevelop.CSharpBinding.Tests/MonoDevelop.CSharpBinding/FoldingParserTests.cs @@ -378,6 +378,31 @@ namespace Foo } } "); + } + + [Test] + public void TestIssue4868 () + { + Test (@" +[/* fold1 + fold1 */] + +namespace Foo +{ + [/* fold2 + fold2 */] + + class Test + { + public static void Main () + { + /* nofold + nofold + nofold */ + } + } +} + "); } } diff --git a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj index 7e96ca9eef..a1aa95b52f 100644 --- a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj +++ b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj @@ -107,6 +107,7 @@ <Compile Include="MonoDevelop.Core.Execution\BinaryMessageTests.cs" /> <Compile Include="MonoDevelop.Core\ProgressMonitorTests.cs" /> <Compile Include="MonoDevelop.Projects\DotNetCoreFileWatcherTests.cs" /> + <Compile Include="MonoDevelop.Core\InstrumentationTests.cs" /> </ItemGroup> <ItemGroup> <Folder Include="MonoDevelop.Core\" /> diff --git a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core/InstrumentationTests.cs b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core/InstrumentationTests.cs new file mode 100644 index 0000000000..53bedd87a8 --- /dev/null +++ b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core/InstrumentationTests.cs @@ -0,0 +1,215 @@ +// +// InstrumentationTests.cs +// +// Author: +// lluis <> +// +// Copyright (c) 2018 +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using MonoDevelop.Core.Instrumentation; +using NUnit.Framework; +using System.Threading; + +namespace MonoDevelop.Core +{ + [TestFixture] + public class InstrumentationTests + { + static List<CounterValue> counterValues = new List<CounterValue> (); + TestInstrumentationConsumer testConsumer; + + class CustomCounterMetadata: CounterMetadata + { + public int SomeMeasure { + get => GetProperty<int> ("SomeMeasure"); + set => SetProperty ("SomeMeasure", value); + } + } + + class TestInstrumentationConsumer : InstrumentationConsumer + { + public TestInstrumentationConsumer () + { + } + + public override bool SupportsCounter (Counter counter) + { + return counter.Name == "TestCounter"; + } + + public override IDisposable BeginTimer (TimerCounter counter, CounterValue value) + { + return new TimerTrackerTest { Value = value }; + } + + public override void ConsumeValue (Counter counter, CounterValue value) + { + counterValues.Add (value); + } + } + + class TimerTrackerTest: IDisposable + { + public CounterValue Value; + + public void Dispose () + { + counterValues.Add (Value); + } + } + + [SetUp] + public void Setup () + { + counterValues.Clear (); + testConsumer = new TestInstrumentationConsumer (); + InstrumentationService.RegisterInstrumentationConsumer (testConsumer); + } + + [TearDown] + public void TearDown () + { + InstrumentationService.UnregisterInstrumentationConsumer (testConsumer); + testConsumer = null; + } + + [Test] + public void TimerCounterWithMetadata () + { + var timer = InstrumentationService.CreateTimerCounter<CustomCounterMetadata> ("TestCounter"); + var t = timer.BeginTiming (); + t.Metadata.SomeMeasure = 4; + t.Dispose (); + Assert.AreEqual (1, counterValues.Count); + var value = counterValues [0]; + + Assert.IsNotNull (value.Metadata); + Assert.IsTrue (value.Metadata.TryGetValue ("SomeMeasure", out var measure)); + Assert.AreEqual ("4", measure); + Assert.IsFalse (value.Metadata.TryGetValue ("Result", out var result)); + } + + [Test] + public void TimerCounterWithNoMetadata () + { + var timer = InstrumentationService.CreateTimerCounter<CustomCounterMetadata> ("TestCounter"); + var t = timer.BeginTiming (); + t.Dispose (); + Assert.AreEqual (1, counterValues.Count); + var value = counterValues [0]; + + Assert.IsNull (value.Metadata); + } + + [Test] + public void TimerCounterWithCustomMetadata () + { + var timer = InstrumentationService.CreateTimerCounter<CustomCounterMetadata> ("TestCounter"); + var md = new CustomCounterMetadata { + SomeMeasure = 5, + Result = CounterResult.Success + }; + var t = timer.BeginTiming (md); + t.Dispose (); + Assert.AreEqual (1, counterValues.Count); + var value = counterValues [0]; + + Assert.IsNotNull (value.Metadata); + Assert.IsTrue (value.Metadata.TryGetValue ("SomeMeasure", out var measure)); + Assert.AreEqual ("5", measure); + Assert.IsTrue (value.Metadata.TryGetValue ("Result", out var result)); + Assert.AreEqual ("Success", result); + } + + [Test] + [TestCase (CounterResult.Failure)] + [TestCase (CounterResult.Success)] + [TestCase (CounterResult.UserCancel)] + [TestCase (CounterResult.UserFault)] + public void TimerCounterWithFailure (CounterResult testResult) + { + counterValues.Clear (); + var timer = InstrumentationService.CreateTimerCounter<CustomCounterMetadata> ("TestCounter"); + var t = timer.BeginTiming (); + t.Metadata.Result = testResult; + t.Dispose (); + Assert.AreEqual (1, counterValues.Count); + var value = counterValues [0]; + + Assert.IsNotNull (value.Metadata); + Assert.IsTrue (value.Metadata.TryGetValue ("Result", out var result)); + Assert.AreEqual (testResult.ToString(), result); + } + + [Test] + public void TimerCounterCancel () + { + var timer = InstrumentationService.CreateTimerCounter<CustomCounterMetadata> ("TestCounter"); + var cs = new CancellationTokenSource (); + var t = timer.BeginTiming (cs.Token); + t.Metadata.SomeMeasure = 4; + cs.Cancel (); + t.Dispose (); + Assert.AreEqual (1, counterValues.Count); + var value = counterValues [0]; + + Assert.IsNotNull (value.Metadata); + Assert.IsTrue (value.Metadata.TryGetValue ("SomeMeasure", out var measure)); + Assert.AreEqual ("4", measure); + Assert.IsTrue (value.Metadata.TryGetValue ("Result", out var result)); + Assert.AreEqual ("UserCancel", result); + } + + [Test] + public void CounterWithMetadata () + { + var counter = InstrumentationService.CreateCounter<CustomCounterMetadata> ("TestCounter"); + + counter.Inc (new CustomCounterMetadata { + SomeMeasure = 5, + Result = CounterResult.Success + }); + + Assert.AreEqual (1, counterValues.Count); + var value = counterValues [0]; + + Assert.IsNotNull (value.Metadata); + Assert.IsTrue (value.Metadata.TryGetValue ("SomeMeasure", out var measure)); + Assert.AreEqual ("5", measure); + Assert.IsTrue (value.Metadata.TryGetValue ("Result", out var result)); + Assert.AreEqual ("Success", result); + } + + [Test] + public void CounterWithNoMetadata () + { + var counter = InstrumentationService.CreateCounter<CustomCounterMetadata> ("TestCounter"); + + counter.Inc (); + + Assert.AreEqual (1, counterValues.Count); + var value = counterValues [0]; + + Assert.IsNull (value.Metadata); + } + } +} diff --git a/main/tests/MonoDevelop.Refactoring.Tests/MonoDevelop.Refactoring.Tests.csproj b/main/tests/MonoDevelop.Refactoring.Tests/MonoDevelop.Refactoring.Tests.csproj index 15e32632f9..b5dadd0141 100644 --- a/main/tests/MonoDevelop.Refactoring.Tests/MonoDevelop.Refactoring.Tests.csproj +++ b/main/tests/MonoDevelop.Refactoring.Tests/MonoDevelop.Refactoring.Tests.csproj @@ -117,11 +117,6 @@ <Name>MonoDevelop.Debugger</Name> <Private>False</Private> </ProjectReference> - <ProjectReference Include="..\Ide.Tests\MonoDevelop.Ide.Tests.csproj"> - <Project>{73D4CC8B-BAB9-4A29-841B-F25C6311F067}</Project> - <Name>MonoDevelop.Ide.Tests</Name> - <Private>False</Private> - </ProjectReference> <ProjectReference Include="..\UnitTests\UnitTests.csproj"> <Project>{1497D0A8-AFF1-4938-BC22-BE79B358BA5B}</Project> <Name>UnitTests</Name> @@ -147,6 +142,11 @@ <Name>RefactoringEssentials</Name> <Private>False</Private> </ProjectReference> + <ProjectReference Include="..\IdeUnitTests\IdeUnitTests.csproj"> + <Project>{F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}</Project> + <Name>IdeUnitTests</Name> + <Private>False</Private> + </ProjectReference> </ItemGroup> <ItemGroup> <Compile Include="ResultsEditorExtensionTestBase.cs" /> diff --git a/main/tests/TestRunner/Runner.cs b/main/tests/TestRunner/Runner.cs index 24659b44c2..6f47baa4b7 100644 --- a/main/tests/TestRunner/Runner.cs +++ b/main/tests/TestRunner/Runner.cs @@ -32,6 +32,7 @@ using Mono.Addins; using System.Linq; using Mono.Addins.Description;
using System.Diagnostics; +using System.Threading; using System.Threading.Tasks; namespace MonoDevelop.Tests.TestRunner @@ -70,9 +71,9 @@ namespace MonoDevelop.Tests.TestRunner if (guiUnitAsm != null) { Xwt.XwtSynchronizationContext.AutoInstall = false; - var sc = new Xwt.XwtSynchronizationContext (); - System.Threading.SynchronizationContext.SetSynchronizationContext (sc); - Runtime.MainSynchronizationContext = sc; + SynchronizationContext.SetSynchronizationContext (new Xwt.XwtSynchronizationContext ()); + Runtime.MainSynchronizationContext = SynchronizationContext.Current; + var method = guiUnitAsm.EntryPoint; return Task.FromResult ((int)method.Invoke (null, new [] { args.ToArray () })); } diff --git a/main/tests/UnitTests/TestBase.cs b/main/tests/UnitTests/TestBase.cs index 26f6d127a4..491f58d104 100644 --- a/main/tests/UnitTests/TestBase.cs +++ b/main/tests/UnitTests/TestBase.cs @@ -45,13 +45,17 @@ namespace UnitTests static string LocateTopLevel () { - var cwd = typeof (TestBase).Assembly.Location; - if (Path.GetDirectoryName (cwd) == Util.TestsRootDir) - return Util.TestsRootDir; - - while (!string.IsNullOrEmpty (cwd) && !File.Exists (Path.Combine (cwd, "top_level_monodevelop"))) - cwd = Path.GetDirectoryName (cwd); - return cwd; + var initialCwd = Path.GetDirectoryName (typeof (TestBase).Assembly.Location); + if (initialCwd != Util.TestsRootDir) { + var cwd = initialCwd; + while (!string.IsNullOrEmpty (cwd)) { + if (File.Exists (Path.Combine (cwd, "top_level_monodevelop"))) + return cwd; + cwd = Path.GetDirectoryName (cwd); + } + } + + return initialCwd; } [TestFixtureSetUp] diff --git a/main/tests/UnitTests/UnitTests.csproj b/main/tests/UnitTests/UnitTests.csproj index 77c8fdc28a..09c956f44c 100644 --- a/main/tests/UnitTests/UnitTests.csproj +++ b/main/tests/UnitTests/UnitTests.csproj @@ -44,22 +44,6 @@ <Reference Include="System.Xml" /> <Reference Include="System.Core" /> <Reference Include="System.Xml.Linq" /> - <Reference Include="Microsoft.CodeAnalysis.Workspaces"> - <HintPath>..\..\build\bin\Microsoft.CodeAnalysis.Workspaces.dll</HintPath> - <Private>False</Private> - </Reference> - <Reference Include="Microsoft.CodeAnalysis"> - <HintPath>..\..\build\bin\Microsoft.CodeAnalysis.dll</HintPath> - <Private>False</Private> - </Reference> - <Reference Include="Microsoft.CodeAnalysis.CSharp.Workspaces"> - <HintPath>..\..\build\bin\Microsoft.CodeAnalysis.CSharp.Workspaces.dll</HintPath> - <Private>False</Private> - </Reference> - <Reference Include="Microsoft.CodeAnalysis.CSharp"> - <HintPath>..\..\build\bin\Microsoft.CodeAnalysis.CSharp.dll</HintPath> - <Private>False</Private> - </Reference> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\src\core\MonoDevelop.Core\MonoDevelop.Core.csproj"> @@ -67,16 +51,6 @@ <Name>MonoDevelop.Core</Name> <Private>False</Private> </ProjectReference> - <ProjectReference Include="..\..\external\nrefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj"> - <Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project> - <Name>ICSharpCode.NRefactory</Name> - <Private>False</Private> - </ProjectReference> - <ProjectReference Include="..\..\external\nrefactory\ICSharpCode.NRefactory.CSharp\ICSharpCode.NRefactory.CSharp.csproj"> - <Project>{53DCA265-3C3C-42F9-B647-F72BA678122B}</Project> - <Name>ICSharpCode.NRefactory.CSharp</Name> - <Private>False</Private> - </ProjectReference> <ProjectReference Include="..\..\external\mono-addins\Mono.Addins\Mono.Addins.csproj"> <Project>{91DD5A2D-9FE3-4C3C-9253-876141874DAD}</Project> <Name>Mono.Addins</Name> diff --git a/main/tests/test-projects/FormsUpdatePackageRef/NetStandardProject/NetStandardProject.csproj b/main/tests/test-projects/FormsUpdatePackageRef/NetStandardProject/NetStandardProject.csproj index 5167806d66..19d29c28bc 100644 --- a/main/tests/test-projects/FormsUpdatePackageRef/NetStandardProject/NetStandardProject.csproj +++ b/main/tests/test-projects/FormsUpdatePackageRef/NetStandardProject/NetStandardProject.csproj @@ -1,7 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>netstandard2.0</TargetFramework> + <TargetFramework>netstandard1.6</TargetFramework> </PropertyGroup> <ItemGroup> diff --git a/version-checks b/version-checks index 2e41603000..b05d78e209 100755 --- a/version-checks +++ b/version-checks @@ -17,7 +17,7 @@ DEP[0]=md-addins DEP_NAME[0]=MDADDINS DEP_PATH[0]=${top_srcdir}/../md-addins DEP_MODULE[0]=git@github.com:xamarin/md-addins.git -DEP_NEEDED_VERSION[0]=1b1b1be91d6a55b325f269b48a1b4e2adc4060ed +DEP_NEEDED_VERSION[0]=b3546ee50d1e07637205f4968c7e69b1449c9e16 DEP_BRANCH_AND_REMOTE[0]="master origin/master" # heap-shot |