Skip to content

#312: Preserve module initializers#393

Merged
KirillOsenkov merged 7 commits intogluck:masterfrom
warappa:features/#312-merge-module-initializers
Feb 18, 2025
Merged

#312: Preserve module initializers#393
KirillOsenkov merged 7 commits intogluck:masterfrom
warappa:features/#312-merge-module-initializers

Conversation

@warappa
Copy link
Copy Markdown
Contributor

@warappa warappa commented Feb 15, 2025

Adds support for preserving all module initializers. Without this, only one of the module initializers appears in the merged assembly because of deduplication.

  • Adds separate step to handle module initializers
  • Renames module initializer methods to unique name (prefixed with fully qualified assembly name)
  • Generates new <Module> class where its static constructor calls all module initializers found in merged assemblies

Fixes #312.

Example of merged module initializers (decompiled by ILSpy):

internal class <Module>
{
	static <Module>()
	{
		AnotherClassLibrary_Version_1.0.2.0_Culture_neutral_PublicKeyToken_null_ModuleInitializer();
		ClassLibrary_Version_1.0.2.0_Culture_neutral_PublicKeyToken_null_ModuleInitializer();
		WPFThemingAndLibraryStyles_Version_1.0.2.0_Culture_neutral_PublicKeyToken_null_ModuleInitializer();
	}

	private static void WPFThemingAndLibraryStyles_Version_1.0.2.0_Culture_neutral_PublicKeyToken_null_ModuleInitializer()
	{
		Program.InitializerOne();
		Program.InitializerTwo();
	}

	private static void AnotherClassLibrary_Version_1.0.2.0_Culture_neutral_PublicKeyToken_null_ModuleInitializer()
	{
		OtherModuleInitializer.Initialize();
	}

	private static void ClassLibrary_Version_1.0.2.0_Culture_neutral_PublicKeyToken_null_ModuleInitializer()
	{
		ModuleInitializer.Initialize();
	}
}

Notes

  • In general, once compiled, all methods of an assembly annotated with [ModuleInitializer] are merged into a namespace-less class named <Module>. Its static constructor calls all methods annotated with [ModuleInitializer] of its module. Therefore, only one type/method must be considered per module when ILRepack merges assemblies.

Tests

Currently, I tested this feature by temporary adding some module initializers to existing projects and verify them with ILSpy/by running the applications. So, as of now, those changes is not part of the PR!

So the question is:
Should there be an additional set of integration scenario projects added to explicitly test merging modules, or should some existing projects be adapted to also test for module initializers being merged and working?v

@KirillOsenkov
Copy link
Copy Markdown
Collaborator

Not sure what's best for testing, I think whatever is easier to implement.

Thanks!

@KirillOsenkov
Copy link
Copy Markdown
Collaborator

I thought of perhaps using the assembly MVID as a unique prefix for each module, but the downside is that it will not be stable, it will change whenever each of the merged assemblies changes. Your approach will be a bit more stable.

Copy link
Copy Markdown
Collaborator

@KirillOsenkov KirillOsenkov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good as far as I can see. Let me know when you feel that it's ready. Tests are nice but optional.

@warappa
Copy link
Copy Markdown
Contributor Author

warappa commented Feb 17, 2025

So I added a test to an existing test app by amending NestedLibraryUsageInXAML, AnotherClassLibrary and ClassLibrary to also test for module initializers - I hope this is OK.

This tests for:

  • If module initializers are called
  • If they are called in right order

I also added a topological sort to ModuleInitializersRepackStep to sort assembly dependencies, because module initializers may depend on dependencies being initialized before themselves.
And I added the module name to the renamed methods to be extra sure it works also with multiple modules (which is very unusual but allowed by the spec).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Module initializers are DuplicateIgnored away

2 participants