dotnet / java-interop

Java.Interop provides open-source bindings of Java's Java Native Interface (JNI) for use with .NET managed languages such as C#
Other
201 stars 52 forks source link

Remove API adjuster #789

Open jonpryor opened 3 years ago

jonpryor commented 3 years ago

Context: https://github.com/xamarin/xamarin-android/issues/5580

The current binding workflow with class-parse results in three api.xml files:

  1. The class-parse output, in api.xml.class-parse
  2. The "adjusted" output, in api.xml (via generator --only-xml-adjuster --xml-adjuster-output=api.xml)
  3. The "fixed" output, after applying Metadata.xml to api.xml, in api.xml.fixed.

The problem is (2): it removes types present in (1).

Take for example xamarin/xamarin-android#5580, in which the HttpRequest..serializer type is not bound.

The problem is that it's removed by (2):

  Error while processing type '[Class] com.vmware.chameleon.http.HttpRequest..serializer': Type 'kotlinx.serialization.internal.GeneratedSerializer' was not found. (TaskId:36)

and thus is not present in api.xml. Consequently, you can't write Metadata.xml transforms which attempt to reference the type, as the type doesn't exist when Metadata.xml is being processed.

The only workaround here is to use <add-node/> with Metadata.xml, copying the XML fragment from api.xml.class-parse, a'la

  <add-node path="//package[@name='com.vmware.chameleon.http']">
    <class
      abstract="false"
      deprecated="deprecated"
      jni-extends="Ljava/lang/Object;"
      extends="java.lang.Object"
      extends-generic-aware="java.lang.Object"
      final="true"
      name="HttpRequest..serializer"
      jni-signature="Lcom/vmware/chameleon/http/HttpRequest$$serializer;"
      source-file-name="HttpRequest.kt"
      static="true"
      visibility="public">
      <implements
        name="kotlinx.serialization.internal.GeneratedSerializer"
        name-generic-aware="kotlinx.serialization.internal.GeneratedSerializer&lt;com.vmware.chameleon.http.HttpRequest&gt;"
        jni-type="Lkotlinx/serialization/internal/GeneratedSerializer&lt;Lcom/vmware/chameleon/http/HttpRequest;&gt;;" />
      <method
        abstract="false"
        deprecated="not deprecated"
        final="false"
        name="childSerializers"
        native="false"
        return="kotlinx.serialization.KSerializer&lt;?&gt;[]"
        jni-return="[Lkotlinx/serialization/KSerializer&lt;*&gt;;"
        static="false"
        synchronized="false"
        visibility="public"
        bridge="false"
        synthetic="false"
        jni-signature="()[Lkotlinx/serialization/KSerializer;"
        return-not-null="true" />
      <method
        abstract="false"
        deprecated="not deprecated"
        final="false"
        name="deserialize"
        native="false"
        return="com.vmware.chameleon.http.HttpRequest"
        jni-return="Lcom/vmware/chameleon/http/HttpRequest;"
        static="false"
        synchronized="false"
        visibility="public"
        bridge="false"
        synthetic="false"
        jni-signature="(Lkotlinx/serialization/Decoder;)Lcom/vmware/chameleon/http/HttpRequest;"
        return-not-null="true">
        <parameter
          name="decoder"
          type="kotlinx.serialization.Decoder"
          jni-type="Lkotlinx/serialization/Decoder;"
          not-null="true" />
      </method>
      <method
        abstract="false"
        deprecated="not deprecated"
        final="false"
        name="deserialize"
        native="false"
        return="java.lang.Object"
        jni-return="Ljava/lang/Object;"
        static="false"
        synchronized="false"
        visibility="public"
        bridge="true"
        synthetic="true"
        jni-signature="(Lkotlinx/serialization/Decoder;)Ljava/lang/Object;">
        <parameter
          name="p0"
          type="kotlinx.serialization.Decoder"
          jni-type="Lkotlinx/serialization/Decoder;" />
      </method>
      <method
        abstract="false"
        deprecated="not deprecated"
        final="false"
        name="getDescriptor"
        native="false"
        return="kotlinx.serialization.SerialDescriptor"
        jni-return="Lkotlinx/serialization/SerialDescriptor;"
        static="false"
        synchronized="false"
        visibility="public"
        bridge="false"
        synthetic="false"
        jni-signature="()Lkotlinx/serialization/SerialDescriptor;"
        return-not-null="true" />
      <method
        abstract="false"
        deprecated="not deprecated"
        final="false"
        name="patch"
        native="false"
        return="com.vmware.chameleon.http.HttpRequest"
        jni-return="Lcom/vmware/chameleon/http/HttpRequest;"
        static="false"
        synchronized="false"
        visibility="public"
        bridge="false"
        synthetic="false"
        jni-signature="(Lkotlinx/serialization/Decoder;Lcom/vmware/chameleon/http/HttpRequest;)Lcom/vmware/chameleon/http/HttpRequest;"
        return-not-null="true">
        <parameter
          name="this"
          type="kotlinx.serialization.Decoder"
          jni-type="Lkotlinx/serialization/Decoder;"
          not-null="true" />
        <parameter
          name="decoder"
          type="com.vmware.chameleon.http.HttpRequest"
          jni-type="Lcom/vmware/chameleon/http/HttpRequest;"
          not-null="true" />
      </method>
      <method
        abstract="false"
        deprecated="not deprecated"
        final="false"
        name="patch"
        native="false"
        return="java.lang.Object"
        jni-return="Ljava/lang/Object;"
        static="false"
        synchronized="false"
        visibility="public"
        bridge="true"
        synthetic="true"
        jni-signature="(Lkotlinx/serialization/Decoder;Ljava/lang/Object;)Ljava/lang/Object;">
        <parameter
          name="p0"
          type="kotlinx.serialization.Decoder"
          jni-type="Lkotlinx/serialization/Decoder;" />
        <parameter
          name="p1"
          type="java.lang.Object"
          jni-type="Ljava/lang/Object;" />
      </method>
      <method
        abstract="false"
        deprecated="not deprecated"
        final="false"
        name="serialize"
        native="false"
        return="void"
        jni-return="V"
        static="false"
        synchronized="false"
        visibility="public"
        bridge="false"
        synthetic="false"
        jni-signature="(Lkotlinx/serialization/Encoder;Lcom/vmware/chameleon/http/HttpRequest;)V">
        <parameter
          name="encoder"
          type="kotlinx.serialization.Encoder"
          jni-type="Lkotlinx/serialization/Encoder;"
          not-null="true" />
        <parameter
          name="value"
          type="com.vmware.chameleon.http.HttpRequest"
          jni-type="Lcom/vmware/chameleon/http/HttpRequest;"
          not-null="true" />
      </method>
      <method
        abstract="false"
        deprecated="not deprecated"
        final="false"
        name="serialize"
        native="false"
        return="void"
        jni-return="V"
        static="false"
        synchronized="false"
        visibility="public"
        bridge="true"
        synthetic="true"
        jni-signature="(Lkotlinx/serialization/Encoder;Ljava/lang/Object;)V">
        <parameter
          name="p0"
          type="kotlinx.serialization.Encoder"
          jni-type="Lkotlinx/serialization/Encoder;" />
        <parameter
          name="p1"
          type="java.lang.Object"
          jni-type="Ljava/lang/Object;" />
      </method>
      <field
        deprecated="not deprecated"
        final="true"
        name="INSTANCE"
        static="true"
        synthetic="false"
        transient="false"
        type="com.vmware.chameleon.http.HttpRequest..serializer"
        type-generic-aware="com.vmware.chameleon.http.HttpRequest..serializer"
        jni-signature="Lcom/vmware/chameleon/http/HttpRequest$$serializer;"
        visibility="public"
        volatile="false" />
    </class>
  </add-node>
jonpryor commented 3 years ago

Note: in the case of xamarin/xamarin-android#5580, the above <add-node/> does not work, because generator doesn't like .. in the name.

If I manually replace all instances of HttpRequest..serializer with HttpRequest._serializer, a HttpRequest._serializer binding is created. Unfortunately it's not valid, because the register attribute is wrong.

Instead, I can add managedName into the <add-node/> text, and then it works!

    <class
      abstract="false"
      managedName="HttpRequest._Serializer"
…

Something I hadn't considered.

Regardless, generator should do more than "silently fail" when a type name has .. in it.

jpobst commented 3 years ago

I just happened to notice the .. issue in generator. Leaving a link here for future me:

https://github.com/xamarin/java.interop/blob/main/tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBaseSupport.cs#L24-L27

akalghatgi commented 3 years ago

@jpobst when is this planned... we are stuck with are release...

jpobst commented 2 years ago

I'm not sure this really explains what the issue is, or what the desired fix is. Resolving all Java types and removing types and members that depend on types we cannot resolve is a needed step in the binding process, lest we create broken bindings.

I think we would need to explore:

For example, in this reported case:

Error while processing type '[Class] com.vmware.chameleon.http.HttpRequest..serializer': Type 'kotlinx.serialization.internal.GeneratedSerializer' was not found.

Why is GeneratedSerializer not found? Why do we still want to bind HttpRequest..serializer if it requires the missing GeneratedSerializer?