Context: #505
PR #505 improves support for binding certain Kotlin constructs.
In "the perfect is the enemy of the good" manner, we will be merging PR #505 even though there's a known issue with it, in part because we don't currently know how to fix this scenario:
Method overloads and "name mangling".
In various situations, Kotlin will emit member names which are not valid Java identifiers. For example, consider this Kotlin class, which overloads the ExampleBase.foo() method to accept both Int and UInt parameters:
packageexample;
publicopenclassExampleBase {
publicopenfunfoo(value:Int) {
}
publicopenfunfoo(value:UInt) {
}
}The use of a UInt parameter type causes Kotlin to "name mangle" the method name, generating a method name which is not a valid Java identifier:
$ kotlinc hello.kt
$ javap -cp . example.ExampleBase
Compiled from "hello.kt"
public class example.ExampleBase {
public void foo(int);
public void foo-WZ4Q5Ns(int);
public example.ExampleBase();
}
With PR #505 we can create an API description:
<apiapi-source="class-parse">
<packagename="example"jni-name="example">
<classabstract="false"deprecated="not deprecated"jni-extends="Ljava/lang/Object;"extends="java.lang.Object"extends-generic-aware="java.lang.Object"final="false"name="ExampleBase"jni-signature="Lexample/ExampleBase;"source-file-name="hello.kt"static="false"visibility="public">
<constructordeprecated="not deprecated"final="false"name="ExampleBase"static="false"visibility="public"bridge="false"synthetic="false"jni-signature="()V" />
<methodabstract="false"deprecated="not deprecated"final="false"name="foo"native="false"return="void"jni-return="V"static="false"synchronized="false"visibility="public"bridge="false"synthetic="false"jni-signature="(I)V">
<parametername="value"type="int"jni-type="I" />
</method>
<methodabstract="false"deprecated="not deprecated"final="false"name="foo-WZ4Q5Ns"native="false"return="void"jni-return="V"static="false"synchronized="false"visibility="public"bridge="false"synthetic="false"jni-signature="(I)V">
<parametername="value"type="int"jni-type="I" />
</method>
</class>
</package>
</api>
The problem comes when we attempt to generate C# source to bind the above API description. It cannot compile, because ExampleBase.Foo(int) is emitted twice:
namespaceExample{[global::Android.Runtime.Register("example/ExampleBase",DoNotGenerateAcw=true)]publicpartialclassExampleBase:Java.Lang.Object{[Register("foo","(I)V","GetFoo_IHandler")]publicvirtualunsafevoidFoo(intvalue){conststring__id="foo.(I)V";try{JniArgumentValue*__args=stackallocJniArgumentValue[1];__args[0]=newJniArgumentValue(value);_members.InstanceMethods.InvokeVirtualVoidMethod(__id,this,__args);}finally{}}[Register("foo-WZ4Q5Ns","(I)V","GetFoo_IHandler")]publicvirtualunsafevoidFoo(intvalue){conststring__id="foo-WZ4Q5Ns.(I)V";try{JniArgumentValue*__args=stackallocJniArgumentValue[1];__args[0]=newJniArgumentValue(value);_members.InstanceMethods.InvokeVirtualVoidMethod(__id,this,__args);}finally{}}}}Attempting to compile the above will cause a CS0111 error to be generated:
src/Example.ExampleBase.cs(98,30): error CS0111: Type 'ExampleBase' already defines a member called 'Foo' with the same parameter types
This can be fixed with Metadata.xml, to "rename" one of the members. (This isn't necessarily ideal, but it's possible.)
<!-- Untested, but go with it for expository purposes -->
<attrpath="/api/package[@name='example']/class[@name='ExampleBase']/method[@name='foo-WZ4Q5Ns']"name="managedName">Foo2</attr>
Were we to do so, we'll find that subclassing won't work:
classMyExample:Example.ExampleBase{publicoverridevoidFoo2(intvalue){}}Subclassing cannot work, because we use Java as an intermediary, and the Java intermediary -- as generated by src/Java.Interop.Tools.JavaCallableWrappers -- uses the [Register] attribute, which will still specify foo-WZ4Q5Ns. We would thus (presumably; untested) get a Java Callable Wrapper for MyExample of:
/* partial */classMyExampleextendsexample.ExampleBase/* ... */ {
publicvoidfoo-WZ4Q5Ns(intvalue) {
n_foo-WZ4Q5Ns (value);
}
nativevoidn_foo-WZ4Q5Ns (intvalue);
}This cannot compile.
How do we address this?
Context: #505
PR #505 improves support for binding certain Kotlin constructs.
In "the perfect is the enemy of the good" manner, we will be merging PR #505 even though there's a known issue with it, in part because we don't currently know how to fix this scenario:
Method overloads and "name mangling".
In various situations, Kotlin will emit member names which are not valid Java identifiers. For example, consider this Kotlin class, which overloads the
ExampleBase.foo()method to accept bothIntandUIntparameters:The use of a
UIntparameter type causes Kotlin to "name mangle" the method name, generating a method name which is not a valid Java identifier:With PR #505 we can create an API description:
The problem comes when we attempt to generate C# source to bind the above API description. It cannot compile, because
ExampleBase.Foo(int)is emitted twice:Attempting to compile the above will cause a CS0111 error to be generated:
This can be fixed with
Metadata.xml, to "rename" one of the members. (This isn't necessarily ideal, but it's possible.)Were we to do so, we'll find that subclassing won't work:
Subclassing cannot work, because we use Java as an intermediary, and the Java intermediary -- as generated by
src/Java.Interop.Tools.JavaCallableWrappers-- uses the[Register]attribute, which will still specifyfoo-WZ4Q5Ns. We would thus (presumably; untested) get a Java Callable Wrapper forMyExampleof:This cannot compile.
How do we address this?