Open mmoraga opened 2 years ago
Thanks for reporting. We don't currently test StreamJsonRpc in AOT environments, so this may be one of many such issues. I have very little experience with AOT runtimes in general, so I'm not great at parsing these error messages. It appears that it's failing to retrieve the value of a simple property, so there's nothing particularly special like dynamic code compilation happening here. My guess is that the code that fails isn't statically invoked anywhere in the assembly so it didn't compile the property getter. Then when serializing, reflection led to invoking that property getter, leading to failure. It seems the solution would be to avoid trimming, or at least avoid trimming this assembly. Is that something that can be done?
I tried adding <MtouchLink>SdkOnly</MtouchLink>
to only link SDK assemblies but the issue is still there. I'll see if I can get some help from the Xamarin guys in disabling linker trimming for StreamJsonRpc.
Unfortunately disabling linking altogether brought in a ton of other issues and is not really an option. Is there anything else I could try or do to workaround the issue? Otherwise I'd be happy to help in finding a fix.
I'm not sure that linking is the problem as much as the AOT compiler. IIRC there's an xml file somewhere where you can list APIs to force them to compile for cases like this. But I have no idea where that file might be. I suppose the general workaround is call the API yourself so that the AOT compiler sees it's in use. But you'd have to call it from code that actually executes as well (or at least fools the compiler into believing it will execute).
If there's an attribute we can add to such APIs, we'd be willing to take a PR. But we'd want a test submitted as well that demonstrates AOT and that it all works, so that we can keep it working across versions.
I tried adding a separate linker.xml file as seen here: https://docs.microsoft.com/en-us/xamarin/cross-platform/deploy-test/linker
with the following configuration:
<linker>
<assembly fullname="StreamJsonRpc" preserve="all" />
</linker>
checking msbuild binlog I see it is being used:
/usr/local/share/dotnet/dotnet "/usr/local/share/dotnet/sdk/6.0.400/Sdks/Microsoft.NET.ILLink.Tasks/tools/net6.0/illink.dll" -x "Linker.xml"
But the resulting app still ends up throwing when getting the value of 'RequestId'. Perhaps this is more of a Xamarin issue.
It was a good try. But at this point ya, I think reaching out to Xamarin for help may be more profitable for you as we just don't have the expertise over here. But again, if you learn anything interesting, please share. Especially if there's something StreamJsonRpc can do to Just Work by default in this environment, I'd like to learn what that is, and we may be able to make those changes.
I created an issue in their issue tracker https://github.com/xamarin/xamarin-macios/issues/15961 it includes a small test repo where I managed to reproduce it.
If it helps anybody, I was able to get this library working in .NET 7 AOT through some trial and error, so something similar may be used for your project.
In my scenario I added a rd.xml
to my project:
...
<ItemGroup>
<RdXmlFile Include="rd.xml" />
</ItemGroup>
...
And the contents of my rd.xml
had to be tweaked over time to this:
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Application>
<!-- I'm sure this could be reduced to specific types, but I didn't have the time to explore this further -->
<Assembly Name="StreamJsonRpc" Dynamic="Required All" />
<!-- Include everything from my assembly that contains types going over the wire. Again, this could be reduced. -->
<Assembly Name="MyJsonRpcAssembly" Dynamic="Required All" />
<Assembly Name="mscorlib">
<!-- If any of my serialized types had array properties, e.g.,
class SomeType
{
public SomeProperty[] PropertyArray { get; set; }
}
then I had to add the following line.
Note that I had to assembly-qualify the type with the double brackets and comma.
Repeat as necessary. -->
<Type Name="System.Collections.Generic.List`1[[SomeProperty,MyJsonRpcAssembly]]" Dynamic="Required All" />
<!-- Finally, if any of my RPC methods were async and returned `Task<Something>`, I had to add each of those,
like shown below. If any of the Task result types were from your assembly, you'll need to do the double bracket
and comma resolution mentioned above. -->
<Type Name="System.Threading.Tasks.Task`1[System.Boolean]" Dynamic="Required All" />
<Type Name="System.Threading.Tasks.Task`1[[SomeType,MyJsonRpcAssembly]]" Dynamic="Required All" />
</Assembly>
<Application>
</Directives>
To discover this, I added a trace listener that logged all messages to a text file which I then examined after-the-fact. Rinse and repeat.
This only happens on AOT compiled Release builds of
net6.0-ios
. Debug builds don't have AOT compilation enabled, so I suspect this might have something to do with trimming/aot and reflection.