ltrzesniewski/InlineIL.Fody

Add a way to get a TypeRef from an assembly at provided path

MichalPetryka opened this issue ยท 8 comments

It would be useful to be able to able to get a TypeRef to an assembly that's not referenced by the project, maybe by adding a new TypeRef constructor that takes a path to a dll and a type name there and adds it as a reference to the output assembly.

Having a path to a dll in the source code doesn't feel clean to me.

May I ask why do you want this feature, and what prevents you from referencing the assembly?

May I ask why do you want this feature, and what prevents you from referencing the assembly?

Me and other contributors to dotnet/runtime sometimes use your InlineIL for testing how the runtime behaves with certain IL patterns, adding this would also make it easier to test new and internal APIs added to the runtime which can't be accessed through normal code due to IDEs building it against older reference assemblies that are stripped of internal APIs. With this you could emit calls to such APIs by making the output reference the locally built runtime while still using a proper IDE.

I see. That's a valid use case. ๐Ÿ‘

However, accessing new runtime APIs may be more difficult than it seems:

  • I suppose you'd like to replace an existing reference with a custom one when there's a name conflict. I'm not sure I like the idea of having a call to TypeRef.FromDll("...", "...") anywhere in the code replace the existing assembly.
  • Should the existing reference be replaced if it only differs by the version? You could have net8.0 assemblies in your test project, but you'd certainly want to use the ones from net9.0.
  • The reference assemblies use type forwarding extensively, so what should be done with the existing reference to System.Runtime if you add a reference to a custom System.Private.CoreLib? I know that having both works, but you may end up in an unexpected state nonetheless.
  • Will the answers to the above questions be necessarily the same for all test projects?

...or I could just add a reference to the custom dll and not care about these issues, I'm just not sure you'd end up in a valid state (you could have two references to the same assembly name for example).

I also wonder about the syntax: TypeRef.FromDll("<path>", "<type>") or [assembly: InlineIL.AddAssemblyReference("<path>")] or maybe an <InlineIL.AssemblyReference Include="<path>" /> item in the csproj?

Speaking of the csproj, wouldn't tweaking the output of the ResolveAssemblyReference task be sufficient for this? Maybe even modifying @(Reference) items could do the job? I feel that handling this through MSBuild would be a better fit than doing it with InlineIL.

What do you think?

(also, Fody's AssemblyResolver would be unaware about the custom dll, and that may cause issues or require changes, but that's an implementation detail)

I think that adding a 2nd reference would be fine here, the normal ones would then refer to System.Runtime and be forwarded at runtime while the emitted ones would call System.Private.CoreLib directly.
As for syntax, I think that TypeRef.FromDll("<path>", "<type>") would be the best since it would clearly show which dll version the type is coming from.
IIRC MsBuild handles reference assemblies to framework differently from the rest and gets quite unhappy when trying to replace them, I don't recall the details though.

Ok, I released v1.9.0-pre1 with this feature. Please test it and tell me if it behaves as you expect.

I'm not sure yet if I want to merge this change TBH, as this required a lot of changes in the existing code, much more than I expected, because of the way Fody and Cecil are structured.

In any case, the FromDll method will remain marked as experimental, as I fully expect it to cause issues (such as other weavers down the chain not being able to resolve the injected symbols for instance).

Please test it and tell me if it behaves as you expect.

It seems to be working nicely, one small improvement would be if it could recognize typeof(X).FullName in addition to just string literals for the type name.
Can't say I have any opinion on the code changes required as I don't know the code here.
I agree with keeping it as experimental too as this option is only useful for experimenting around I'd say.

Cool! ๐Ÿ™‚

Maybe I could add an overload with System.Type instead of (or in addition to) handling typeof(X).FullName. Since X would need to be an element type (in the sense of not being a type spec like X[] or X*), it wouldn't make sense to have an overload with InlineIL.TypeRef here.

I think I'll rename the method to something like FromDllFile, maybe simplify the code a bit, add a few more tests then merge this. I'll probably release a v1.9.0-pre2 with those changes, but I won't release a stable version until I have a non-experimental feature to ship. ๐Ÿ™‚

I pushed v1.9.0-pre2 with these changes.