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

A module initializer will not be triggered when accessing a forwarded type #77632

Closed
DaZombieKiller opened this issue Oct 29, 2022 · 5 comments
Closed
Labels
area-TypeSystem-coreclr question Answer questions and provide assistance, not an issue with source code or documentation.

Comments

@DaZombieKiller
Copy link
Contributor

This is a problem I encountered while investigating mitigations for #60573.

Imagine that we have Library, which contains a number of structures for interop. However, the native structures have differing packing for certain platforms.

To work around this, I split the project up as follows:

  • Library
    • Contains Library.Platform.Windows and Library.Platform.Unix as embedded resources.
    • Contains a module initializer that subscribes to AppDomain.Current.AssemblyResolve to load the appropriate platform assembly.
    • Contains type forwarders pointing to Library.Platform.
  • Library.Platform
    • Serves as a reference assembly for Library to compile against.
    • Contains all of the types that were originally in Library.
  • Library.Ref
    • Contains all of the same code as Library and Library.Platform combined.
    • Is only compiled as a reference assembly.
    • The AssemblyName is set to Library.
  • Library.Platform.Windows
    • Contains all of the same code as Library.Platform, but compiled with the WINDOWS define active.
    • The AssemblyName is set to Library.Platform.
  • Library.Platform.Unix
    • Contains all of the same code as Library.Platform, but compiled with the UNIX define active.
    • The AssemblyName is set to Library.Platform.

A program would then compile against Library.Ref, but load Library. However, whenever an attempt is made to access one of the types, the program will throw a FileNotFoundException for Library.Platform due to the module initializer not being called when accessing forwarded types.

Is there any way to ensure that the module initializer is invoked in this situation without manual intervention from the API consumer? Or would it be possible to change the behaviour of module initializers to also run for forwarded types?

Workarounds

This can be worked around by exposing an empty static method on Library that consumers are required to call before accessing anything else, but this is certainly not an ideal solution.

I've also considered writing a C# source generator that emits the following into a consuming project:

[ModuleInitializer]
internal static void EnsureLibraryInitialized()
{
    Library.EnsureInitialized();
}

But naturally this would only work for C#.

@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Oct 29, 2022
@jkotas
Copy link
Member

jkotas commented Oct 29, 2022

Module initializers are not triggered by referencing a type. They are only triggered by accessing a method or a static field. This is documented in https://github.com/dotnet/runtime/blob/main/docs/design/specs/Ecma-335-Augments.md#module-initializer

@jkotas jkotas added question Answer questions and provide assistance, not an issue with source code or documentation. area-TypeSystem-coreclr labels Oct 29, 2022
@DaZombieKiller
Copy link
Contributor Author

I see. I suppose the workarounds I listed are probably the only options currently available then?

@jkotas
Copy link
Member

jkotas commented Oct 29, 2022

The solutions that we use in dotnet/runtime in these situations are either:

I would not recommend the embedded resource trick. It is not compatible with trimming and AOT.

@DaZombieKiller
Copy link
Contributor Author

I had originally attempted the first approach, though due to the library needing to target netstandard2.1 I was unable to take advantage of the -windows, -Unix, etc TargetFramework suffixes (netstandard2.1-windows doesn't error, but the -windows portion is ignored. I looked into the SDK and it seems this is only supported for net5.0 and newer).

The second approach would unfortunately be a huge amount of code and complexity for this case, especially since these types are supposed to be publicly exposed.

The trimming and AOT concerns are good points, I had only intended to use this trick in JIT scenarios but being unable to trim is still not ideal.

@jkotas jkotas closed this as completed Feb 1, 2023
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Feb 1, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Mar 3, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-TypeSystem-coreclr question Answer questions and provide assistance, not an issue with source code or documentation.
Projects
None yet
Development

No branches or pull requests

2 participants