-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[generator] Support default interface methods. (#459)
Fixes: #25 Context: #341 Java 8 supports [interface default methods][0]: public interface HelloJava8 { public void a (); public default int getFoo () { return 8; } public default void setFoo (int newValue) { throw new UnsupportedOperationException(); } } With C#8, C# also supports [default interface members][1]. Add a new `generator --lang-features=default-interface-methods` flag which takes advantage of C#8 default interface members to bind Java 8 default interface methods: // C# binding of HelloJava8 public partial interface IHelloJava8 : IJavaObject, IJavaPeerable { static new readonly JniPeerMembers _members = new JniPeerMembers ("HelloJava8", typeof (IHelloJava8)); void A(); virtual unsafe int Foo { [Regsiter ("getFoo", "()I", "…")] get { return _members.InstanceMethods.InvokeVirtualInt32Method ("getFoo.()I", this, null); } [Regsiter ("setFoo", "(I)V", "…")] set { JniArgumentValue* __args = stackalloc JniArgumentValue [1]; __args [0] = new JniArgumentValue (value); return _members.InstanceMethods.InvokeVirtualVoidMethod ("setFoo.(I)V", this, __args); } } } C#8 Default Interface Members cannot be used with legacy `generator --codegen-target=XamarinAndroid`, as they require the `IJavaPeerable` infrastructure in order to work. Connector Methods are emitted within the interface binding, and not within the corresponding `*Invoker` type. If a Java default interface method is "invalid", we just skip binding the method instead of invalidating the entire interface, just as we do with classes and non-`abstract` methods. Finally, the default interface method implementation uses `virtual` dispatch, *not* non-`virtual` dispatch, in order to support Java-side versioning. For example, imagine `exampele.jar` v1: // Java public interface Fooable { default void foo() { System.out.println ("Fooable.foo"); } } public class Example implements Fooable { } In v1, `Example` does *not* contain an `Example.foo()` method, though `foo()` can be invoked on it, because of `Fooable.foo()`: Fooable value = new Example(); value.foo(); // Invokes Fooable.foo() In v2, `Example` overrides `Fooable.foo`: public class Example implements Fooable { public void foo() { System.out.println ("Example.foo"); } } If our binding used non-`virtual` dispatch for `IFooable.Foo()`, and bound `example.jar` v1, then if we updated `example.jar` to v2 *without* producing a new binding -- and why should a new binding be required? -- then we would continue invoking `Fooable.foo()` when we *should* be invoking `Example.foo()`. Use of `virtual` dispatch thus ensures we support Java-side versioning. [0]: https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html [1]: https://github.com/dotnet/csharplang/blob/f7952cdddf85316a4beec493a0ecc14fcb3241c8/proposals/csharp-8.0/default-interface-methods.md
- Loading branch information
Showing
27 changed files
with
831 additions
and
99 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 81 additions & 36 deletions
117
tools/generator/Java.Interop.Tools.Generator.CodeGeneration/CodeGenerator.cs
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 4 additions & 5 deletions
9
...s/XamarinAndroid/WriteInterfaceMethod.txt → ...WriteInterfaceRedeclaredDefaultMethod.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
...it-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteDefaultInterfaceMethodInvoker.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
[global::Android.Runtime.Register ("java/code/IMyInterface", DoNotGenerateAcw=true)] | ||
internal partial class IMyInterfaceInvoker : global::Java.Lang.Object, IMyInterface { | ||
|
||
internal static new readonly JniPeerMembers _members = new JniPeerMembers ("java/code/IMyInterface", typeof (IMyInterfaceInvoker)); | ||
|
||
static IntPtr java_class_ref { | ||
get { return _members.JniPeerType.PeerReference.Handle; } | ||
} | ||
|
||
public override global::Java.Interop.JniPeerMembers JniPeerMembers { | ||
get { return _members; } | ||
} | ||
|
||
protected override IntPtr ThresholdClass { | ||
get { return class_ref; } | ||
} | ||
|
||
protected override global::System.Type ThresholdType { | ||
get { return _members.ManagedPeerType; } | ||
} | ||
|
||
IntPtr class_ref; | ||
|
||
public static IMyInterface GetObject (IntPtr handle, JniHandleOwnership transfer) | ||
{ | ||
return global::Java.Lang.Object.GetObject<IMyInterface> (handle, transfer); | ||
} | ||
|
||
static IntPtr Validate (IntPtr handle) | ||
{ | ||
if (!JNIEnv.IsInstanceOf (handle, java_class_ref)) | ||
throw new InvalidCastException (string.Format ("Unable to convert instance of type '{0}' to type '{1}'.", | ||
JNIEnv.GetClassNameFromInstance (handle), "java.code.IMyInterface")); | ||
return handle; | ||
} | ||
|
||
protected override void Dispose (bool disposing) | ||
{ | ||
if (this.class_ref != IntPtr.Zero) | ||
JNIEnv.DeleteGlobalRef (this.class_ref); | ||
this.class_ref = IntPtr.Zero; | ||
base.Dispose (disposing); | ||
} | ||
|
||
public IMyInterfaceInvoker (IntPtr handle, JniHandleOwnership transfer) : base (Validate (handle), transfer) | ||
{ | ||
IntPtr local_ref = JNIEnv.GetObjectClass (((global::Java.Lang.Object) this).Handle); | ||
this.class_ref = JNIEnv.NewGlobalRef (local_ref); | ||
JNIEnv.DeleteLocalRef (local_ref); | ||
} | ||
|
||
static Delegate cb_DoDeclaration; | ||
#pragma warning disable 0169 | ||
static Delegate GetDoDeclarationHandler () | ||
{ | ||
if (cb_DoDeclaration == null) | ||
cb_DoDeclaration = JNINativeWrapper.CreateDelegate ((Action<IntPtr, IntPtr>) n_DoDeclaration); | ||
return cb_DoDeclaration; | ||
} | ||
|
||
static void n_DoDeclaration (IntPtr jnienv, IntPtr native__this) | ||
{ | ||
java.code.IMyInterface __this = global::Java.Lang.Object.GetObject<java.code.IMyInterface> (jnienv, native__this, JniHandleOwnership.DoNotTransfer); | ||
__this.DoDeclaration (); | ||
} | ||
#pragma warning restore 0169 | ||
|
||
IntPtr id_DoDeclaration; | ||
public unsafe void DoDeclaration () | ||
{ | ||
if (id_DoDeclaration == IntPtr.Zero) | ||
id_DoDeclaration = JNIEnv.GetMethodID (class_ref, "DoDeclaration", "()V"); | ||
JNIEnv.CallVoidMethod (((global::Java.Lang.Object) this).Handle, id_DoDeclaration); | ||
} | ||
|
||
} | ||
|
34 changes: 34 additions & 0 deletions
34
...ests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteInterfaceDefaultMethod.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// Metadata.xml XPath interface reference: path="/api/package[@name='java.code']/interface[@name='IMyInterface']" | ||
[Register ("java/code/IMyInterface", "", "java.code.IMyInterfaceInvoker")] | ||
public partial interface IMyInterface : IJavaObject, IJavaPeerable { | ||
static new readonly JniPeerMembers _members = new JniPeerMembers ("java/code/IMyInterface", typeof (IMyInterface)); | ||
|
||
static Delegate cb_DoSomething; | ||
#pragma warning disable 0169 | ||
static Delegate GetDoSomethingHandler () | ||
{ | ||
if (cb_DoSomething == null) | ||
cb_DoSomething = JNINativeWrapper.CreateDelegate ((Action<IntPtr, IntPtr>) n_DoSomething); | ||
return cb_DoSomething; | ||
} | ||
|
||
static void n_DoSomething (IntPtr jnienv, IntPtr native__this) | ||
{ | ||
java.code.IMyInterface __this = global::Java.Lang.Object.GetObject<java.code.IMyInterface> (jnienv, native__this, JniHandleOwnership.DoNotTransfer); | ||
__this.DoSomething (); | ||
} | ||
#pragma warning restore 0169 | ||
|
||
// Metadata.xml XPath method reference: path="/api/package[@name='java.code']/interface[@name='IMyInterface']/method[@name='DoSomething' and count(parameter)=0]" | ||
[Register ("DoSomething", "()V", "GetDoSomethingHandler:java.code.IMyInterface, MyAssembly")] | ||
virtual unsafe void DoSomething () | ||
{ | ||
const string __id = "DoSomething.()V"; | ||
try { | ||
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, null); | ||
} finally { | ||
} | ||
} | ||
|
||
} | ||
|
63 changes: 63 additions & 0 deletions
63
...ts/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteInterfaceDefaultProperty.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// Metadata.xml XPath interface reference: path="/api/package[@name='java.code']/interface[@name='IMyInterface']" | ||
[Register ("java/code/IMyInterface", "", "java.code.IMyInterfaceInvoker")] | ||
public partial interface IMyInterface : IJavaObject, IJavaPeerable { | ||
static new readonly JniPeerMembers _members = new JniPeerMembers ("java/code/IMyInterface", typeof (IMyInterface)); | ||
|
||
static Delegate cb_get_Value; | ||
#pragma warning disable 0169 | ||
static Delegate Getget_ValueHandler () | ||
{ | ||
if (cb_get_Value == null) | ||
cb_get_Value = JNINativeWrapper.CreateDelegate ((Func<IntPtr, IntPtr, int>) n_get_Value); | ||
return cb_get_Value; | ||
} | ||
|
||
static int n_get_Value (IntPtr jnienv, IntPtr native__this) | ||
{ | ||
java.code.IMyInterface __this = global::Java.Lang.Object.GetObject<java.code.IMyInterface> (jnienv, native__this, JniHandleOwnership.DoNotTransfer); | ||
return __this.Value; | ||
} | ||
#pragma warning restore 0169 | ||
|
||
static Delegate cb_set_Value_I; | ||
#pragma warning disable 0169 | ||
static Delegate Getset_Value_IHandler () | ||
{ | ||
if (cb_set_Value_I == null) | ||
cb_set_Value_I = JNINativeWrapper.CreateDelegate ((Action<IntPtr, IntPtr, int>) n_set_Value_I); | ||
return cb_set_Value_I; | ||
} | ||
|
||
static void n_set_Value_I (IntPtr jnienv, IntPtr native__this, int value) | ||
{ | ||
java.code.IMyInterface __this = global::Java.Lang.Object.GetObject<java.code.IMyInterface> (jnienv, native__this, JniHandleOwnership.DoNotTransfer); | ||
__this.Value = value; | ||
} | ||
#pragma warning restore 0169 | ||
|
||
virtual unsafe int Value { | ||
// Metadata.xml XPath method reference: path="/api/package[@name='java.code']/interface[@name='IMyInterface']/method[@name='get_Value' and count(parameter)=0]" | ||
[Register ("get_Value", "()I", "Getget_ValueHandler:java.code.IMyInterface, MyAssembly")] | ||
get { | ||
const string __id = "get_Value.()I"; | ||
try { | ||
var __rm = _members.InstanceMethods.InvokeVirtualInt32Method (__id, this, null); | ||
return __rm; | ||
} finally { | ||
} | ||
} | ||
// Metadata.xml XPath method reference: path="/api/package[@name='java.code']/interface[@name='IMyInterface']/method[@name='set_Value' and count(parameter)=1 and parameter[1][@type='int']]" | ||
[Register ("set_Value", "(I)V", "Getset_Value_IHandler:java.code.IMyInterface, MyAssembly")] | ||
set { | ||
const string __id = "set_Value.(I)V"; | ||
try { | ||
JniArgumentValue* __args = stackalloc JniArgumentValue [1]; | ||
__args [0] = new JniArgumentValue (value); | ||
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args); | ||
} finally { | ||
} | ||
} | ||
} | ||
|
||
} | ||
|
Oops, something went wrong.