diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index dca1897f8..f0eca9c7f 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -26,6 +26,7 @@ using System.Reflection.PortableExecutable; using System.Text.RegularExpressions; using System.Threading; +using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax; @@ -522,6 +523,14 @@ namespace ICSharpCode.Decompiler.CSharp } } + DecompileRun CreateDecompileRun() + { + return new DecompileRun(settings) { + DocumentationProvider = DocumentationProvider ?? CreateDefaultDocumentationProvider(), + CancellationToken = CancellationToken + }; + } + void RunTransforms(AstNode rootNode, DecompileRun decompileRun, ITypeResolveContext decompilationContext) { var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); @@ -550,10 +559,7 @@ namespace ICSharpCode.Decompiler.CSharp public SyntaxTree DecompileModuleAndAssemblyAttributes() { var decompilationContext = new SimpleTypeResolveContext(typeSystem.MainModule); - var decompileRun = new DecompileRun(settings) { - DocumentationProvider = DocumentationProvider ?? CreateDefaultDocumentationProvider(), - CancellationToken = CancellationToken - }; + DecompileRun decompileRun = CreateDecompileRun(); syntaxTree = new SyntaxTree(); RequiredNamespaceCollector.CollectAttributeNamespaces(module, decompileRun.Namespaces); DoDecompileModuleAndAssemblyAttributes(decompileRun, decompilationContext, syntaxTree); @@ -639,10 +645,7 @@ namespace ICSharpCode.Decompiler.CSharp public SyntaxTree DecompileWholeModuleAsSingleFile(bool sortTypes) { var decompilationContext = new SimpleTypeResolveContext(typeSystem.MainModule); - var decompileRun = new DecompileRun(settings) { - DocumentationProvider = DocumentationProvider ?? CreateDefaultDocumentationProvider(), - CancellationToken = CancellationToken - }; + var decompileRun = CreateDecompileRun(); syntaxTree = new SyntaxTree(); RequiredNamespaceCollector.CollectNamespaces(module, decompileRun.Namespaces); DoDecompileModuleAndAssemblyAttributes(decompileRun, decompilationContext, syntaxTree); @@ -664,10 +667,7 @@ namespace ICSharpCode.Decompiler.CSharp /// public ILTransformContext CreateILTransformContext(ILFunction function) { - var decompileRun = new DecompileRun(settings) { - DocumentationProvider = DocumentationProvider ?? CreateDefaultDocumentationProvider(), - CancellationToken = CancellationToken - }; + var decompileRun = CreateDecompileRun(); RequiredNamespaceCollector.CollectNamespaces(function.Method, module, decompileRun.Namespaces); return new ILTransformContext(function, typeSystem, DebugInfoProvider, settings) { CancellationToken = CancellationToken, @@ -912,10 +912,7 @@ namespace ICSharpCode.Decompiler.CSharp if (types == null) throw new ArgumentNullException(nameof(types)); var decompilationContext = new SimpleTypeResolveContext(typeSystem.MainModule); - var decompileRun = new DecompileRun(settings) { - DocumentationProvider = DocumentationProvider ?? CreateDefaultDocumentationProvider(), - CancellationToken = CancellationToken - }; + var decompileRun = CreateDecompileRun(); syntaxTree = new SyntaxTree(); foreach (var type in types) @@ -957,10 +954,7 @@ namespace ICSharpCode.Decompiler.CSharp if (type.ParentModule != typeSystem.MainModule) throw new NotSupportedException("Decompiling types that are not part of the main module is not supported."); var decompilationContext = new SimpleTypeResolveContext(typeSystem.MainModule); - var decompileRun = new DecompileRun(settings) { - DocumentationProvider = DocumentationProvider ?? CreateDefaultDocumentationProvider(), - CancellationToken = CancellationToken - }; + var decompileRun = CreateDecompileRun(); syntaxTree = new SyntaxTree(); RequiredNamespaceCollector.CollectNamespaces(type.MetadataToken, module, decompileRun.Namespaces); DoDecompileTypes(new[] { (TypeDefinitionHandle)type.MetadataToken }, decompileRun, decompilationContext, syntaxTree); @@ -995,10 +989,7 @@ namespace ICSharpCode.Decompiler.CSharp if (definitions == null) throw new ArgumentNullException(nameof(definitions)); syntaxTree = new SyntaxTree(); - var decompileRun = new DecompileRun(settings) { - DocumentationProvider = DocumentationProvider ?? CreateDefaultDocumentationProvider(), - CancellationToken = CancellationToken - }; + var decompileRun = CreateDecompileRun(); foreach (var entity in definitions) { if (entity.IsNil) @@ -1100,6 +1091,20 @@ namespace ICSharpCode.Decompiler.CSharp return SyntaxTreeToString(Decompile(definitions)); } + readonly Dictionary partialTypes = new(); + + public void AddPartialTypeDefinition(PartialTypeInfo info) + { + if (!partialTypes.TryGetValue(info.DeclaringTypeDefinitionHandle, out var existingInfo)) + { + partialTypes.Add(info.DeclaringTypeDefinitionHandle, info); + } + else + { + existingInfo.AddDeclaredMembers(info); + } + } + IEnumerable AddInterfaceImplHelpers( EntityDeclaration memberDecl, IMethod method, TypeSystemAstBuilder astBuilder) @@ -1319,6 +1324,11 @@ namespace ICSharpCode.Decompiler.CSharp var allOrderedEntities = typeDef.NestedTypes.Concat(allOrderedMembers).ToArray(); + if (!partialTypes.TryGetValue((TypeDefinitionHandle)typeDef.MetadataToken, out var partialTypeInfo)) + { + partialTypeInfo = null; + } + // Decompile members that are not compiler-generated. foreach (var entity in allOrderedEntities) { @@ -1326,7 +1336,7 @@ namespace ICSharpCode.Decompiler.CSharp { continue; } - DoDecompileMember(entity, recordDecompiler); + DoDecompileMember(entity, recordDecompiler, partialTypeInfo); } // Decompile compiler-generated members that are still needed. @@ -1338,7 +1348,7 @@ namespace ICSharpCode.Decompiler.CSharp // Member is already decompiled. continue; } - DoDecompileMember(entity, recordDecompiler); + DoDecompileMember(entity, recordDecompiler, partialTypeInfo); } // Add all decompiled members to syntax tree in the correct order. @@ -1352,6 +1362,10 @@ namespace ICSharpCode.Decompiler.CSharp // Remove the [DefaultMember] attribute if the class contains indexers RemoveAttribute(typeDecl, KnownAttribute.DefaultMember); } + if (partialTypeInfo != null) + { + typeDecl.Modifiers |= Modifiers.Partial; + } if (settings.IntroduceRefModifiersOnStructs) { if (FindAttribute(typeDecl, KnownAttribute.Obsolete, out var attr)) @@ -1406,8 +1420,13 @@ namespace ICSharpCode.Decompiler.CSharp Instrumentation.DecompilerEventSource.Log.DoDecompileTypeDefinition(typeDef.FullName, watch.ElapsedMilliseconds); } - void DoDecompileMember(IEntity entity, RecordDecompiler recordDecompiler) + void DoDecompileMember(IEntity entity, RecordDecompiler recordDecompiler, PartialTypeInfo partialType) { + if (partialType != null && partialType.IsDeclaredMember(entity.MetadataToken)) + { + return; + } + EntityDeclaration entityDecl; switch (entity) { diff --git a/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs index ad3ffdb8f..3057cbb6f 100644 --- a/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs @@ -149,8 +149,9 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler } TargetDirectory = targetDirectory; directories.Clear(); - var files = WriteCodeFilesInProject(moduleDefinition, cancellationToken).ToList(); - files.AddRange(WriteResourceFilesInProject(moduleDefinition)); + var resources = WriteResourceFilesInProject(moduleDefinition).ToList(); + var files = WriteCodeFilesInProject(moduleDefinition, resources.SelectMany(r => r.partialTypes ?? Enumerable.Empty()).ToList(), cancellationToken).ToList(); + files.AddRange(resources.Select(r => (r.itemType, r.fileName))); files.AddRange(WriteMiscellaneousFilesInProject(moduleDefinition)); if (StrongNameKeyFile != null) { @@ -202,7 +203,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler return new[] { ("Compile", assemblyInfo) }; } - IEnumerable<(string itemType, string fileName)> WriteCodeFilesInProject(Metadata.PEFile module, CancellationToken cancellationToken) + IEnumerable<(string itemType, string fileName)> WriteCodeFilesInProject(Metadata.PEFile module, IList partialTypes, CancellationToken cancellationToken) { var metadata = module.Metadata; var files = module.Metadata.GetTopLevelTypeDefinitions().Where(td => IncludeTypeWhenDecompilingProject(module, td)).GroupBy( @@ -237,6 +238,12 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler try { CSharpDecompiler decompiler = CreateDecompiler(ts); + + foreach (var partialType in partialTypes) + { + decompiler.AddPartialTypeDefinition(partialType); + } + decompiler.CancellationToken = cancellationToken; var syntaxTree = decompiler.DecompileTypes(file.ToArray()); syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, Settings.CSharpFormattingOptions)); @@ -253,7 +260,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler #endregion #region WriteResourceFilesInProject - protected virtual IEnumerable<(string itemType, string fileName)> WriteResourceFilesInProject(Metadata.PEFile module) + protected virtual IEnumerable<(string itemType, string fileName, List partialTypes)> WriteResourceFilesInProject(Metadata.PEFile module) { foreach (var r in module.Resources.Where(r => r.ResourceType == ResourceType.Embedded)) { @@ -263,7 +270,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler if (r.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) { bool decodedIntoIndividualFiles; - var individualResources = new List<(string itemType, string fileName)>(); + var individualResources = new List<(string itemType, string fileName, List partialTypes)>(); try { var resourcesFile = new ResourcesFile(stream); @@ -323,12 +330,12 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler stream.Position = 0; stream.CopyTo(fs); } - yield return ("EmbeddedResource", fileName); + yield return ("EmbeddedResource", fileName, null); } } } - protected virtual IEnumerable<(string itemType, string fileName)> WriteResourceToFile(string fileName, string resourceName, Stream entryStream) + protected virtual IEnumerable<(string itemType, string fileName, List partialTypes)> WriteResourceToFile(string fileName, string resourceName, Stream entryStream) { if (fileName.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) { @@ -343,7 +350,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler writer.AddResource(entry.Key, entry.Value); } } - return new[] { ("EmbeddedResource", resx) }; + return new[] { ("EmbeddedResource", resx, (List)null) }; } catch (BadImageFormatException) { @@ -358,7 +365,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler { entryStream.CopyTo(fs); } - return new[] { ("EmbeddedResource", fileName) }; + return new[] { ("EmbeddedResource", fileName, (List)null) }; } string GetFileNameForResource(string fullName) @@ -558,8 +565,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler /// /// Cleans up a node name for use as a file system name. If is active, /// dots are seen as segment separators. Each segment is limited to maxSegmentLength characters. - /// (see ) If is active, - /// we check for file a extension and try to preserve it, if it's valid. + /// If is active, we check for file a extension and try to preserve it, + /// if it's valid. /// static string CleanUpName(string text, bool separateAtDots, bool treatAsFileName) { diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 4f66c233f..c5011ed45 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -84,6 +84,7 @@ + diff --git a/ICSharpCode.Decompiler/PartialTypeInfo.cs b/ICSharpCode.Decompiler/PartialTypeInfo.cs new file mode 100644 index 000000000..efe171bfa --- /dev/null +++ b/ICSharpCode.Decompiler/PartialTypeInfo.cs @@ -0,0 +1,76 @@ +// Copyright (c) 2022 Siegfried Pammer +// +// 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.Metadata; +using System.Reflection.Metadata.Ecma335; + +using ICSharpCode.Decompiler.TypeSystem; + +namespace ICSharpCode.Decompiler +{ + public class PartialTypeInfo + { + readonly HashSet declaredMembers = new(); + + public PartialTypeInfo(ITypeDefinition declaringTypeDefinition) + { + DeclaringTypeDefinitionHandle = (TypeDefinitionHandle)declaringTypeDefinition.MetadataToken; + } + + public PartialTypeInfo(TypeDefinitionHandle declaringTypeDefinitionHandle) + { + DeclaringTypeDefinitionHandle = declaringTypeDefinitionHandle; + } + + public TypeDefinitionHandle DeclaringTypeDefinitionHandle { get; } + + public void AddDeclaredMember(IMember member) + { + declaredMembers.Add(member.MetadataToken); + } + + public void AddDeclaredMember(EntityHandle handle) + { + declaredMembers.Add(handle); + } + + public bool IsDeclaredMember(IMember member) + { + return declaredMembers.Contains(member.MetadataToken); + } + + public bool IsDeclaredMember(EntityHandle handle) + { + return declaredMembers.Contains(handle); + } + + public void AddDeclaredMembers(PartialTypeInfo info) + { + foreach (var member in info.declaredMembers) + { + declaredMembers.Add(member); + } + } + + public string DebugOutput => string.Join(", ", declaredMembers.Select(m => MetadataTokens.GetToken(m).ToString("X"))); + } +} \ No newline at end of file diff --git a/ILSpy.BamlDecompiler/BamlConnectionId.cs b/ILSpy.BamlDecompiler/BamlConnectionId.cs index 9bf19607d..30d11f0cf 100644 --- a/ILSpy.BamlDecompiler/BamlConnectionId.cs +++ b/ILSpy.BamlDecompiler/BamlConnectionId.cs @@ -20,6 +20,8 @@ THE SOFTWARE. */ +using ICSharpCode.Decompiler.TypeSystem; + namespace ILSpy.BamlDecompiler { /// @@ -27,7 +29,7 @@ namespace ILSpy.BamlDecompiler /// internal sealed class FieldAssignment { - public string FieldName; + public IField Field; } /// diff --git a/ILSpy.BamlDecompiler/BamlDecompilationResult.cs b/ILSpy.BamlDecompiler/BamlDecompilationResult.cs index e46651b8a..83e8942ed 100644 --- a/ILSpy.BamlDecompiler/BamlDecompilationResult.cs +++ b/ILSpy.BamlDecompiler/BamlDecompilationResult.cs @@ -18,6 +18,7 @@ using System.Collections.Generic; using System.Linq; +using System.Reflection.Metadata; using System.Xml.Linq; using ICSharpCode.Decompiler.TypeSystem; @@ -31,11 +32,14 @@ namespace ILSpy.BamlDecompiler public FullTypeName? TypeName { get; } - public BamlDecompilationResult(XDocument xaml, FullTypeName? typeName, IEnumerable assemblyReferences) + public List GeneratedMembers { get; } + + public BamlDecompilationResult(XDocument xaml, FullTypeName? typeName, IEnumerable assemblyReferences, IEnumerable generatedMembers) { this.Xaml = xaml; this.TypeName = typeName; this.AssemblyReferences = assemblyReferences.ToList(); + this.GeneratedMembers = generatedMembers.ToList(); } } } \ No newline at end of file diff --git a/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs b/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs index d61f20b79..be0a6ae1c 100644 --- a/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs +++ b/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs @@ -19,7 +19,9 @@ using System; using System.ComponentModel.Composition; using System.IO; +using System.Linq; +using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy; @@ -45,25 +47,34 @@ namespace ILSpy.BamlDecompiler public sealed class BamlResourceFileHandler : IResourceFileHandler { public string EntryType => "Page"; - public bool CanHandle(string name, DecompilationOptions options) => name.EndsWith(".baml", StringComparison.OrdinalIgnoreCase); + public bool CanHandle(string name, ResourceFileHandlerContext context) => name.EndsWith(".baml", StringComparison.OrdinalIgnoreCase); - public string WriteResourceToFile(LoadedAssembly assembly, string fileName, Stream stream, DecompilationOptions options) + public string WriteResourceToFile(LoadedAssembly assembly, string fileName, Stream stream, ResourceFileHandlerContext context) { BamlDecompilerTypeSystem typeSystem = new BamlDecompilerTypeSystem(assembly.GetPEFileOrNull(), assembly.GetAssemblyResolver()); var decompiler = new XamlDecompiler(typeSystem, new BamlDecompilerSettings() { - ThrowOnAssemblyResolveErrors = options.DecompilerSettings.ThrowOnAssemblyResolveErrors + ThrowOnAssemblyResolveErrors = context.DecompilationOptions.DecompilerSettings.ThrowOnAssemblyResolveErrors }); - decompiler.CancellationToken = options.CancellationToken; + decompiler.CancellationToken = context.DecompilationOptions.CancellationToken; var result = decompiler.Decompile(stream); - if (result.TypeName.HasValue) + var typeDefinition = result.TypeName.HasValue ? typeSystem.MainModule.GetTypeDefinition(result.TypeName.Value.TopLevelTypeName) : null; + if (typeDefinition != null) { - fileName = WholeProjectDecompiler.CleanUpPath(result.TypeName.Value.ReflectionName) + ".xaml"; + fileName = WholeProjectDecompiler.CleanUpPath(typeDefinition.ReflectionName) + ".xaml"; + var partialTypeInfo = new PartialTypeInfo(typeDefinition); + foreach (var member in result.GeneratedMembers) + { + partialTypeInfo.AddDeclaredMember(member); + } + context.AddPartialTypeInfo(partialTypeInfo); } else { fileName = Path.ChangeExtension(fileName, ".xaml"); } - result.Xaml.Save(Path.Combine(options.SaveAsProjectDirectory, fileName)); + string saveFileName = Path.Combine(context.DecompilationOptions.SaveAsProjectDirectory, fileName); + Directory.CreateDirectory(Path.GetDirectoryName(saveFileName)); + result.Xaml.Save(saveFileName); return fileName; } } diff --git a/ILSpy.BamlDecompiler/Properties/launchSettings.json b/ILSpy.BamlDecompiler/Properties/launchSettings.json index 253f16b70..93c6637b2 100644 --- a/ILSpy.BamlDecompiler/Properties/launchSettings.json +++ b/ILSpy.BamlDecompiler/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "ILSpy.BamlDecompiler": { "commandName": "Executable", - "executablePath": "C:\\Users\\sie_p\\Projects\\ILSpy\\ILSpy\\bin\\Debug\\net472\\ILSpy.exe", + "executablePath": "$(SolutionDir)\\ILSpy\\bin\\Debug\\net6.0-windows\\ILSpy.exe", "commandLineArgs": "/separate" } } diff --git a/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs b/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs index f1797c8fe..ba05f6df7 100644 --- a/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs +++ b/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs @@ -25,13 +25,14 @@ using System.Xml.Linq; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.Transforms; -using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; using ILSpy.BamlDecompiler.Xaml; namespace ILSpy.BamlDecompiler.Rewrite { + using ICSharpCode.Decompiler.TypeSystem; + internal class ConnectionIdRewritePass : IRewritePass { static readonly TopLevelTypeName componentConnectorTypeName @@ -61,8 +62,12 @@ namespace ILSpy.BamlDecompiler.Rewrite if ((index = fieldAssignments.FindIndex(item => item.key.Contains(annotation.Id))) > -1) { var xName = ctx.GetKnownNamespace("Name", XamlContext.KnownNamespace_Xaml, element); + FieldAssignment fieldAssignment = fieldAssignments[index].value; if (element.Attribute("Name") is null && element.Attribute(xName) is null) - element.Add(new XAttribute(xName, fieldAssignments[index].value.FieldName)); + { + element.Add(new XAttribute(xName, fieldAssignment.Field.Name)); + } + ctx.GeneratedMembers.Add(fieldAssignment.Field.MetadataToken); found = true; } if ((index = eventMappings.FindIndex(item => item.key.Contains(annotation.Id))) > -1) @@ -121,32 +126,61 @@ namespace ILSpy.BamlDecompiler.Rewrite return; var connect = connectorInterface.GetMethods(m => m.Name == "Connect").SingleOrDefault(); - IMethod method = null; - MethodDefinition metadataEntry = default; + IMethod connectMethod = null; + MethodDefinition connectMetadataEntry = default; var module = ctx.TypeSystem.MainModule.PEFile; foreach (IMethod m in type.Methods) { - if (m.ExplicitlyImplementedInterfaceMembers.Any(md => md.MemberDefinition.Equals(connect))) + if (connectMethod == null && m.ExplicitlyImplementedInterfaceMembers.Any(md => md.MemberDefinition.Equals(connect))) { - method = m; - metadataEntry = module.Metadata - .GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken); - break; + connectMethod = m; + connectMetadataEntry = module.Metadata + .GetMethodDefinition((MethodDefinitionHandle)connectMethod.MetadataToken); + } + else if (m.Parameters.Count == 0 + && m.ReturnType.Kind == TypeKind.Void + && !m.IsStatic + && m.Accessibility == Accessibility.Public + && m.Name == "InitializeComponent" + && m.GetAttributes().Any(a => a.AttributeType.ReflectionName == "System.CodeDom.Compiler.GeneratedCodeAttribute")) + { + ctx.GeneratedMembers.Add(m.MetadataToken); + } + else if (m.Parameters.Count == 0 + && m.ReturnType.Kind == TypeKind.Void + && m.IsStatic + && m.Accessibility == Accessibility.Public + && m.Name == "Main" + && m.DeclaringTypeDefinition.GetNonInterfaceBaseTypes().Any(t => t.ReflectionName == "System.Windows.Application") + && m.GetAttributes().Any(a => a.AttributeType.ReflectionName == "System.CodeDom.Compiler.GeneratedCodeAttribute")) + { + ctx.GeneratedMembers.Add(m.MetadataToken); } } - if (method == null || metadataEntry.RelativeVirtualAddress <= 0) + if (type.Fields.FirstOrDefault(f => f.Name == "_contentLoaded" && f.Type.IsKnownType(KnownTypeCode.Boolean)) is { + Accessibility: Accessibility.Private, IsStatic: false + } contentLoadedField) + { + ctx.GeneratedMembers.Add(contentLoadedField.MetadataToken); + } + + if (connectMethod == null || connectMetadataEntry.RelativeVirtualAddress <= 0) return; - var body = module.Reader.GetMethodBody(metadataEntry.RelativeVirtualAddress); + ctx.GeneratedMembers.Add(connectMethod.MetadataToken); + + + + var body = module.Reader.GetMethodBody(connectMetadataEntry.RelativeVirtualAddress); var genericContext = new GenericContext( - classTypeParameters: method.DeclaringType?.TypeParameters, - methodTypeParameters: method.TypeParameters); + classTypeParameters: connectMethod.DeclaringType?.TypeParameters, + methodTypeParameters: connectMethod.TypeParameters); // decompile method and optimize the switch var ilReader = new ILReader(ctx.TypeSystem.MainModule); - var function = ilReader.ReadIL((MethodDefinitionHandle)method.MetadataToken, body, genericContext, + var function = ilReader.ReadIL((MethodDefinitionHandle)connectMethod.MetadataToken, body, genericContext, ILFunctionKind.TopLevelFunction, ctx.CancellationToken); var context = new ILTransformContext(function, ctx.TypeSystem, null) { @@ -224,7 +258,7 @@ namespace ILSpy.BamlDecompiler.Rewrite || !(arg.MatchLdLoc(out var t) && t.Kind == VariableKind.Parameter && t.Index == 1)) return false; - field = new FieldAssignment { FieldName = fld.Name }; + field = new FieldAssignment { Field = fld }; return true; } diff --git a/ILSpy.BamlDecompiler/XamlContext.cs b/ILSpy.BamlDecompiler/XamlContext.cs index 437277f57..d9be0620c 100644 --- a/ILSpy.BamlDecompiler/XamlContext.cs +++ b/ILSpy.BamlDecompiler/XamlContext.cs @@ -23,6 +23,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Reflection.Metadata; using System.Threading; using System.Xml.Linq; @@ -40,6 +41,7 @@ namespace ILSpy.BamlDecompiler TypeSystem = typeSystem; NodeMap = new Dictionary(); XmlNs = new XmlnsDictionary(); + GeneratedMembers = new List(); XClassNames = new List(); } @@ -57,6 +59,8 @@ namespace ILSpy.BamlDecompiler public List XClassNames { get; } + public List GeneratedMembers { get; } + public XmlnsDictionary XmlNs { get; } public static XamlContext Construct(IDecompilerTypeSystem typeSystem, BamlDocument document, CancellationToken token, BamlDecompilerSettings bamlDecompilerOptions) diff --git a/ILSpy.BamlDecompiler/XamlDecompiler.cs b/ILSpy.BamlDecompiler/XamlDecompiler.cs index e406516a2..0d5c5205c 100644 --- a/ILSpy.BamlDecompiler/XamlDecompiler.cs +++ b/ILSpy.BamlDecompiler/XamlDecompiler.cs @@ -121,7 +121,7 @@ namespace ILSpy.BamlDecompiler var assemblyReferences = ctx.Baml.AssemblyIdMap.Select(a => a.Value.AssemblyFullName); var typeName = ctx.XClassNames.FirstOrDefault() is string s ? (FullTypeName?)new FullTypeName(s) : null; - return new BamlDecompilationResult(xaml, typeName, assemblyReferences); + return new BamlDecompilationResult(xaml, typeName, assemblyReferences, ctx.GeneratedMembers); } } } \ No newline at end of file diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index abdafa2ec..fdbccd101 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -509,15 +509,17 @@ namespace ICSharpCode.ILSpy this.options = options; } - protected override IEnumerable<(string itemType, string fileName)> WriteResourceToFile(string fileName, string resourceName, Stream entryStream) + protected override IEnumerable<(string itemType, string fileName, List partialTypes)> WriteResourceToFile(string fileName, string resourceName, Stream entryStream) { + var context = new ResourceFileHandlerContext(options); foreach (var handler in App.ExportProvider.GetExportedValues()) { - if (handler.CanHandle(fileName, options)) + if (handler.CanHandle(fileName, context)) { entryStream.Position = 0; - fileName = handler.WriteResourceToFile(assembly, fileName, entryStream, options); - return new[] { (handler.EntryType, fileName) }; + fileName = handler.WriteResourceToFile(assembly, fileName, entryStream, context); + + return new[] { (handler.EntryType, fileName, context.PartialTypes) }; } } return base.WriteResourceToFile(fileName, resourceName, entryStream); diff --git a/ILSpy/Languages/IResourceFileHandler.cs b/ILSpy/Languages/IResourceFileHandler.cs index 43270bec4..8fe336f06 100644 --- a/ILSpy/Languages/IResourceFileHandler.cs +++ b/ILSpy/Languages/IResourceFileHandler.cs @@ -16,8 +16,10 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.Collections.Generic; using System.IO; +using ICSharpCode.Decompiler; using ICSharpCode.ILSpyX; namespace ICSharpCode.ILSpy @@ -25,7 +27,25 @@ namespace ICSharpCode.ILSpy public interface IResourceFileHandler { string EntryType { get; } - bool CanHandle(string name, DecompilationOptions options); - string WriteResourceToFile(LoadedAssembly assembly, string fileName, Stream stream, DecompilationOptions options); + bool CanHandle(string name, ResourceFileHandlerContext context); + string WriteResourceToFile(LoadedAssembly assembly, string fileName, Stream stream, ResourceFileHandlerContext context); + } + + public class ResourceFileHandlerContext + { + readonly List partialTypes = new(); + internal List PartialTypes => partialTypes; + + public DecompilationOptions DecompilationOptions { get; } + + public ResourceFileHandlerContext(DecompilationOptions options) + { + this.DecompilationOptions = options; + } + + public void AddPartialTypeInfo(PartialTypeInfo info) + { + this.PartialTypes.Add(info); + } } }