diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index e5b1d09fb..36eabbbab 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.Ast public class AstBuilder { - DecompilerContext context = new DecompilerContext(); + DecompilerContext context; CompilationUnit astCompileUnit = new CompilationUnit(); Dictionary astNamespaces = new Dictionary(); bool transformationsHaveRun; diff --git a/ICSharpCode.Decompiler/Ast/DecompilerContext.cs b/ICSharpCode.Decompiler/Ast/DecompilerContext.cs index 1028a4d4d..ce9bbaefc 100644 --- a/ICSharpCode.Decompiler/Ast/DecompilerContext.cs +++ b/ICSharpCode.Decompiler/Ast/DecompilerContext.cs @@ -10,11 +10,19 @@ namespace ICSharpCode.Decompiler { public class DecompilerContext { + public ModuleDefinition CurrentModule; public CancellationToken CancellationToken; public TypeDefinition CurrentType; public MethodDefinition CurrentMethod; public DecompilerSettings Settings = new DecompilerSettings(); + public DecompilerContext(ModuleDefinition currentModule) + { + if (currentModule == null) + throw new ArgumentNullException("currentModule"); + this.CurrentModule = currentModule; + } + /// /// Used to pass variable names from a method to its anonymous methods. /// diff --git a/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs b/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs index 85606b413..3fbbef73c 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs @@ -44,10 +44,22 @@ namespace ICSharpCode.Decompiler.Ast.Transforms compilationUnit.InsertChildAfter(null, new UsingDeclaration { Import = nsType }, CompilationUnit.MemberRole); } - // TODO: verify that the SimpleTypes refer to the correct type (no ambiguities) + 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) + FullyQualifyAmbiguousTypeNames(compilationUnit); } + readonly HashSet declaredNamespaces = new HashSet() { string.Empty }; readonly HashSet importedNamespaces = new HashSet(); + + readonly HashSet availableTypeNames = new HashSet(); + readonly HashSet ambiguousTypeNames = new HashSet(); string currentNamespace; bool IsParentOfCurrentNamespace(string ns) @@ -77,10 +89,49 @@ namespace ICSharpCode.Decompiler.Ast.Transforms string oldNamespace = currentNamespace; foreach (Identifier ident in namespaceDeclaration.Identifiers) { currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident.Name); + 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); + } + } + } + } + + void FullyQualifyAmbiguousTypeNames(AstNode compilationUnit) + { + foreach (SimpleType simpleType in compilationUnit.Descendants.OfType()) { + TypeReference tr = simpleType.Annotation(); + if (tr != null && ambiguousTypeNames.Contains(tr.Name)) { + AstType ns; + if (string.IsNullOrEmpty(tr.Namespace)) { + ns = new SimpleType("global"); + } else { + string[] parts = tr.Namespace.Split('.'); + 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); + } + } + } } } diff --git a/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs b/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs index 15787f0a2..dd9c285c4 100644 --- a/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs +++ b/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs @@ -35,7 +35,7 @@ namespace ICSharpCode.Decompiler.Tests static string RoundtripCode(string code) { AssemblyDefinition assembly = Compile(code); - AstBuilder decompiler = new AstBuilder(new DecompilerContext()); + AstBuilder decompiler = new AstBuilder(new DecompilerContext(assembly.MainModule)); decompiler.AddAssembly(assembly); new Helpers.RemoveCompilerAttribute().Run(decompiler.CompilationUnit); StringWriter output = new StringWriter(); diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs index 57775beb3..4ac77b536 100644 --- a/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -98,7 +98,7 @@ namespace ICSharpCode.Decompiler.Tests { string code = File.ReadAllText(fileName); AssemblyDefinition assembly = Compile(code); - AstBuilder decompiler = new AstBuilder(new DecompilerContext()); + AstBuilder decompiler = new AstBuilder(new DecompilerContext(assembly.MainModule)); decompiler.AddAssembly(assembly); new Helpers.RemoveCompilerAttribute().Run(decompiler.CompilationUnit); StringWriter output = new StringWriter(); diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/CSharpLanguage.cs index 87b160307..057d0d532 100644 --- a/ILSpy/CSharpLanguage.cs +++ b/ILSpy/CSharpLanguage.cs @@ -51,8 +51,9 @@ namespace ICSharpCode.ILSpy #if DEBUG internal static IEnumerable GetDebugLanguages() { + DecompilerContext context = new DecompilerContext(ModuleDefinition.CreateModule("dummy", ModuleKind.Dll)); string lastTransformName = "no transforms"; - foreach (Type _transformType in TransformationPipeline.CreatePipeline(new DecompilerContext()).Select(v => v.GetType()).Distinct()) { + foreach (Type _transformType in TransformationPipeline.CreatePipeline(context).Select(v => v.GetType()).Distinct()) { Type transformType = _transformType; // copy for lambda yield return new CSharpLanguage { transformAbortCondition = v => transformType.IsInstanceOfType(v), @@ -83,7 +84,7 @@ namespace ICSharpCode.ILSpy public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(method.DeclaringType, includeNamespace: true)); - AstBuilder codeDomBuilder = CreateAstBuilder(options, method.DeclaringType); + AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: method.DeclaringType); codeDomBuilder.AddMethod(method); codeDomBuilder.RunTransformations(transformAbortCondition); codeDomBuilder.GenerateCode(output); @@ -92,7 +93,7 @@ namespace ICSharpCode.ILSpy public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(property.DeclaringType, includeNamespace: true)); - AstBuilder codeDomBuilder = CreateAstBuilder(options, property.DeclaringType); + AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: property.DeclaringType); codeDomBuilder.AddProperty(property); codeDomBuilder.RunTransformations(transformAbortCondition); codeDomBuilder.GenerateCode(output); @@ -101,7 +102,7 @@ namespace ICSharpCode.ILSpy public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(field.DeclaringType, includeNamespace: true)); - AstBuilder codeDomBuilder = CreateAstBuilder(options, field.DeclaringType); + AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: field.DeclaringType); codeDomBuilder.AddField(field); codeDomBuilder.RunTransformations(transformAbortCondition); codeDomBuilder.GenerateCode(output); @@ -110,7 +111,7 @@ namespace ICSharpCode.ILSpy public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(ev.DeclaringType, includeNamespace: true)); - AstBuilder codeDomBuilder = CreateAstBuilder(options, ev.DeclaringType); + AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: ev.DeclaringType); codeDomBuilder.AddEvent(ev); codeDomBuilder.RunTransformations(transformAbortCondition); codeDomBuilder.GenerateCode(output); @@ -118,7 +119,7 @@ namespace ICSharpCode.ILSpy public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = CreateAstBuilder(options, type); + AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: type); codeDomBuilder.AddType(type); codeDomBuilder.RunTransformations(transformAbortCondition); codeDomBuilder.GenerateCode(output); @@ -133,7 +134,7 @@ namespace ICSharpCode.ILSpy WriteProjectFile(new TextOutputWriter(output), files, assembly.MainModule); } else { base.DecompileAssembly(assembly, fileName, output, options); - AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: null); + AstBuilder codeDomBuilder = CreateAstBuilder(options, currentModule: assembly.MainModule); codeDomBuilder.AddAssembly(assembly, onlyAssemblyLevel: !options.FullDecompilation); codeDomBuilder.RunTransformations(transformAbortCondition); codeDomBuilder.GenerateCode(output); @@ -296,7 +297,7 @@ namespace ICSharpCode.ILSpy new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, delegate (IGrouping file) { using (StreamWriter w = new StreamWriter(Path.Combine(options.SaveAsProjectDirectory, file.Key))) { - AstBuilder codeDomBuilder = CreateAstBuilder(options, null); + AstBuilder codeDomBuilder = CreateAstBuilder(options, currentModule: assembly.MainModule); foreach (TypeDefinition type in file) { codeDomBuilder.AddType(type); } @@ -383,10 +384,12 @@ namespace ICSharpCode.ILSpy } #endregion - AstBuilder CreateAstBuilder(DecompilationOptions options, TypeDefinition currentType) + AstBuilder CreateAstBuilder(DecompilationOptions options, ModuleDefinition currentModule = null, TypeDefinition currentType = null) { + if (currentModule == null) + currentModule = currentType.Module; return new AstBuilder( - new DecompilerContext { + new DecompilerContext(currentModule) { CancellationToken = options.CancellationToken, CurrentType = currentType, Settings = options.DecompilerSettings diff --git a/ILSpy/ILAstLanguage.cs b/ILSpy/ILAstLanguage.cs index a931fac4c..84348a4de 100644 --- a/ILSpy/ILAstLanguage.cs +++ b/ILSpy/ILAstLanguage.cs @@ -56,7 +56,7 @@ namespace ICSharpCode.ILSpy ilMethod.Body = astBuilder.Build(method, inlineVariables); if (abortBeforeStep != null) { - DecompilerContext context = new DecompilerContext { CurrentType = method.DeclaringType, CurrentMethod = method }; + DecompilerContext context = new DecompilerContext(method.Module) { CurrentType = method.DeclaringType, CurrentMethod = method }; new ILAstOptimizer().Optimize(context, ilMethod, abortBeforeStep.Value); }