Skip to content

Loading xaml on demand to decrease app size after trimming #69

@hez2010

Description

@hez2010

Currently XamlX emitted a huge !CompiledAvaloniaXaml.!XamlLoader.TryLoad which puts all xaml files into a single method, which leads to all generated code being preserved after trimming, even if some of those xaml files were not being used.

For example, the !XamlLoader.TryLoad for Avalonia.Themes.Fluent is:

	public static object TryLoad(string P_0)
	{
		if (string.Equals(P_0, "avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml"))
		{
			return CompiledAvaloniaXaml.!AvaloniaResources.Build:/Accents/AccentColors.xaml(XamlIlRuntimeHelpers.CreateRootServiceProviderV2());
		}
		if (string.Equals(P_0, "avares://Avalonia.Themes.Fluent/Accents/Base.xaml"))
		{
			return CompiledAvaloniaXaml.!AvaloniaResources.Build:/Accents/Base.xaml(XamlIlRuntimeHelpers.CreateRootServiceProviderV2());
		}
		if (string.Equals(P_0, "avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml"))
		{
			return CompiledAvaloniaXaml.!AvaloniaResources.Build:/Accents/BaseDark.xaml(XamlIlRuntimeHelpers.CreateRootServiceProviderV2());
		}
		if (string.Equals(P_0, "avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml"))
		{
			return CompiledAvaloniaXaml.!AvaloniaResources.Build:/Accents/BaseLight.xaml(XamlIlRuntimeHelpers.CreateRootServiceProviderV2());
		}
		if (string.Equals(P_0, "avares://Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml"))
		{
			return CompiledAvaloniaXaml.!AvaloniaResources.Build:/Accents/FluentControlResourcesDark.xaml(XamlIlRuntimeHelpers.CreateRootServiceProviderV2());
		}
		if (string.Equals(P_0, "avares://Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml"))
		{
			return CompiledAvaloniaXaml.!AvaloniaResources.Build:/Accents/FluentControlResourcesLight.xaml(XamlIlRuntimeHelpers.CreateRootServiceProviderV2());
		}
		if (string.Equals(P_0, "avares://Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml"))
		{
			return CompiledAvaloniaXaml.!AvaloniaResources.Build:/Controls/AutoCompleteBox.xaml(XamlIlRuntimeHelpers.CreateRootServiceProviderV2());
		}
		if (string.Equals(P_0, "avares://Avalonia.Themes.Fluent/Controls/Button.xaml"))
		{
			return CompiledAvaloniaXaml.!AvaloniaResources.Build:/Controls/Button.xaml(XamlIlRuntimeHelpers.CreateRootServiceProviderV2());
		}
		if (string.Equals(P_0, "avares://Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml"))
		{
			return CompiledAvaloniaXaml.!AvaloniaResources.Build:/Controls/ButtonSpinner.xaml(XamlIlRuntimeHelpers.CreateRootServiceProviderV2());
		}
		if (string.Equals(P_0, "avares://Avalonia.Themes.Fluent/Controls/Calendar.xaml"))
		{
			return CompiledAvaloniaXaml.!AvaloniaResources.Build:/Controls/Calendar.xaml(XamlIlRuntimeHelpers.CreateRootServiceProviderV2());
		}
		if (string.Equals(P_0, "avares://Avalonia.Themes.Fluent/Controls/CalendarButton.xaml"))
		{
			return CompiledAvaloniaXaml.!AvaloniaResources.Build:/Controls/CalendarButton.xaml(XamlIlRuntimeHelpers.CreateRootServiceProviderV2());
		}
		if (string.Equals(P_0, "avares://Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml"))
		{
			return CompiledAvaloniaXaml.!AvaloniaResources.Build:/Controls/CalendarDatePicker.xaml(XamlIlRuntimeHelpers.CreateRootServiceProviderV2());
		}
		if (string.Equals(P_0, "avares://Avalonia.Themes.Fluent/Controls/CalendarDayButton.xaml"))
		{
			return CompiledAvaloniaXaml.!AvaloniaResources.Build:/Controls/CalendarDayButton.xaml(XamlIlRuntimeHelpers.CreateRootServiceProviderV2());
		}
                // .....
		return null;
	}

which leads to all those compiled xaml code being kept even if the user only use a Button in the app.

To make the loading process on demand, instead of emitting all things in a single TryLoad, emitting TryLoad_{uri} is a more properate choise. In this case, the TryLoad for avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml will become:

public static object TryLoad_avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml
{
    return CompiledAvaloniaXaml.!AvaloniaResources.Build:/Accents/AccentColors.xaml(XamlIlRuntimeHelpers.CreateRootServiceProviderV2());
}

To achieve trimming compatibility, we also need to introduce an attribute which save related types using string or Type with DynamicallyAccessedMembers, so that the trimmer can figure out which types are being used during compilation.

This is a breaking change, I would like to see the change landing before 11.0 GA getting released.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions