Skip to content
This repository was archived by the owner on Jul 6, 2026. It is now read-only.

Commit 22d5687

Browse files
authored
[generator] Add @managedOverride values none and reabstract. (#1000)
Fixes: #981 Context: 5a0e37e Add support for `//class/method[@managedOverride = 'none']` and `//interface/method[@managedOverride = 'reabstract']`. Setting `@managedOverride` to `none` ensures that the member is not marked with `virtual` or `override`. This is useful for `sealed` classes, to avoid a [CS0549 error][0]. Previously, given the Java: // Java public final class MyClass { public void doThing() {/* … */ } } The `class-parse` XML would specify that `MyClass.doThing()` was "virtual" -- `@abstract` is false, `@final` is false: <class name="MyClass …> <method abstract="false" final="false" name="doThing" return="void" … /> </class> This would result in the C# binding: // C# public sealed partial class MyClass { public virtual void DoThing() => … } which would error out with a CS0549: error CS0549: 'MyClass.DoThing()' is a new virtual member in sealed type 'MyClass' This can now be resolved by setting `@managedOverride` to `none`: <attr path="//class[@name='MyClass']/method[@name='doThing']" name="managedOverride">none</attr> which will result in the compilable binding: // C# public sealed partial class MyClass { public void DoThing() => … } Setting `@managedOverride` to `reabstract` is part of support for re-abstracting interface members; see also a65d6fb. Currently in `src/Java.Base`, we have Java: // Java public interface AnnotatedType { default AnnotatedType getAnnotatedOwnerType() {…} } public interface AnnotatedArrayType implements AnnotatedType { AnnotatedType getAnnotatedOwnerType(); // re-abstract interface default method } which results in the C# binding: // C# public partial interface IAnnotatedType { virtual IAnnotatedType? AnnotatedOwnerType { get => … } } public partial interface IAnnotatedArrayType : IAnnotatedType { IAnnotatedType? AnnotatedOwnerType { get; } // CS0108 } which results in a [warning CS0108][1]: warning CS0108: 'IAnnotatedArrayType.AnnotatedOwnerType' hides inherited member 'IAnnotatedType.AnnotatedOwnerType'. Use the new keyword if hiding was intended. Fixing this requires two steps. First, we can now set `//interface/method/@managedOverride` to `reabstract`: <attr path="//interface[@name='AnnotatedArrayType']/method[@name='getAnnotatedOwnerType']" name="managedOverride">reabstract</attr> What we *also* need to do is make the member *explicitly qualified*. This can be "forced" for *properties* by setting `@propertyName`: <attr path="//interface[@name='AnnotatedArrayType']/method[@name='getAnnotatedOwnerType']" name="propertyName">IAnnotatedType.AnnotatedOwnerType</attr> `@managedOverride` and `@propertyName` work together to emit: public partial interface IAnnotatedArrayType : IAnnotatedType { abstract IAnnotatedType? IAnnotatedType.AnnotatedOwnerType {get;} } The problem is that this combination probably breaks Android output, and it can't be used for *method* overrides, e.g. having `java.io.Closeable.close()` re-abstract `java.lang.AutoCloseable.close()`. TODO: complete the "interface reabstract" case, possibly via `//interface/method[@explicitInterface='ManagedInterfaceName']`: <attr path="//interface[@name='AnnotatedArrayType']/method[@name='getAnnotatedOwnerType']" name="explicitInterface">IAnnotatedType</attr> [0]: https://docs.microsoft.com/en-us/dotnet/csharp/misc/cs0549 [1]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/cs0108
1 parent 7f1d2d7 commit 22d5687

5 files changed

Lines changed: 114 additions & 0 deletions

File tree

‎tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs‎

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,56 @@ public void ManagedOverrideMethod_Override ()
147147
Assert.True(writer.ToString().Contains("public override unsafe int DoStuff ()"),$"was: `{writer.ToString()}`");
148148
}
149149

150+
[Test]
151+
publicvoidManagedOverrideMethod_None()
152+
{
153+
varxml=@"<api>
154+
<package name='java.lang' jni-name='java/lang'>
155+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
156+
</package>
157+
<package name='com.xamarin.android' jni-name='com/xamarin/android'>
158+
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='MyClass' static='false' visibility='public' jni-signature='Lcom/xamarin/android/MyClass;'>
159+
<method abstract='false' deprecated='not deprecated' final='false' name='DoStuff' jni-signature='()I' bridge='false' native='false' return='int' jni-return='I' static='false' synchronized='false' synthetic='false' visibility='public' managedOverride='none'></method>
160+
</class>
161+
</package>
162+
</api>";
163+
164+
vargens=ParseApiDefinition(xml);
165+
varklass=gens.Single(g =>g.Name=="MyClass");
166+
167+
generator.Context.ContextTypes.Push(klass);
168+
generator.WriteType(klass,string.Empty,newGenerationInfo("","","MyAssembly"));
169+
generator.Context.ContextTypes.Pop();
170+
171+
// This would contain 'virtual' if the 'managedOverride' was not working
172+
Assert.True(writer.ToString().Contains("public unsafe int DoStuff ()"),$"was: `{writer}`");
173+
}
174+
175+
[Test]
176+
publicvoidManagedOverrideInterfaceMethod_Reabstract()
177+
{
178+
varxml=@"<api>
179+
<package name='java.lang' jni-name='java/lang'>
180+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
181+
</package>
182+
<package name='com.xamarin.android' jni-name='com/xamarin/android'>
183+
<interface abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='MyInterface' static='false' visibility='public' jni-signature='Lcom/xamarin/android/MyInterface;'>
184+
<method abstract='true' deprecated='not deprecated' final='false' name='DoStuff' jni-signature='()I' bridge='false' native='false' return='int' jni-return='I' static='false' synchronized='false' synthetic='false' visibility='public' managedOverride='reabstract'></method>
185+
</interface>
186+
</package>
187+
</api>";
188+
189+
vargens=ParseApiDefinition(xml);
190+
variface=gens.Single(g =>g.Name=="IMyInterface");
191+
192+
generator.Context.ContextTypes.Push(iface);
193+
generator.WriteType(iface,string.Empty,newGenerationInfo("","","MyAssembly"));
194+
generator.Context.ContextTypes.Pop();
195+
196+
// This would not contain 'abstract' if the 'managedOverride' was not working
197+
Assert.True(writer.ToString().Contains("abstract int DoStuff ()"),$"was: `{writer}`");
198+
}
199+
150200
[Test]
151201
publicvoidManagedOverrideProperty_Virtual()
152202
{
@@ -195,6 +245,56 @@ public void ManagedOverrideProperty_Override ()
195245
Assert.True(writer.ToString().Contains("public override unsafe int Name {"),$"was: `{writer.ToString()}`");
196246
}
197247

248+
[Test]
249+
publicvoidManagedOverrideProperty_None()
250+
{
251+
varxml=@"<api>
252+
<package name='java.lang' jni-name='java/lang'>
253+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
254+
</package>
255+
<package name='com.xamarin.android' jni-name='com/xamarin/android'>
256+
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='MyClass' static='false' visibility='public' jni-signature='Lcom/xamarin/android/MyClass;'>
257+
<method abstract='false' deprecated='not deprecated' final='false' name='getName' jni-signature='()I' bridge='false' native='false' return='int' jni-return='I' static='false' synchronized='false' synthetic='false' visibility='public' managedOverride='none'></method>
258+
</class>
259+
</package>
260+
</api>";
261+
262+
vargens=ParseApiDefinition(xml);
263+
varklass=gens.Single(g =>g.Name=="MyClass");
264+
265+
generator.Context.ContextTypes.Push(klass);
266+
generator.WriteType(klass,string.Empty,newGenerationInfo("","","MyAssembly"));
267+
generator.Context.ContextTypes.Pop();
268+
269+
// This would contain 'virtual' if the 'managedOverride' was not working
270+
Assert.True(writer.ToString().Contains("public unsafe int Name {"),$"was: `{writer}`");
271+
}
272+
273+
[Test]
274+
publicvoidManagedOverrideInterfaceProperty_Reabstract()
275+
{
276+
varxml=@"<api>
277+
<package name='java.lang' jni-name='java/lang'>
278+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
279+
</package>
280+
<package name='com.xamarin.android' jni-name='com/xamarin/android'>
281+
<interface abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='MyInterface' static='false' visibility='public' jni-signature='Lcom/xamarin/android/MyInterface;'>
282+
<method abstract='true' deprecated='not deprecated' final='false' name='getName' jni-signature='()I' bridge='false' native='false' return='int' jni-return='I' static='false' synchronized='false' synthetic='false' visibility='public' managedOverride='reabstract'></method>
283+
</interface>
284+
</package>
285+
</api>";
286+
287+
vargens=ParseApiDefinition(xml);
288+
variface=gens.Single(g =>g.Name=="IMyInterface");
289+
290+
generator.Context.ContextTypes.Push(iface);
291+
generator.WriteType(iface,string.Empty,newGenerationInfo("","","MyAssembly"));
292+
generator.Context.ContextTypes.Pop();
293+
294+
// This would not contain 'abstract' if the 'managedOverride' was not working
295+
Assert.True(writer.ToString().Contains("abstract int Name {"),$"was: `{writer}`");
296+
}
297+
198298
[Test]
199299
publicvoidWriteDuplicateInterfaceEventArgs()
200300
{

‎tools/generator/SourceWriters/BoundInterfaceMethodDeclaration.cs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ public BoundInterfaceMethodDeclaration (Method method, string adapter, CodeGener
2525
ReturnType=newTypeReferenceWriter(opt.GetTypeReferenceName(method.RetVal));
2626
IsDeclaration=true;
2727

28+
// Allow user to force adding the 'abstract' keyword for "reabstraction"
29+
if(method.ManagedOverride?.ToLowerInvariant()=="reabstract")
30+
IsAbstract=true;
31+
2832
if(method.DeclaringType.IsGeneratable)
2933
Comments.Add($"// Metadata.xml XPath method reference: path=\"{method.GetMetadataXPathReference(method.DeclaringType)}\"");
3034
if(method.Deprecated!=null)

‎tools/generator/SourceWriters/BoundInterfacePropertyDeclaration.cs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ public BoundInterfacePropertyDeclaration (GenBase gen, Property property, string
2020
PropertyType=newTypeReferenceWriter(opt.GetTypeReferenceName(property));
2121
IsAutoProperty=true;
2222

23+
// Allow user to force adding the 'abstract' keyword for "reabstraction"
24+
if((property.Getter??property.Setter).ManagedOverride?.ToLowerInvariant()=="reabstract")
25+
IsAbstract=true;
26+
2327
if(property.Getter!=null){
2428
HasGet=true;
2529

‎tools/generator/SourceWriters/BoundMethod.cs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ public BoundMethod (GenBase type, Method method, CodeGenerationOptions opt, bool
5858
}elseif(method.ManagedOverride?.ToLowerInvariant()=="override"){
5959
IsVirtual=false;
6060
IsOverride=true;
61+
}elseif(method.ManagedOverride?.ToLowerInvariant()=="none"){
62+
IsVirtual=false;
63+
IsOverride=false;
6164
}
6265

6366
ReturnType=newTypeReferenceWriter(opt.GetTypeReferenceName(method.RetVal));

‎tools/generator/SourceWriters/BoundProperty.cs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ public BoundProperty (GenBase gen, Property property, CodeGenerationOptions opt,
6666
}elseif(!forceOverride&&(property.Getter??property.Setter).ManagedOverride?.ToLowerInvariant()=="override"){
6767
IsVirtual=false;
6868
IsOverride=true;
69+
}elseif(!forceOverride&&(property.Getter??property.Setter).ManagedOverride?.ToLowerInvariant()=="none"){
70+
IsVirtual=false;
71+
IsOverride=false;
6972
}
7073

7174
// Unlike [Register], [Obsolete] cannot be put on property accessors, so we can apply them only under limited condition...

0 commit comments

Comments
 (0)