diff --git a/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs b/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs index 79b715105..71c5f4131 100644 --- a/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs +++ b/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs @@ -10,8 +10,10 @@ using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; using ICSharpCode.AvalonEdit.Highlighting; +using ICSharpCode.ILSpy; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; +using Mono.Cecil; using Ricciolo.StylesExplorer.MarkupReflection; namespace ILSpy.BamlDecompiler @@ -43,25 +45,56 @@ namespace ILSpy.BamlDecompiler return true; } + const string XWPFNamespace = "http://schemas.microsoft.com/winfx/2006/xaml"; + const string DefaultWPFNamespace = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"; + bool LoadBaml(AvalonEditTextOutput output) { var asm = this.Ancestors().OfType().FirstOrDefault().LoadedAssembly; - MemoryStream bamlStream = new MemoryStream(); Data.Position = 0; - Data.CopyTo(bamlStream); - bamlStream.Position = 0; + + XDocument xamlDocument = LoadIntoDocument(asm.GetAssemblyResolver(), asm.AssemblyDefinition, Data); + + output.Write(xamlDocument.ToString()); + return true; + } + + internal static XDocument LoadIntoDocument(IAssemblyResolver resolver, AssemblyDefinition asm, Stream stream) + { XDocument xamlDocument; - using (XmlBamlReader reader = new XmlBamlReader(bamlStream, new CecilTypeResolver(asm))) + using (XmlBamlReader reader = new XmlBamlReader(stream, new CecilTypeResolver(resolver, asm))) xamlDocument = XDocument.Load(reader); - ConvertToEmptyElements(xamlDocument.Root); + MoveNamespacesToRoot(xamlDocument); + return xamlDocument; + } + + static void MoveNamespacesToRoot(XDocument xamlDocument) + { + var additionalXmlns = new List { + new XAttribute("xmlns", DefaultWPFNamespace), + new XAttribute(XName.Get("x", XNamespace.Xmlns.NamespaceName), XWPFNamespace) + }; - output.Write(xamlDocument.ToString()); - return true; + foreach (var element in xamlDocument.Root.DescendantsAndSelf()) { + if (element.Name.NamespaceName != DefaultWPFNamespace && !additionalXmlns.Any(ka => ka.Value == element.Name.NamespaceName)) { + string newPrefix = new string(element.Name.LocalName.Where(c => char.IsUpper(c)).ToArray()).ToLowerInvariant(); + int current = additionalXmlns.Count(ka => ka.Name.Namespace == XNamespace.Xmlns && ka.Name.LocalName.TrimEnd(ch => char.IsNumber(ch)) == newPrefix); + if (current > 0) + newPrefix += (current + 1).ToString(); + XName defaultXmlns = XName.Get(newPrefix, XNamespace.Xmlns.NamespaceName); + if (element.Name.NamespaceName != DefaultWPFNamespace) + additionalXmlns.Add(new XAttribute(defaultXmlns, element.Name.NamespaceName)); + } + } + + foreach (var xmlns in additionalXmlns.Except(xamlDocument.Root.Attributes())) { + xamlDocument.Root.Add(xmlns); + } } - void ConvertToEmptyElements(XElement element) + static void ConvertToEmptyElements(XElement element) { foreach (var el in element.Elements()) { if (!el.IsEmpty && !el.HasElements && el.Value == "") { diff --git a/ILSpy.BamlDecompiler/CecilTypeResolver.cs b/ILSpy.BamlDecompiler/CecilTypeResolver.cs index 853025b06..90d0d0916 100644 --- a/ILSpy.BamlDecompiler/CecilTypeResolver.cs +++ b/ILSpy.BamlDecompiler/CecilTypeResolver.cs @@ -14,11 +14,13 @@ namespace ILSpy.BamlDecompiler /// public class CecilTypeResolver : ITypeResolver { - LoadedAssembly assembly; + IAssemblyResolver resolver; + AssemblyDefinition thisAssembly; - public CecilTypeResolver(LoadedAssembly assembly) + public CecilTypeResolver(IAssemblyResolver resolver, AssemblyDefinition asm) { - this.assembly = assembly; + this.resolver = resolver; + this.thisAssembly = asm; } public IType GetTypeByAssemblyQualifiedName(string name) @@ -31,12 +33,12 @@ namespace ILSpy.BamlDecompiler string fullName = name.Substring(0, comma); string assemblyName = name.Substring(comma + 1).Trim(); - var type = assembly.AssemblyDefinition.MainModule.GetType(fullName); + var type = thisAssembly.MainModule.GetType(fullName); if (type == null) { - var otherAssembly = assembly.LookupReferencedAssembly(assemblyName); + var otherAssembly = resolver.Resolve(assemblyName); if (otherAssembly == null) throw new Exception("could not resolve '" + assemblyName + "'!"); - type = otherAssembly.AssemblyDefinition.MainModule.GetType(fullName); + type = otherAssembly.MainModule.GetType(fullName); } return new CecilType(type); diff --git a/ILSpy.BamlDecompiler/Extensions.cs b/ILSpy.BamlDecompiler/Extensions.cs new file mode 100644 index 000000000..c76a581ea --- /dev/null +++ b/ILSpy.BamlDecompiler/Extensions.cs @@ -0,0 +1,22 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team +// This code is distributed under the MS-PL (for details please see \doc\MS-PL.txt) + +using System; +using System.Linq; + +namespace ILSpy.BamlDecompiler +{ + public static class Extensions + { + public static string TrimEnd(this string target, Func predicate) + { + if (target == null) + throw new ArgumentNullException("target"); + + while (predicate(target.LastOrDefault())) + target = target.Remove(target.Length - 1); + + return target; + } + } +} diff --git a/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj b/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj index 6514c632b..c87d30781 100644 --- a/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj +++ b/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj @@ -77,6 +77,7 @@ + diff --git a/ILSpy.BamlDecompiler/Properties/AssemblyInfo.cs b/ILSpy.BamlDecompiler/Properties/AssemblyInfo.cs index f28440c93..a2c459d27 100644 --- a/ILSpy.BamlDecompiler/Properties/AssemblyInfo.cs +++ b/ILSpy.BamlDecompiler/Properties/AssemblyInfo.cs @@ -3,6 +3,7 @@ using System; using System.Reflection; using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; #endregion @@ -22,6 +23,8 @@ using System.Runtime.InteropServices; // If you need to expose a type to COM, use [ComVisible(true)] on that type. [assembly: ComVisible(false)] +[assembly: InternalsVisibleTo("ILSpy.BamlDecompiler.Tests")] + // The assembly version has following format : // // Major.Minor.Build.Revision diff --git a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs index 7f6fbbfc5..28120b490 100644 --- a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs +++ b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs @@ -284,13 +284,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection else currentType = (BamlRecordType)type; - if (currentType.ToString().EndsWith("End")) - Debug.Unindent(); - - Debug.WriteLine(currentType); - - if (currentType.ToString().StartsWith("Start")) - Debug.Indent(); +// Debug.WriteLine(currentType); } private bool SetNextNode() diff --git a/ILSpy.BamlDecompiler/Tests/Cases/Simple.xaml b/ILSpy.BamlDecompiler/Tests/Cases/Simple.xaml new file mode 100644 index 000000000..e54fa6efa --- /dev/null +++ b/ILSpy.BamlDecompiler/Tests/Cases/Simple.xaml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/ILSpy.BamlDecompiler/Tests/Cases/Simple.xaml.cs b/ILSpy.BamlDecompiler/Tests/Cases/Simple.xaml.cs new file mode 100644 index 000000000..12770ff11 --- /dev/null +++ b/ILSpy.BamlDecompiler/Tests/Cases/Simple.xaml.cs @@ -0,0 +1,26 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; + +namespace ILSpy.BamlDecompiler.Tests.Cases +{ + /// + /// Interaction logic for Simple.xaml + /// + public partial class Simple : Window + { + public Simple() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/ILSpy.BamlDecompiler/Tests/ILSpy.BamlDecompiler.Tests.csproj b/ILSpy.BamlDecompiler/Tests/ILSpy.BamlDecompiler.Tests.csproj new file mode 100644 index 000000000..4955c4e1b --- /dev/null +++ b/ILSpy.BamlDecompiler/Tests/ILSpy.BamlDecompiler.Tests.csproj @@ -0,0 +1,111 @@ + + + + {1169E6D1-1899-43D4-A500-07CE4235B388} + Debug + x86 + Library + ILSpy.BamlDecompiler.Tests + ILSpy.BamlDecompiler.Tests + v4.0 + Client + False + False + 4 + false + + + x86 + False + Auto + 4194304 + 4096 + + + ..\bin\Debug\ + true + Full + False + True + DEBUG;TRACE + + + ..\bin\Release\ + false + None + True + False + TRACE + + + + ..\..\packages\DiffLib.1.0.0.55\lib\net35-Client\DiffLib.dll + + + ..\..\ICSharpCode.Decompiler\Tests\nunit.framework.dll + + + + 3.0 + + + + 3.0 + + + + 3.5 + + + 4.0 + + + + 3.5 + + + + 3.0 + + + + + Simple.xaml + Code + + + + + + + {FEC0DA52-C4A6-4710-BE36-B484A20C5E22} + ICSharpCode.Decompiler.Tests + + + {1E85EFF9-E370-4683-83E4-8A3D063FF791} + ILSpy + + + {D68133BD-1E63-496E-9EDE-4FBDBF77B486} + Mono.Cecil + + + {DDE2A481-8271-4EAC-A330-8FA6A38D13D1} + ICSharpCode.TreeView + + + {A6BAD2BA-76BA-461C-8B6D-418607591247} + ILSpy.BamlDecompiler + + + + + + + + + Always + + + + \ No newline at end of file diff --git a/ILSpy.BamlDecompiler/Tests/Properties/AssemblyInfo.cs b/ILSpy.BamlDecompiler/Tests/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..30c5daa49 --- /dev/null +++ b/ILSpy.BamlDecompiler/Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,31 @@ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ILSpy.BamlDecompiler.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ILSpy.BamlDecompiler.Tests")] +[assembly: AssemblyCopyright("Copyright 2011")] +[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)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.*")] diff --git a/ILSpy.BamlDecompiler/Tests/TestRunner.cs b/ILSpy.BamlDecompiler/Tests/TestRunner.cs new file mode 100644 index 000000000..6579dd0a1 --- /dev/null +++ b/ILSpy.BamlDecompiler/Tests/TestRunner.cs @@ -0,0 +1,67 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Resources; +using System.Xml.Linq; +using ICSharpCode.Decompiler.Tests.Helpers; +using ICSharpCode.ILSpy; +using Mono.Cecil; +using NUnit.Framework; +using Ricciolo.StylesExplorer.MarkupReflection; + +namespace ILSpy.BamlDecompiler.Tests +{ + [TestFixture] + public class TestRunner + { + [Test] + public void Simple() + { + RunTest("cases/simple"); + } + + void RunTest(string name) + { + string asmPath = typeof(TestRunner).Assembly.Location; + var assembly = AssemblyDefinition.ReadAssembly(asmPath); + Resource res = assembly.MainModule.Resources.First(); + Stream bamlStream = LoadBaml(res, name + ".baml"); + Assert.IsNotNull(bamlStream); + XDocument document = BamlResourceEntryNode.LoadIntoDocument(new DefaultAssemblyResolver(), assembly, bamlStream); + string path = Path.Combine("..\\..\\Tests", name + ".xaml"); + + CodeAssert.AreEqual(document.ToString(), File.ReadAllText(path)); + } + + Stream LoadBaml(Resource res, string name) + { + EmbeddedResource er = res as EmbeddedResource; + if (er != null) { + Stream s = er.GetResourceStream(); + s.Position = 0; + ResourceReader reader; + try { + reader = new ResourceReader(s); + } + catch (ArgumentException) { + return null; + } + foreach (DictionaryEntry entry in reader.Cast().OrderBy(e => e.Key.ToString())) { + if (entry.Key.ToString() == name) { + if (entry.Value is Stream) + return (Stream)entry.Value; + if (entry.Value is byte[]) + return new MemoryStream((byte[])entry.Value); + } + } + } + + return null; + } + } +} diff --git a/ILSpy.sln b/ILSpy.sln index e3c407eb5..22cda9165 100644 --- a/ILSpy.sln +++ b/ILSpy.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 -# SharpDevelop 4.0.1.7146 +# SharpDevelop 4.1.0.7466-alpha Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{F45DB999-7E72-4000-B5AD-3A7B485A0896}" ProjectSection(SolutionItems) = postProject doc\Command Line.txt = doc\Command Line.txt @@ -27,6 +27,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil.Pdb", "Mono.Ceci EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.BamlDecompiler", "ILSpy.BamlDecompiler\ILSpy.BamlDecompiler.csproj", "{A6BAD2BA-76BA-461C-8B6D-418607591247}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.BamlDecompiler.Tests", "ILSpy.BamlDecompiler\Tests\ILSpy.BamlDecompiler.Tests.csproj", "{1169E6D1-1899-43D4-A500-07CE4235B388}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -115,6 +117,14 @@ Global {A6BAD2BA-76BA-461C-8B6D-418607591247}.Release|Any CPU.ActiveCfg = Release|Any CPU {A6BAD2BA-76BA-461C-8B6D-418607591247}.Release|x86.Build.0 = Release|x86 {A6BAD2BA-76BA-461C-8B6D-418607591247}.Release|x86.ActiveCfg = Release|x86 + {1169E6D1-1899-43D4-A500-07CE4235B388}.Debug|Any CPU.Build.0 = Debug|x86 + {1169E6D1-1899-43D4-A500-07CE4235B388}.Debug|Any CPU.ActiveCfg = Debug|x86 + {1169E6D1-1899-43D4-A500-07CE4235B388}.Debug|x86.Build.0 = Debug|x86 + {1169E6D1-1899-43D4-A500-07CE4235B388}.Debug|x86.ActiveCfg = Debug|x86 + {1169E6D1-1899-43D4-A500-07CE4235B388}.Release|Any CPU.Build.0 = Release|x86 + {1169E6D1-1899-43D4-A500-07CE4235B388}.Release|Any CPU.ActiveCfg = Release|x86 + {1169E6D1-1899-43D4-A500-07CE4235B388}.Release|x86.Build.0 = Release|x86 + {1169E6D1-1899-43D4-A500-07CE4235B388}.Release|x86.ActiveCfg = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ILSpy/LoadedAssembly.cs b/ILSpy/LoadedAssembly.cs index 8285be9ae..2bb0ae05d 100644 --- a/ILSpy/LoadedAssembly.cs +++ b/ILSpy/LoadedAssembly.cs @@ -171,6 +171,11 @@ namespace ICSharpCode.ILSpy } } + public IAssemblyResolver GetAssemblyResolver() + { + return new MyAssemblyResolver(this); + } + public LoadedAssembly LookupReferencedAssembly(string fullName) { foreach (LoadedAssembly asm in assemblyList.GetAssemblies()) {