Skip to content

Commit f1cd7cc

Browse files
committed
Initial default interface methods work.
- Added simple test case that contains a DIM method and a DIM-based property. - CodeGenerator takes SupportDefaultInterfaceMethods property. - command line option --default-interface-methods is added. - DIMs in interface generates implementation, not declaration. - DIM-based properties in interface generates implementation, not declaration.
1 parent 5efe5c2 commit f1cd7cc

File tree

9 files changed

+218
-32
lines changed

9 files changed

+218
-32
lines changed

tools/generator/ClassGen.cs

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -366,24 +366,6 @@ void GenMethods (StreamWriter sw, string indent, CodeGenerationOptions opt)
366366
}
367367
}
368368

369-
void GenProperties (StreamWriter sw, string indent, CodeGenerationOptions opt)
370-
{
371-
foreach (Property prop in Properties) {
372-
bool get_virt = prop.Getter.IsVirtual;
373-
bool set_virt = prop.Setter == null ? false : prop.Setter.IsVirtual;
374-
prop.Getter.IsVirtual = !IsFinal && get_virt;
375-
if (prop.Setter != null)
376-
prop.Setter.IsVirtual = !IsFinal && set_virt;
377-
if (prop.Getter.IsAbstract)
378-
prop.GenerateAbstractDeclaration (sw, indent, opt, this);
379-
else
380-
prop.Generate (this, sw, indent, opt);
381-
prop.Getter.IsVirtual = get_virt;
382-
if (prop.Setter != null)
383-
prop.Setter.IsVirtual = set_virt;
384-
}
385-
}
386-
387369
public override void Generate (StreamWriter sw, string indent, CodeGenerationOptions opt, GenerationInfo gen_info)
388370
{
389371
opt.ContextTypes.Push (this);
@@ -480,7 +462,7 @@ public override void Generate (StreamWriter sw, string indent, CodeGenerationOpt
480462

481463
GenConstructors (sw, indent + "\t", opt);
482464

483-
GenProperties (sw, indent + "\t", opt);
465+
GenerateImplementedProperties (sw, indent + "\t", IsFinal, opt);
484466
GenMethods (sw, indent + "\t", opt);
485467

486468
if (IsAbstract)

tools/generator/CodeGenerator.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public CodeGeneratorOptions ()
5757
public string MappingReportFile { get; set; }
5858
public bool OnlyRunApiXmlAdjuster { get; set; }
5959
public string ApiXmlAdjusterOutput { get; set; }
60+
public bool SupportDefaultInterfaceMethods { get; set; }
6061

6162
public static CodeGeneratorOptions Parse (string[] args)
6263
{
@@ -103,6 +104,9 @@ public static CodeGeneratorOptions Parse (string[] args)
103104
{ "sdk-platform|api-level=",
104105
"SDK Platform {VERSION}/API level.",
105106
v => opts.ApiLevel = v },
107+
{ "default-interface-methods",
108+
"For internal use.",
109+
v => opts.SupportDefaultInterfaceMethods = v != null },
106110
{ "preserve-enums",
107111
"For internal use.",
108112
v => opts.PreserveEnums = v != null },
@@ -242,7 +246,8 @@ static void Run (CodeGeneratorOptions options, DirectoryAssemblyResolver resolve
242246
UseGlobal = options.GlobalTypeNames,
243247
IgnoreNonPublicType = true,
244248
UseShortFileNames = options.UseShortFileNames,
245-
ProductVersion = options.ProductVersion
249+
ProductVersion = options.ProductVersion,
250+
SupportDefaultInterfaceMethods = options.SupportDefaultInterfaceMethods,
246251
};
247252

248253
// Load reference libraries
@@ -313,7 +318,7 @@ static void Run (CodeGeneratorOptions options, DirectoryAssemblyResolver resolve
313318
// disable interface default methods here, especially before validation.
314319
gens = gens.Where (g => !g.IsObfuscated && g.Visibility != "private").ToList ();
315320
foreach (var gen in gens) {
316-
gen.StripNonBindables ();
321+
gen.StripNonBindables (opt);
317322
if (gen.IsGeneratable)
318323
AddTypeToTable (opt, gen);
319324
}
@@ -624,6 +629,7 @@ public CodeGenerationTarget CodeGenerationTarget {
624629
public bool UseShortFileNames { get; set; }
625630
public IList<GenBase> Gens {get;set;}
626631
public int ProductVersion { get; set; }
632+
public bool SupportDefaultInterfaceMethods { get; set; }
627633

628634
public string GetOutputName (string s)
629635
{
@@ -1084,7 +1090,8 @@ public void WriteMethod (Method method, TextWriter writer, string indent, CodeGe
10841090
writer.WriteLine ("{0}[Register (\"{1}\", \"{2}\", \"{3}\"{4})]",
10851091
indent, method.JavaName, method.JniSignature, method.IsVirtual ? method.ConnectorName : String.Empty, method.AdditionalAttributeString ());
10861092
WriteMethodCustomAttributes (method, writer, indent);
1087-
writer.WriteLine ("{0}{1}{2}{3}{4} unsafe {5} {6} ({7})", indent, method.Visibility, static_arg, virt_ov, seal, ret, method.AdjustedName, GenBase.GetSignature (method, opt));
1093+
string visibility = type.IsInterface ? string.Empty : method.Visibility;
1094+
writer.WriteLine ("{0}{1}{2}{3}{4} unsafe {5} {6} ({7})", indent, visibility, static_arg, virt_ov, seal, ret, method.AdjustedName, GenBase.GetSignature (method, opt));
10881095
writer.WriteLine ("{0}{{", indent);
10891096
WriteMethodBody (method, writer, indent + "\t", opt);
10901097
writer.WriteLine ("{0}}}", indent);

tools/generator/GenBase.cs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ public string DeprecatedComment {
6464
public bool IsGeneratable {
6565
get { return support.IsGeneratable; }
6666
}
67+
68+
public virtual bool IsInterface {
69+
get { return false; }
70+
}
6771

6872
public virtual ClassGen BaseGen {
6973
get { return null; }
@@ -595,6 +599,24 @@ public List<InterfaceGen> GetAllDerivedInterfaces ()
595599
return result;
596600
}
597601

602+
protected void GenerateImplementedProperties (StreamWriter sw, string indent, bool isFinal, CodeGenerationOptions opt)
603+
{
604+
foreach (Property prop in Properties) {
605+
bool get_virt = prop.Getter.IsVirtual;
606+
bool set_virt = prop.Setter == null ? false : prop.Setter.IsVirtual;
607+
prop.Getter.IsVirtual = !isFinal && get_virt;
608+
if (prop.Setter != null)
609+
prop.Setter.IsVirtual = !isFinal && set_virt;
610+
if (prop.Getter.IsAbstract)
611+
prop.GenerateAbstractDeclaration (sw, indent, opt, this);
612+
else
613+
prop.Generate (this, sw, indent, opt);
614+
prop.Getter.IsVirtual = get_virt;
615+
if (prop.Setter != null)
616+
prop.Setter.IsVirtual = set_virt;
617+
}
618+
}
619+
598620
void GetAllDerivedInterfaces (List<InterfaceGen> ifaces)
599621
{
600622
foreach (ISymbol isym in Interfaces) {
@@ -716,15 +738,16 @@ protected virtual bool OnValidate (CodeGenerationOptions opt, GenericParameterDe
716738

717739
bool property_filling;
718740

719-
public void StripNonBindables ()
741+
public void StripNonBindables (CodeGenerationOptions opt)
720742
{
721743
// As of now, if we generate bindings for interface default methods, that means users will
722744
// have to "implement" those methods because they are declared and you have to implement
723745
// any declared methods in C#. That is going to be problematic a lot.
724-
methods = methods.Where (m => !m.IsInterfaceDefaultMethod).ToList ();
746+
if (!opt.SupportDefaultInterfaceMethods)
747+
methods = methods.Where (m => !m.IsInterfaceDefaultMethod).ToList ();
725748
nested_types = nested_types.Where (n => !n.IsObfuscated && n.Visibility != "private").ToList ();
726749
foreach (var n in nested_types)
727-
n.StripNonBindables ();
750+
n.StripNonBindables (opt);
728751
}
729752

730753
public virtual void FixupAccessModifiers (CodeGenerationOptions opt)

tools/generator/InterfaceGen.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
using Xamarin.Android.Binder;
1111
using Xamarin.Android.Tools;
12+
using System.Diagnostics;
1213

1314
namespace MonoDroid.Generation {
1415
#if HAVE_CECIL
@@ -106,6 +107,10 @@ public bool IsConstSugar {
106107
}
107108
}
108109

110+
public override bool IsInterface {
111+
get { return true; }
112+
}
113+
109114
public bool IsListener {
110115
// If there is a property it cannot generate valid implementor, so reject this at least so far.
111116
get { return Name.EndsWith ("Listener") && Properties.Count == 0 && Interfaces.Count == 0; }
@@ -197,11 +202,13 @@ protected override bool OnValidate (CodeGenerationOptions opt, GenericParameterD
197202

198203
void GenMethods (StreamWriter sw, string indent, CodeGenerationOptions opt)
199204
{
200-
foreach (Method m in Methods.Where (m => !m.IsStatic)) {
205+
foreach (Method m in Methods.Where (m => !m.IsStatic && !m.IsInterfaceDefaultMethod)) {
201206
if (m.Name == Name || ContainsProperty (m.Name, true))
202207
m.Name = "Invoke" + m.Name;
203208
opt.CodeGenerator.WriteMethodDeclaration (m, sw, indent, opt, this, AssemblyQualifiedName + "Invoker");
204209
}
210+
foreach (Method m in Methods.Where (m => m.IsInterfaceDefaultMethod))
211+
opt.CodeGenerator.WriteMethod (m, sw, indent, opt, this, true);
205212
}
206213

207214
void GenExtensionMethods (StreamWriter sw, string indent, CodeGenerationOptions opt)
@@ -214,8 +221,9 @@ void GenExtensionMethods (StreamWriter sw, string indent, CodeGenerationOptions
214221

215222
void GenProperties (StreamWriter sw, string indent, CodeGenerationOptions opt)
216223
{
217-
foreach (Property prop in Properties.Where (p => !p.Getter.IsStatic))
224+
foreach (Property prop in Properties.Where (p => !p.Getter.IsStatic && !p.Getter.IsInterfaceDefaultMethod))
218225
prop.GenerateDeclaration (sw, indent, opt, this, AssemblyQualifiedName + "Invoker");
226+
base.GenerateImplementedProperties (sw, indent, false, opt);
219227
}
220228

221229
void GenerateInvoker (StreamWriter sw, string indent, CodeGenerationOptions opt)
@@ -256,14 +264,14 @@ void GenerateInvoker (StreamWriter sw, string indent, CodeGenerationOptions opt)
256264
sw.WriteLine ();
257265

258266
HashSet<string> members = new HashSet<string> ();
259-
GenerateInvoker (sw, Properties.Where (p => !p.Getter.IsStatic), indent + "\t", opt, members);
260-
GenerateInvoker (sw, Methods.Where (m => !m.IsStatic), indent + "\t", opt, members);
267+
GenerateInvoker (sw, Properties.Where (p => !p.Getter.IsStatic && !p.Getter.IsInterfaceDefaultMethod), indent + "\t", opt, members);
268+
GenerateInvoker (sw, Methods.Where (m => !m.IsStatic && !m.IsInterfaceDefaultMethod), indent + "\t", opt, members);
261269
if (FullName == "Java.Lang.ICharSequence")
262270
GenCharSequenceEnumerator (sw, indent + "\t", opt);
263271

264272
foreach (InterfaceGen iface in GetAllDerivedInterfaces ()) {
265-
GenerateInvoker (sw, iface.Properties.Where (p => !p.Getter.IsStatic), indent + "\t", opt, members);
266-
GenerateInvoker (sw, iface.Methods.Where (m => !m.IsStatic && !IsCovariantMethod (m) && !(iface.FullName.StartsWith ("Java.Lang.ICharSequence") && m.Name.EndsWith ("Formatted"))), indent + "\t", opt, members);
273+
GenerateInvoker (sw, iface.Properties.Where (p => !p.Getter.IsStatic && !p.Getter.IsInterfaceDefaultMethod), indent + "\t", opt, members);
274+
GenerateInvoker (sw, iface.Methods.Where (m => !m.IsStatic && !m.IsInterfaceDefaultMethod && !IsCovariantMethod (m) && !(iface.FullName.StartsWith ("Java.Lang.ICharSequence") && m.Name.EndsWith ("Formatted"))), indent + "\t", opt, members);
267275
if (iface.FullName == "Java.Lang.ICharSequence")
268276
GenCharSequenceEnumerator (sw, indent + "\t", opt);
269277
}

tools/generator/Property.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ public void Generate (GenBase gen, StreamWriter sw, string indent, CodeGeneratio
264264
opt.CodeGenerator.WriteMethodIdField (Getter, sw, indent, opt);
265265
if (Setter != null)
266266
opt.CodeGenerator.WriteMethodIdField (Setter, sw, indent, opt);
267-
string visibility = Getter.IsAbstract && Getter.RetVal.IsGeneric ? "protected" : (Setter ?? Getter).Visibility;
267+
string visibility = gen.IsInterface ? string.Empty : Getter.IsAbstract && Getter.RetVal.IsGeneric ? "protected" : (Setter ?? Getter).Visibility;
268268
// Unlike [Register], mcs does not allow applying [Obsolete] on property accessors, so we can apply them only under limited condition...
269269
if (Getter.Deprecated != null && (Setter == null || Setter.Deprecated != null))
270270
sw.WriteLine ("{0}[Obsolete (@\"{1}\")]", indent, Getter.Deprecated.Replace ("\"", "\"\"").Trim () + (Setter != null && Setter.Deprecated != Getter.Deprecated ? " " + Setter.Deprecated.Replace ("\"", "\"\"").Trim () : null));
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System;
2+
using System.IO;
3+
using NUnit.Framework;
4+
using Xamarin.Android.Binder;
5+
6+
namespace generatortests
7+
{
8+
[TestFixture]
9+
public class DefaultInterfaceMethods : BaseGeneratorTest
10+
{
11+
[Test]
12+
public void GeneratedOK ()
13+
{
14+
Options.SupportDefaultInterfaceMethods = true;
15+
RunAllTargets (
16+
outputRelativePath: "DefaultInterfaceMethods",
17+
apiDescriptionFile: "expected.ji/DefaultInterfaceMethods/DefaultInterfaceMethods.xml",
18+
expectedRelativePath: "DefaultInterfaceMethods",
19+
additionalSupportPaths: null);
20+
}
21+
22+
void RunAllTargets (string outputRelativePath, string apiDescriptionFile, string expectedRelativePath, string [] additionalSupportPaths)
23+
{
24+
Run (CodeGenerationTarget.JavaInterop1, Path.Combine ("out.ji", outputRelativePath), apiDescriptionFile, Path.Combine ("expected.ji", expectedRelativePath), additionalSupportPaths);
25+
}
26+
}
27+
}
28+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<api>
3+
<package name="java.lang">
4+
<class abstract="false" deprecated="not deprecated" final="false" name="Object" static="false" visibility="public">
5+
</class>
6+
</package>
7+
<package name="xamarin.test">
8+
<interface abstract="true" deprecated="not deprecated" final="false" name="TheInterface" static="false" visibility="public">
9+
<method abstract="false" deprecated="not deprecated" final="false" name="foo" native="false" return="int" static="false" synchronized="false" visibility="public">
10+
</method>
11+
<method abstract="false" deprecated="not deprecated" final="false" name="getBar" native="false" return="int" static="false" synchronized="false" visibility="public">
12+
</method>
13+
</interface>
14+
</package>
15+
</api>
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Android.Runtime;
4+
5+
namespace Xamarin.Test {
6+
7+
// Metadata.xml XPath interface reference: path="/api/package[@name='xamarin.test']/interface[@name='TheInterface']"
8+
[Register ("xamarin/test/TheInterface", "", "Xamarin.Test.ITheInterfaceInvoker")]
9+
public partial interface ITheInterface : IJavaObject {
10+
11+
static Delegate cb_getBar;
12+
#pragma warning disable 0169
13+
static Delegate GetGetBarHandler ()
14+
{
15+
if (cb_getBar == null)
16+
cb_getBar = JNINativeWrapper.CreateDelegate ((Func<IntPtr, IntPtr, int>) n_GetBar);
17+
return cb_getBar;
18+
}
19+
20+
static int n_GetBar (IntPtr jnienv, IntPtr native__this)
21+
{
22+
global::Xamarin.Test.TheInterface __this = global::Java.Lang.Object.GetObject<global::Xamarin.Test.TheInterface> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
23+
return __this.Bar;
24+
}
25+
#pragma warning restore 0169
26+
27+
public virtual unsafe int Bar {
28+
// Metadata.xml XPath method reference: path="/api/package[@name='xamarin.test']/class[@name='TheInterface']/method[@name='getBar' and count(parameter)=0]"
29+
[Register ("getBar", "()I", "GetGetBarHandler")]
30+
get {
31+
const string __id = "getBar.()I";
32+
try {
33+
var __rm = _members.InstanceMethods.InvokeVirtualInt32Method (__id, this, null);
34+
return __rm;
35+
} finally {
36+
}
37+
}
38+
}
39+
40+
static Delegate cb_foo;
41+
#pragma warning disable 0169
42+
static Delegate GetFooHandler ()
43+
{
44+
if (cb_foo == null)
45+
cb_foo = JNINativeWrapper.CreateDelegate ((Func<IntPtr, IntPtr, int>) n_Foo);
46+
return cb_foo;
47+
}
48+
49+
static int n_Foo (IntPtr jnienv, IntPtr native__this)
50+
{
51+
global::Xamarin.Test.TheInterface __this = global::Java.Lang.Object.GetObject<global::Xamarin.Test.TheInterface> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
52+
return __this.Foo ();
53+
}
54+
#pragma warning restore 0169
55+
56+
// Metadata.xml XPath method reference: path="/api/package[@name='xamarin.test']/class[@name='TheInterface']/method[@name='foo' and count(parameter)=0]"
57+
[Register ("foo", "()I", "GetFooHandler")]
58+
public virtual unsafe int Foo ()
59+
{
60+
const string __id = "foo.()I";
61+
try {
62+
var __rm = _members.InstanceMethods.InvokeVirtualInt32Method (__id, this, null);
63+
return __rm;
64+
} finally {
65+
}
66+
}
67+
}
68+
69+
[global::Android.Runtime.Register ("xamarin/test/TheInterface", DoNotGenerateAcw=true)]
70+
internal class ITheInterfaceInvoker : global::Java.Lang.Object, ITheInterface {
71+
72+
static IntPtr java_class_ref = JNIEnv.FindClass ("xamarin/test/TheInterface");
73+
74+
protected override IntPtr ThresholdClass {
75+
get { return class_ref; }
76+
}
77+
78+
protected override global::System.Type ThresholdType {
79+
get { return typeof (ITheInterfaceInvoker); }
80+
}
81+
82+
IntPtr class_ref;
83+
84+
public static ITheInterface GetObject (IntPtr handle, JniHandleOwnership transfer)
85+
{
86+
return global::Java.Lang.Object.GetObject<ITheInterface> (handle, transfer);
87+
}
88+
89+
static IntPtr Validate (IntPtr handle)
90+
{
91+
if (!JNIEnv.IsInstanceOf (handle, java_class_ref))
92+
throw new InvalidCastException (string.Format ("Unable to convert instance of type '{0}' to type '{1}'.",
93+
JNIEnv.GetClassNameFromInstance (handle), "xamarin.test.TheInterface"));
94+
return handle;
95+
}
96+
97+
protected override void Dispose (bool disposing)
98+
{
99+
if (this.class_ref != IntPtr.Zero)
100+
JNIEnv.DeleteGlobalRef (this.class_ref);
101+
this.class_ref = IntPtr.Zero;
102+
base.Dispose (disposing);
103+
}
104+
105+
public ITheInterfaceInvoker (IntPtr handle, JniHandleOwnership transfer) : base (Validate (handle), transfer)
106+
{
107+
IntPtr local_ref = JNIEnv.GetObjectClass (((global::Java.Lang.Object) this).Handle);
108+
this.class_ref = JNIEnv.NewGlobalRef (local_ref);
109+
JNIEnv.DeleteLocalRef (local_ref);
110+
}
111+
}
112+
113+
}

0 commit comments

Comments
 (0)