Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[generator] Support using DIM to nest interface types. (#589)
Context: #509 Consider the following Java code: package example; public interface Parent { public interface Child { } } Before C#8, interfaces could not contain types. To bind `example.Parent.Child`, we had to "un-nest" the interface, *prefixing* the interface name with the name of the enclosing type: namespace Example { public interface IParent { } public interface IParentChild { } } With C#8, interfaces can now contain types. We can now emit: namespace Example { public interface IParent { public interface IChild { } } } Enable this new style of binding when `generator --lang-features=nested-interface-types` is used. This is *disabled* by default. However, even when `nested-interface-types` is enabled, not all interfaces should follow this new strategy. In the case of `Mono.Android.dll`, nested interface types should *only* be used for types added in API-R/API-30. Add support for a new `unnest` Metadata attribute which can be placed on nested types to explicitly emit the C#7-compatible bindings: <attr path="/api/package[@name='example']/interface[@name='Parent.Child']" name="unnest">true</attr> In a future integration with xamarin-android, [`src/Mono.Android/metadata`][0] can be updated so that all APIs prior to API-30 are unnested, and all APIs starting with API-30 are nested: <!-- Handle everything that has always existed --> <attr api-since="30" path="/api/package/interface[not(@merge.SourceFile)]" name="unnest">true</attr> <attr api-since="30" path="/api/package/class[not(@merge.SourceFile)]" name="unnest">true</attr> <!-- Need these 2 for each platform prior to 30 --> <attr api-since="30" path="/api/package/interface[contains(@merge.SourceFile,'api-29.xml.in')]" name="unnest">true</attr> <attr api-since="30" path="/api/package/class[contains(@merge.SourceFile,'api-29.xml.in')]" name="unnest">true</attr> This will ensure we preserve API compatibility. [0]: https://github.com/xamarin/xamarin-android/blob/b366ac1e6d411dc052dc8711d881d71b7e967685/src/Mono.Android/metadata
- Loading branch information
Showing
18 changed files
with
833 additions
and
34 deletions.
There are no files selected for viewing
179 changes: 179 additions & 0 deletions
179
...-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteNestedInterfaceTypes.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,179 @@ | ||
// Metadata.xml XPath interface reference: path="/api/package[@name='com.xamarin.android']/interface[@name='Parent']" | ||
[Register ("com/xamarin/android/Parent", "", "Com.Xamarin.Android.IParentInvoker")] | ||
public partial interface IParent : IJavaObject, IJavaPeerable { | ||
|
||
int Bar { | ||
// Metadata.xml XPath method reference: path="/api/package[@name='com.xamarin.android']/interface[@name='Parent']/method[@name='getBar' and count(parameter)=0]" | ||
[Register ("getBar", "()I", "GetGetBarHandler:Com.Xamarin.Android.IParentInvoker, MyAssembly")] get; | ||
} | ||
|
||
// Metadata.xml XPath interface reference: path="/api/package[@name='com.xamarin.android']/interface[@name='Parent.Child']" | ||
[Register ("com/xamarin/android/Parent$Child", "", "Com.Xamarin.Android.IParent/IChildInvoker")] | ||
public partial interface IChild : IJavaObject, IJavaPeerable { | ||
|
||
int Bar { | ||
// Metadata.xml XPath method reference: path="/api/package[@name='com.xamarin.android']/interface[@name='Parent.Child']/method[@name='getBar' and count(parameter)=0]" | ||
[Register ("getBar", "()I", "GetGetBarHandler:Com.Xamarin.Android.IParent/IChildInvoker, MyAssembly")] get; | ||
} | ||
|
||
} | ||
|
||
[global::Android.Runtime.Register ("com/xamarin/android/Parent$Child", DoNotGenerateAcw=true)] | ||
internal partial class IChildInvoker : global::Java.Lang.Object, IChild { | ||
|
||
static readonly JniPeerMembers _members = new JniPeerMembers ("com/xamarin/android/Parent$Child", typeof (IChildInvoker)); | ||
|
||
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; } | ||
} | ||
|
||
new IntPtr class_ref; | ||
|
||
public static IChild GetObject (IntPtr handle, JniHandleOwnership transfer) | ||
{ | ||
return global::Java.Lang.Object.GetObject<IChild> (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), "com.xamarin.android.Parent.Child")); | ||
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 IChildInvoker (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_getBar; | ||
#pragma warning disable 0169 | ||
static Delegate GetGetBarHandler () | ||
{ | ||
if (cb_getBar == null) | ||
cb_getBar = JNINativeWrapper.CreateDelegate ((Func<IntPtr, IntPtr, int>) n_GetBar); | ||
return cb_getBar; | ||
} | ||
|
||
static int n_GetBar (IntPtr jnienv, IntPtr native__this) | ||
{ | ||
Com.Xamarin.Android.IParent.IChild __this = global::Java.Lang.Object.GetObject<Com.Xamarin.Android.IParent.IChild> (jnienv, native__this, JniHandleOwnership.DoNotTransfer); | ||
return __this.Bar; | ||
} | ||
#pragma warning restore 0169 | ||
|
||
IntPtr id_getBar; | ||
public unsafe int Bar { | ||
get { | ||
if (id_getBar == IntPtr.Zero) | ||
id_getBar = JNIEnv.GetMethodID (class_ref, "getBar", "()I"); | ||
return JNIEnv.CallIntMethod (((global::Java.Lang.Object) this).Handle, id_getBar); | ||
} | ||
} | ||
|
||
} | ||
|
||
|
||
} | ||
|
||
[global::Android.Runtime.Register ("com/xamarin/android/Parent", DoNotGenerateAcw=true)] | ||
internal partial class IParentInvoker : global::Java.Lang.Object, IParent { | ||
|
||
static readonly JniPeerMembers _members = new JniPeerMembers ("com/xamarin/android/Parent", typeof (IParentInvoker)); | ||
|
||
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; } | ||
} | ||
|
||
new IntPtr class_ref; | ||
|
||
public static IParent GetObject (IntPtr handle, JniHandleOwnership transfer) | ||
{ | ||
return global::Java.Lang.Object.GetObject<IParent> (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), "com.xamarin.android.Parent")); | ||
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 IParentInvoker (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_getBar; | ||
#pragma warning disable 0169 | ||
static Delegate GetGetBarHandler () | ||
{ | ||
if (cb_getBar == null) | ||
cb_getBar = JNINativeWrapper.CreateDelegate ((Func<IntPtr, IntPtr, int>) n_GetBar); | ||
return cb_getBar; | ||
} | ||
|
||
static int n_GetBar (IntPtr jnienv, IntPtr native__this) | ||
{ | ||
Com.Xamarin.Android.IParent __this = global::Java.Lang.Object.GetObject<Com.Xamarin.Android.IParent> (jnienv, native__this, JniHandleOwnership.DoNotTransfer); | ||
return __this.Bar; | ||
} | ||
#pragma warning restore 0169 | ||
|
||
IntPtr id_getBar; | ||
public unsafe int Bar { | ||
get { | ||
if (id_getBar == IntPtr.Zero) | ||
id_getBar = JNIEnv.GetMethodID (class_ref, "getBar", "()I"); | ||
return JNIEnv.CallIntMethod (((global::Java.Lang.Object) this).Handle, id_getBar); | ||
} | ||
} | ||
|
||
} | ||
|
179 changes: 179 additions & 0 deletions
179
...ests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteUnnestedInterfaceTypes.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,179 @@ | ||
// Metadata.xml XPath interface reference: path="/api/package[@name='com.xamarin.android']/interface[@name='Parent.Child']" | ||
[Register ("com/xamarin/android/Parent$Child", "", "Com.Xamarin.Android.IParentChildInvoker")] | ||
public partial interface IParentChild : IJavaObject, IJavaPeerable { | ||
|
||
int Bar { | ||
// Metadata.xml XPath method reference: path="/api/package[@name='com.xamarin.android']/interface[@name='Parent.Child']/method[@name='getBar' and count(parameter)=0]" | ||
[Register ("getBar", "()I", "GetGetBarHandler:Com.Xamarin.Android.IParentChildInvoker, MyAssembly")] get; | ||
} | ||
|
||
} | ||
|
||
[global::Android.Runtime.Register ("com/xamarin/android/Parent$Child", DoNotGenerateAcw=true)] | ||
internal partial class IParentChildInvoker : global::Java.Lang.Object, IParentChild { | ||
|
||
static readonly JniPeerMembers _members = new JniPeerMembers ("com/xamarin/android/Parent$Child", typeof (IParentChildInvoker)); | ||
|
||
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; } | ||
} | ||
|
||
new IntPtr class_ref; | ||
|
||
public static IParentChild GetObject (IntPtr handle, JniHandleOwnership transfer) | ||
{ | ||
return global::Java.Lang.Object.GetObject<IParentChild> (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), "com.xamarin.android.Parent.Child")); | ||
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 IParentChildInvoker (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_getBar; | ||
#pragma warning disable 0169 | ||
static Delegate GetGetBarHandler () | ||
{ | ||
if (cb_getBar == null) | ||
cb_getBar = JNINativeWrapper.CreateDelegate ((Func<IntPtr, IntPtr, int>) n_GetBar); | ||
return cb_getBar; | ||
} | ||
|
||
static int n_GetBar (IntPtr jnienv, IntPtr native__this) | ||
{ | ||
Com.Xamarin.Android.IParentChild __this = global::Java.Lang.Object.GetObject<Com.Xamarin.Android.IParentChild> (jnienv, native__this, JniHandleOwnership.DoNotTransfer); | ||
return __this.Bar; | ||
} | ||
#pragma warning restore 0169 | ||
|
||
IntPtr id_getBar; | ||
public unsafe int Bar { | ||
get { | ||
if (id_getBar == IntPtr.Zero) | ||
id_getBar = JNIEnv.GetMethodID (class_ref, "getBar", "()I"); | ||
return JNIEnv.CallIntMethod (((global::Java.Lang.Object) this).Handle, id_getBar); | ||
} | ||
} | ||
|
||
} | ||
|
||
|
||
// Metadata.xml XPath interface reference: path="/api/package[@name='com.xamarin.android']/interface[@name='Parent']" | ||
[Register ("com/xamarin/android/Parent", "", "Com.Xamarin.Android.IParentInvoker")] | ||
public partial interface IParent : IJavaObject, IJavaPeerable { | ||
|
||
int Bar { | ||
// Metadata.xml XPath method reference: path="/api/package[@name='com.xamarin.android']/interface[@name='Parent']/method[@name='getBar' and count(parameter)=0]" | ||
[Register ("getBar", "()I", "GetGetBarHandler:Com.Xamarin.Android.IParentInvoker, MyAssembly")] get; | ||
} | ||
|
||
} | ||
|
||
[global::Android.Runtime.Register ("com/xamarin/android/Parent", DoNotGenerateAcw=true)] | ||
internal partial class IParentInvoker : global::Java.Lang.Object, IParent { | ||
|
||
static readonly JniPeerMembers _members = new JniPeerMembers ("com/xamarin/android/Parent", typeof (IParentInvoker)); | ||
|
||
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; } | ||
} | ||
|
||
new IntPtr class_ref; | ||
|
||
public static IParent GetObject (IntPtr handle, JniHandleOwnership transfer) | ||
{ | ||
return global::Java.Lang.Object.GetObject<IParent> (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), "com.xamarin.android.Parent")); | ||
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 IParentInvoker (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_getBar; | ||
#pragma warning disable 0169 | ||
static Delegate GetGetBarHandler () | ||
{ | ||
if (cb_getBar == null) | ||
cb_getBar = JNINativeWrapper.CreateDelegate ((Func<IntPtr, IntPtr, int>) n_GetBar); | ||
return cb_getBar; | ||
} | ||
|
||
static int n_GetBar (IntPtr jnienv, IntPtr native__this) | ||
{ | ||
Com.Xamarin.Android.IParent __this = global::Java.Lang.Object.GetObject<Com.Xamarin.Android.IParent> (jnienv, native__this, JniHandleOwnership.DoNotTransfer); | ||
return __this.Bar; | ||
} | ||
#pragma warning restore 0169 | ||
|
||
IntPtr id_getBar; | ||
public unsafe int Bar { | ||
get { | ||
if (id_getBar == IntPtr.Zero) | ||
id_getBar = JNIEnv.GetMethodID (class_ref, "getBar", "()I"); | ||
return JNIEnv.CallIntMethod (((global::Java.Lang.Object) this).Handle, id_getBar); | ||
} | ||
} | ||
|
||
} | ||
|
Oops, something went wrong.