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

Error getting value from 'RequestId' on net6.0-ios app #840

Open
mmoraga opened this issue Sep 6, 2022 · 8 comments
Open

Error getting value from 'RequestId' on net6.0-ios app #840

mmoraga opened this issue Sep 6, 2022 · 8 comments

Comments

@mmoraga
Copy link

mmoraga commented Sep 6, 2022

Exception thrown while transmitting message: Newtonsoft.Json.JsonSerializationException: Error writing JSON RPC Message: JsonSerializationException: Error getting value from 'RequestId' on 'StreamJsonRpc.JsonMessageFormatter+JsonRpcError'.
 ---> Newtonsoft.Json.JsonSerializationException: Error getting value from 'RequestId' on 'StreamJsonRpc.JsonMessageFormatter+JsonRpcError'.
 ---> System.ExecutionEngineException: Attempting to JIT compile method '(wrapper delegate-invoke) StreamJsonRpc.RequestId <Module>:invoke_callvirt_RequestId_JsonRpcError (StreamJsonRpc.Protocol.JsonRpcError)' while running in aot-only mode. See https://docs.microsoft.com/xamarin/ios/internals/limitations for more information.

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.

@AArnott
Copy link
Member

AArnott commented Sep 6, 2022

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?

@mmoraga
Copy link
Author

mmoraga commented Sep 7, 2022

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.

@mmoraga
Copy link
Author

mmoraga commented Sep 9, 2022

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.

@AArnott
Copy link
Member

AArnott commented Sep 9, 2022

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.

@mmoraga
Copy link
Author

mmoraga commented Sep 13, 2022

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.

@AArnott
Copy link
Member

AArnott commented Sep 13, 2022

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.

@mmoraga
Copy link
Author

mmoraga commented Sep 15, 2022

I created an issue in their issue tracker xamarin/xamarin-macios#15961 it includes a small test repo where I managed to reproduce it.

@brettfo
Copy link
Member

brettfo commented Mar 8, 2023

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.

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

No branches or pull requests

3 participants