Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question] Extract BamlRecordReader feasability #10165

Open
yowl opened this issue Dec 11, 2024 · 9 comments
Open

[Question] Extract BamlRecordReader feasability #10165

yowl opened this issue Dec 11, 2024 · 9 comments
Labels
Question General question, not a problem in source code or documentation (yet)

Comments

@yowl
Copy link

yowl commented Dec 11, 2024

Hi, I contribute to the NativeAOT-LLVM project which is a NAOT compiler for LLVM and WebAssembly. I am currently bringing up a WPF applicaation in the browser using Avalonia's XPF bridge and have a question. As you may know NAOT is an AOT compiler that uses roots to establish a dependency tree of required types and methods that need compiling. WPF presents a challenge in that a some types/properties (get/set methods) may only be used in Xaml or Baml. So it would be advantageous from the goal of gathering dependencies to read the Baml resources and root the necessary types and methods. To that end I was looking at BamlRecordReader.cs and wondering if it could be extracted from the PresentationFramework assembly. Most things are possible, but is this going to be really hard do you think?

Thanks!

@lindexi
Copy link
Member

lindexi commented Dec 12, 2024

@yowl I firmly believe that this task is feasible. In fact, I think it can be accomplished with just a simple Roslyn analyzer. If you need any assistance from me, I would be more than happy to help.

@yowl
Copy link
Author

yowl commented Dec 12, 2024

Thanks for the offer of help! How did you envisage this working given the constraints of a Roslyn analyzer ?

@himgoyalmicro himgoyalmicro added the Question General question, not a problem in source code or documentation (yet) label Dec 13, 2024
@lindexi
Copy link
Member

lindexi commented Dec 13, 2024

@yowl How about add the Xaml file to Additional file and then analyze it?

@yowl
Copy link
Author

yowl commented Dec 13, 2024

In many cases we do not have xaml, just the dlls. The inputs to this process would be dlls and the output would be a trimmer xml file (or modifications to ItemGroups in msbuild, but that sounds more complicated).

@lindexi
Copy link
Member

lindexi commented Dec 16, 2024

@yowl I do not know how to read the embed assembly resource from IAssemblySymbol.

@maxkatz6
Copy link

ICSharpCode.BamlDecompiler is an independent parser that could be used to analyze dependencies and emit trimmer metadata before it's read. No idea how feasible this idea is though. Specifically, BAML lacks information about bindings anyway, as WPF doesn't have any concept of compiled bindings like Avalonia/MAUI or xBind like UWP.

@yowl
Copy link
Author

yowl commented Dec 16, 2024

Yes bindings are still a hole, but if DataContext is used, then it could be partially filled, the remaining items, like hierarchical grids would still be holes and would have to be filled manually. I have something working for the other stuff using IlSpy, testing it now.

@lindexi
Copy link
Member

lindexi commented Dec 16, 2024

Thank you @maxkatz6

@yowl I write the demo code about read the baml resource from assembly in IncrementalGenerator. You can find my code in https://github.com/lindexi/lindexi_gd/tree/ba3dba2934e3c56e6efadc9ea91ad3f2f25987b8/Roslyn/RerhabaikaKaloharfo

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices.ComTypes;
using System.Xml.Linq;
using ICSharpCode.BamlDecompiler;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Util;
using Microsoft.CodeAnalysis;

namespace NowabehairFearkeqerche
{
    [Generator(LanguageNames.CSharp)]
    public class FooGenerator : IIncrementalGenerator
    {
        public void Initialize(IncrementalGeneratorInitializationContext context)
        {
            var xamlIncrementalValueProvider = context.CompilationProvider.Select((compilation, token) =>
            {
                var list = new List<XamlRecord>();

                foreach (MetadataReference compilationReference in compilation.References)
                {
                    if (!(compilationReference is PortableExecutableReference portableExecutableReference))
                    {
                        continue;
                    }

                    var assemblySymbol = compilation.GetAssemblyOrModuleSymbol(compilationReference) as IAssemblySymbol;

                    var testDemoProjectName = "DalljukanemDaryawceceegal";

                    if (assemblySymbol?.Name != testDemoProjectName)
                    {
                        continue;
                    }

                    var filePath = portableExecutableReference.FilePath;

                    if (filePath is null)
                    {
                        continue;
                    }

                    if (Path.GetDirectoryName(filePath) is var directory && Path.GetFileName(directory) == "ref")
                    {
                        filePath = Path.Combine(directory, "..", "..", "..", "..", "bin", "Debug",
                            "net9.0-windows", "DalljukanemDaryawceceegal.dll");
                        filePath = Path.GetFullPath(filePath);
                    }

                    var fileInfo = new FileInfo(filePath);

                    var xamlDecompiler = new XamlDecompiler(fileInfo.FullName, new BamlDecompilerSettings()
                    {
                        ThrowOnAssemblyResolveErrors = false
                    });

                    var peFile = new PEFile(fileInfo.FullName);

                    foreach (var resource in peFile.Resources)
                    {
                        if (!(resource.TryOpenStream() is Stream stream))
                        {
                            continue;
                        }

                        var resourcesFile = new ResourcesFile(stream);
                        foreach (var keyValuePair in resourcesFile)
                        {
                            if (keyValuePair.Key.EndsWith(".baml"))
                            {
                                if (keyValuePair.Value is Stream bamlStream)
                                {
                                    var decompilationResult = xamlDecompiler.Decompile(bamlStream);
                                    var xaml = decompilationResult.Xaml;

                                    var xamlRecord = new XamlRecord(xaml, portableExecutableReference,
                                        assemblySymbol);
                                    list.Add(xamlRecord);
                                }
                            }
                        }
                    }
                }

                return list;
            });

            context.RegisterSourceOutput(xamlIncrementalValueProvider, (productionContext, provider) =>
            {

            });
        }
    }

    struct XamlRecord
    {
        public XamlRecord(XDocument xaml, PortableExecutableReference portableExecutableReference,
            IAssemblySymbol assemblySymbol)
        {
            Xaml = xaml;
            PortableExecutableReference = portableExecutableReference;
            AssemblySymbol = assemblySymbol;
        }

        public XDocument Xaml { get; }
        public PortableExecutableReference PortableExecutableReference { get; }
        public IAssemblySymbol AssemblySymbol { get; }
    }
}

@yowl
Copy link
Author

yowl commented Dec 16, 2024

System.Windows.Setter is another cause of dependencies

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question General question, not a problem in source code or documentation (yet)
Projects
None yet
Development

No branches or pull requests

4 participants