Skip to content

Commit d6d86b2

Browse files
jpobstjonpryor
authored andcommitted
[generator] Ensure DIM from assembly refs are correctly marked (#770)
Context: dotnet/android-libraries#235 Consider the following Java API: // Java public interface MyInterface { default void doSomething() {} } public class MyAbstractClass implements MyInterface { } which is then bound in C# as: // C# [Register (…)] public interface IMyInterface { [Register(…)] public void DoSomething() => …; // default interface method } [Register (…)] public abstract class MyAbstractClass : Java.Lang.Object, IMyInterface { // Doesn't override IMyInterface.DoSomething() } `generator` needs to ensure that `Method.IsInterfaceDefaultMethod()` gets set for `IMyInterface.DoSomething()` so that the `BoundClass.AddInterfaceAbstractMembers()` logic knows it does not need to create an `abstract` method `MyAbstractClass.DoSomething()`. This works correctly if the interface is in the assembly we are binding, because it uses `XmlApiImporter`. That is, `generator` output is valid if `MyInterface` and `MyAbstractClass` are in the same `.jar`. However, if the interface is in a reference assembly -- `MyInterface` and `MyAbstractClass` are in different assemblies -- then `generator` uses `CecilApiImporter`, and our Cecil importer only marks Java Interface Default Methods if the method has a `[JavaInterfaceDefaultMethodAttribute]` custom attribute, which we don't appear to ever emit. Consequently, when `MyInterface` and `MyAbstractClass` are in separate libraries, then `MyAbstractClass` is bound as: // C# [Register (…)] public abstract class MyAbstractClass : Java.Lang.Object, IMyInterface { public abstract void DoSomething(); } which causes most bindings that inherit `MyAbstractClass` to fail, as they typically don't override the interface default method: // C# [Register (…)] public class MyClass : MyAbstractClass { } // error CS0534: 'MyClass' does not implemented inherited abstract member 'MyAbstractClass.DoSomething()' Update `generator` and `CecilApiImporter` to properly detect C#8 default interface methods without requiring the presence of the (unused!) `[JavaInterfaceDefaultMethodAttribute]` custom attribute, so that classes don't re-declare C#8 interface default methods as "new" abstract methods. This fixes the generated `MyAbstractClass` declaration to be: // C* [Register (…)] public class MyAbstractClass : Java.Lang.Object, IMyInterface { // No `DoSomething()` declaration }
1 parent 20edccb commit d6d86b2

File tree

1 file changed

+12
-1
lines changed

1 file changed

+12
-1
lines changed

tools/generator/Java.Interop.Tools.Generator.Importers/CecilApiImporter.cs

+12-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ public static Method CreateMethod (GenBase declaringType, MethodDefinition m)
191191
IsAbstract = m.IsAbstract,
192192
IsAcw = reg_attr != null,
193193
IsFinal = m.IsFinal,
194-
IsInterfaceDefaultMethod = GetJavaDefaultInterfaceMethodAttribute (m.CustomAttributes) != null,
194+
IsInterfaceDefaultMethod = IsDefaultInterfaceMethod (declaringType, m),
195195
IsReturnEnumified = GetGeneratedEnumAttribute (m.MethodReturnType.CustomAttributes) != null,
196196
IsStatic = m.IsStatic,
197197
IsVirtual = m.IsVirtual,
@@ -248,5 +248,16 @@ static string GetObsoleteComment (CustomAttribute attribute) =>
248248

249249
static CustomAttribute GetRegisterAttribute (Collection<CustomAttribute> attributes) =>
250250
attributes.FirstOrDefault (a => a.AttributeType.FullNameCorrected () == "Android.Runtime.RegisterAttribute");
251+
252+
static bool IsDefaultInterfaceMethod (GenBase declaringType, MethodDefinition method)
253+
{
254+
if (!(declaringType is InterfaceGen))
255+
return false;
256+
257+
if (GetJavaDefaultInterfaceMethodAttribute (method.CustomAttributes) != null)
258+
return true;
259+
260+
return !method.IsAbstract && !method.IsStatic;
261+
}
251262
}
252263
}

0 commit comments

Comments
 (0)