Open breenbob opened 1 year ago
@breenbob Hi, thanks for thorough analysis. I am struggling to find the cause before, so hopefully I will find something now with your details.
To be honest I was aware of the limitations around AOT/generics, but I wasn't aware of those attributes until I went searching for the docs to link here. I will give them a go in my fork of 5.1.0 and let you know on here how that works out for me. Thanks for considering this issue, and the great library!
Theoretically, if they work, I am guessing it would mean no need to re-introduce WithoutFastExpressionCompiler
functionality, but instead updating the FastExpressionCompiler library to use same annotations..
After reading the docs some more, I came across this:
Rules.WithUseInterpretation
The compilation (essentially `System.Reflection.Emit`) is not supported by all targets, e.g. by the Xamarin iOS. In this case you may specify to always use the interpretation via the rule:
var c = new Container(rules => rules.WithUseInterpretation());
DryIoc uses its own interpretation mechanism which is faster than `System.Linq.Expressions.Expression.Compile(preferInterpretation: true)` because DryIoc can recognize its own internal methods in the resolved expression tree, and call them directly without reflection. It has other optimizations as well.
I knew Prism.Maui did not set this rule by default, so I tried adding it in a new branch of Prism.Maui referencing the stock v5.1.0 of DryIoc.dll from Nuget (as opposed to my fork). Pushed the Prism Nugets to private repo, referenced them in my app, did a clean/build & run in Release mode on iOS to ensure AOT compilation, and my issue is resolved!
Would like your opinion @dadhi but pretty sure that means this issue can be closed?
I will PR the fix into Prism.Maui separately.
@breenbob Huh, sorry :-( I was for some reason assuming that WithUseInterpretation()
is used.
Yes, this is the way to solve it.
In the related issues,
I wanted to improve the situation by recognizing the target is not supporting System.Reflection.Emit
and setting it in the container automatically.
But I did not find the way.
Maybe you will have a suggestion, as someone really working on these targets. It may be some environment check, some reflection probe for missing property, method or class... or preferably more robust modern way?
Yes sorry, I didn't write the package and not really looked at this before.
I'm honestly not sure if there is anyway to detect AOT compilation, either directly or by proxy. I do know there is a csproj setting that gets enabled for this by default in Release build configurations:
<RunAOTCompilation>true</RunAOTCompilation>
But doubt there is any way to check that at runtime. Perhaps some msbuild magic...
Whether you can check for unsupported aspects using reflection, I just don't know, but because of AOT compilation coming into play again I would doubt it.
All I can suggest (for Xamarin/.Net Maui at least) is to do something like this:
#if RELEASE && (ANDROID || IOS || MACCATALYST)
defaultRules = defaultRules.WithUseInterpretation();
#endif
But that would not be fool proof, e.g. a custom Release build configuration where the standard Release constant has not been defined. We do this sometimes to have separate configs for CI vs App Store releases...
@breenbob This one looks promising:
#if RELEASE && (ANDROID || IOS || MACCATALYST)
defaultRules = defaultRules.WithUseInterpretation();
#endif
Maybe RELEASE
is not required here, because why care for the compilation in the DEBUG
. I think from the debugging perspective it even better to have the interpretation, because it is more consistent experience (no compilation jump).
Then the next question, how to approach it when building the DryIoc library.
net6.0-ios
, net6.0-android
, etc.#if
in the partial class Container
implementation on the client side.
The both approaches may have a problem when DryIoc is the transitive dependency of the other library, like the Prism.MAUI.What are your thoughts on this? What targets are you using personally, what .net?
I am facing this same issue. I am on the same page with @breenbob using Prism, Maui and DryIoc. @breenbob did you manage to create a PR for Prism, or perhaps you found a different way of resolving this issue?
For those who may come across this thread. I found a solution with Prism.Maui and DryIoc.
Luckily there's an overload of UserPrism which takes the DryIoc Rules. So, I created rules and passed them to that extension method. IOS/Android apps in Release/AOT mode are not crashing anymore.
private static Rules DefaultRules => Rules.Default.WithConcreteTypeDynamicRegistrations(reuse:Reuse.Transient)
.WithUseInterpretation();
.....
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UsePrism(DefaultRules, Startup.SetupPrismApplication)
....
Prior to the removal of the .WithoutFastExpressionCompiler rule, DryIoc used to work fine in Xamarin.Android, Xamarin.iOS, and I suspect .Net Maui Android/iOS apps also. The introduction of fast expression compilation seems (to me) to be the point at which code that depended on availability of the JIT compiler to run was introduced.
As per the documentation on limitations of Xamarin.iOS, iOS apps must use AOT compilation and as such do not support aspects of generics and reflection that depend on JIT compilation.
I have run into this issue when using the Prism.DryIoc.Maui package:
The issue itself seems to stem from DryIoc and the FastCompilers packages in their dependency on these unsupported aspects.
This documentation also suggests marking methods that rely on these with one of the following attributes would bypass this problem: UnmanagedFunctionPointerAttribute (preferred, since it is cross-platform and compatible with .NET Standard 1.1+) MonoNativeFunctionWrapperAttribute
It states that failing to provide one of these attributes will result in a runtime error exactly like I am seeing in my iOS app above.
As an experiment I have tried forking DryIoc from v5.1.0 (version referenced by Prism.DryIoc.Maui) and re-adding the dropped commit for .WithoutFastExpressionCompiler. This was painstaking to say the least! I pushed the packed nuget for this to a private repo and referenced from there. After updating Prism.DryIoc.Maui and my app to use the updated packages, my app still crashed with a similar error to above... but it was in a different method this time, nothing to do with the fast compiler stuff:
So I am guessing DryIoc now has other areas in the code also dependent on JIT compilation added since this feature was removed that these changes alone do not cover.
Is it possible for support for AOT compilation to be added to DryIoc?
Note, best I can tell this issue does not seem to occur in Prism.Forms, which references DryIoc v4.7.7.
Any help or hints greatly appreciated!