Skip to content

Commit 4ec5d4e

Browse files
jonathanpeppersjonpryor
authored andcommitted
[generator] better support for "package-private" classes (#223)
Fixes: #168 Java allows public classes to inherit from "package-private" classes, in which case the `public` & `protected` members of the "package-private" class are visible within the public class: // Java /* package */ abstract class SpannableStringInternal { public void getChars(int start, int end, char[] dest, int off); // ... } public class SpannableString extends SpannableStringInternal { // ... } The problem is that `generator` didn't properly support this construct, and *skips* binding of "package-private" types, resulting in generated C# code such as: // C# public partial class SpannableString : SpannableStringInternal { // CS0246: The type or namespace name `SpannableStringInternal` could not be found } This could be worked around via Metadata by making the "package-private" class `public`, but this means that if (when?) the "package-private" class is *removed*, we have an ABI break. Support this construct by updating `generator` to "copy" the members from the "package-private" class into the declaring class: // C# public partial class SpannableString : Java.Lang.Object { public void GetChars(int start, int end, char[] dest, int off); // ... } This allows the generated code to compile without metadata fixup. Specifics in implementing this: - Add a `GenBase.FixupAccessModifiers()` method, later this may need to be further extended for methods - Call `FixupAccessModifiers()` in the "validation" step - Added a setter for `BaseType` so it can be modified - In `FixupAccessModifiers()`, lookup the base class of the current type and check if it is non-`public`. - Skip the package-private types, and replace them with that class's base type - Look for each method on the base type, and copy it to the public type if it does not exist - Added tests for this scenario
1 parent 8ac72be commit 4ec5d4e

16 files changed

+778
-1
lines changed

tools/generator/ClassGen.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public ManagedClassGen (TypeDefinition t)
5959

6060
public override string BaseType {
6161
get { return nominal_base_type != null ? nominal_base_type.FullNameCorrected () : null; }
62+
set { throw new NotSupportedException (); }
6263
}
6364

6465
public override bool IsAbstract {
@@ -121,6 +122,7 @@ public override bool IsFinal {
121122

122123
public override string BaseType {
123124
get { return base_type; }
125+
set { base_type = value; }
124126
}
125127
}
126128

@@ -164,7 +166,7 @@ public IList<Ctor> Ctors {
164166
get { return ctors; }
165167
}
166168

167-
public abstract string BaseType { get; }
169+
public abstract string BaseType { get; set; }
168170

169171
public bool ContainsCtor (string jni_sig)
170172
{
@@ -236,6 +238,26 @@ protected override bool OnValidate (CodeGenerationOptions opt, GenericParameterD
236238

237239
return true;
238240
}
241+
242+
public override void FixupAccessModifiers ()
243+
{
244+
while (!IsAnnotation && !string.IsNullOrEmpty (BaseType)) {
245+
var baseClass = SymbolTable.Lookup (BaseType) as ClassGen;
246+
if (baseClass != null && RawVisibility == "public" && baseClass.RawVisibility != "public") {
247+
//Skip the BaseType and copy over any "missing" methods
248+
foreach (var baseMethod in baseClass.Methods) {
249+
var method = Methods.FirstOrDefault (m => m.Name == baseMethod.Name && m.Parameters.JavaSignature == baseMethod.Parameters.JavaSignature);
250+
if (method == null)
251+
Methods.Add (baseMethod);
252+
}
253+
BaseType = baseClass.BaseType;
254+
} else {
255+
break;
256+
}
257+
}
258+
259+
base.FixupAccessModifiers ();
260+
}
239261

240262
public override void FixupExplicitImplementation ()
241263
{

tools/generator/CodeGenerator.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,8 @@ static void Validate (List<GenBase> gens, CodeGenerationOptions opt)
400400
removed.Clear ();
401401
foreach (GenBase gen in gens)
402402
gen.ResetValidation ();
403+
foreach (GenBase gen in gens)
404+
gen.FixupAccessModifiers ();
403405
foreach (GenBase gen in gens)
404406
if ((opt.IgnoreNonPublicType &&
405407
(gen.RawVisibility != "public" && gen.RawVisibility != "internal"))

tools/generator/GenBase.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,12 @@ public void StripNonBindables ()
727727
n.StripNonBindables ();
728728
}
729729

730+
public virtual void FixupAccessModifiers ()
731+
{
732+
foreach (var nt in NestedTypes)
733+
nt.FixupAccessModifiers ();
734+
}
735+
730736
public void FillProperties ()
731737
{
732738
if (property_filled)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
using NUnit.Framework;
3+
4+
namespace generatortests
5+
{
6+
[TestFixture]
7+
public class AccessModifiers : BaseGeneratorTest
8+
{
9+
[Test]
10+
public void GeneratedOK ()
11+
{
12+
RunAllTargets (
13+
outputRelativePath: "AccessModifiers",
14+
apiDescriptionFile: "expected/AccessModifiers/AccessModifiers.xml",
15+
expectedRelativePath: "AccessModifiers",
16+
additionalSupportPaths: null);
17+
}
18+
}
19+
}
20+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup>
4+
<DefineConstants>$(DefineConstants);ANDROID_1;ANDROID_2;ANDROID_3;ANDROID_4</DefineConstants>
5+
</PropertyGroup>
6+
<!-- Classes -->
7+
<ItemGroup>
8+
<Compile Include="$(MSBuildThisFileDirectory)\Java.Interop.__TypeRegistrations.cs" />
9+
<Compile Include="$(MSBuildThisFileDirectory)\Java.Lang.Object.cs" />
10+
<Compile Include="$(MSBuildThisFileDirectory)\Xamarin.Test.BasePublicClass.cs" />
11+
<Compile Include="$(MSBuildThisFileDirectory)\Xamarin.Test.ExtendPublicClass.cs" />
12+
<Compile Include="$(MSBuildThisFileDirectory)\Xamarin.Test.PublicClass.cs" />
13+
<Compile Include="$(MSBuildThisFileDirectory)\Xamarin.Test.PublicFinalClass.cs" />
14+
<Compile Include="$(MSBuildThisFileDirectory)\__NamespaceMapping__.cs" />
15+
</ItemGroup>
16+
<!-- Enums -->
17+
<ItemGroup />
18+
</Project>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Android.Runtime;
4+
using Java.Interop;
5+
6+
namespace Xamarin.Test {
7+
8+
// Metadata.xml XPath class reference: path="/api/package[@name='xamarin.test']/class[@name='BasePublicClass']"
9+
[global::Android.Runtime.Register ("xamarin/test/BasePublicClass", DoNotGenerateAcw=true)]
10+
public partial class BasePublicClass : global::Java.Lang.Object {
11+
12+
internal new static readonly JniPeerMembers _members = new JniPeerMembers ("xamarin/test/BasePublicClass", typeof (BasePublicClass));
13+
internal static new IntPtr class_ref {
14+
get {
15+
return _members.JniPeerType.PeerReference.Handle;
16+
}
17+
}
18+
19+
public override global::Java.Interop.JniPeerMembers JniPeerMembers {
20+
get { return _members; }
21+
}
22+
23+
protected override IntPtr ThresholdClass {
24+
get { return _members.JniPeerType.PeerReference.Handle; }
25+
}
26+
27+
protected override global::System.Type ThresholdType {
28+
get { return _members.ManagedPeerType; }
29+
}
30+
31+
protected BasePublicClass (IntPtr javaReference, JniHandleOwnership transfer) : base (javaReference, transfer) {}
32+
33+
static Delegate cb_baseMethod;
34+
#pragma warning disable 0169
35+
static Delegate GetBaseMethodHandler ()
36+
{
37+
if (cb_baseMethod == null)
38+
cb_baseMethod = JNINativeWrapper.CreateDelegate ((Action<IntPtr, IntPtr>) n_BaseMethod);
39+
return cb_baseMethod;
40+
}
41+
42+
static void n_BaseMethod (IntPtr jnienv, IntPtr native__this)
43+
{
44+
global::Xamarin.Test.BasePublicClass __this = global::Java.Lang.Object.GetObject<global::Xamarin.Test.BasePublicClass> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
45+
__this.BaseMethod ();
46+
}
47+
#pragma warning restore 0169
48+
49+
// Metadata.xml XPath method reference: path="/api/package[@name='xamarin.test']/class[@name='BasePublicClass']/method[@name='baseMethod' and count(parameter)=0]"
50+
[Register ("baseMethod", "()V", "GetBaseMethodHandler")]
51+
public virtual unsafe void BaseMethod ()
52+
{
53+
const string __id = "baseMethod.()V";
54+
try {
55+
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, null);
56+
} finally {
57+
}
58+
}
59+
60+
}
61+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Android.Runtime;
4+
using Java.Interop;
5+
6+
namespace Xamarin.Test {
7+
8+
// Metadata.xml XPath class reference: path="/api/package[@name='xamarin.test']/class[@name='ExtendPublicClass']"
9+
[global::Android.Runtime.Register ("xamarin/test/ExtendPublicClass", DoNotGenerateAcw=true)]
10+
public partial class ExtendPublicClass : global::Java.Lang.Object {
11+
12+
internal new static readonly JniPeerMembers _members = new JniPeerMembers ("xamarin/test/ExtendPublicClass", typeof (ExtendPublicClass));
13+
internal static new IntPtr class_ref {
14+
get {
15+
return _members.JniPeerType.PeerReference.Handle;
16+
}
17+
}
18+
19+
public override global::Java.Interop.JniPeerMembers JniPeerMembers {
20+
get { return _members; }
21+
}
22+
23+
protected override IntPtr ThresholdClass {
24+
get { return _members.JniPeerType.PeerReference.Handle; }
25+
}
26+
27+
protected override global::System.Type ThresholdType {
28+
get { return _members.ManagedPeerType; }
29+
}
30+
31+
protected ExtendPublicClass (IntPtr javaReference, JniHandleOwnership transfer) : base (javaReference, transfer) {}
32+
33+
// Metadata.xml XPath constructor reference: path="/api/package[@name='xamarin.test']/class[@name='ExtendPublicClass']/constructor[@name='ExtendPublicClass' and count(parameter)=0]"
34+
[Register (".ctor", "()V", "")]
35+
public unsafe ExtendPublicClass ()
36+
: base (IntPtr.Zero, JniHandleOwnership.DoNotTransfer)
37+
{
38+
const string __id = "()V";
39+
40+
if (((global::Java.Lang.Object) this).Handle != IntPtr.Zero)
41+
return;
42+
43+
try {
44+
var __r = _members.InstanceMethods.StartCreateInstance (__id, ((object) this).GetType (), null);
45+
SetHandle (__r.Handle, JniHandleOwnership.TransferLocalRef);
46+
_members.InstanceMethods.FinishCreateInstance (__id, this, null);
47+
} finally {
48+
}
49+
}
50+
51+
static Delegate cb_foo;
52+
#pragma warning disable 0169
53+
static Delegate GetFooHandler ()
54+
{
55+
if (cb_foo == null)
56+
cb_foo = JNINativeWrapper.CreateDelegate ((Action<IntPtr, IntPtr>) n_Foo);
57+
return cb_foo;
58+
}
59+
60+
static void n_Foo (IntPtr jnienv, IntPtr native__this)
61+
{
62+
global::Xamarin.Test.ExtendPublicClass __this = global::Java.Lang.Object.GetObject<global::Xamarin.Test.ExtendPublicClass> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
63+
__this.Foo ();
64+
}
65+
#pragma warning restore 0169
66+
67+
// Metadata.xml XPath method reference: path="/api/package[@name='xamarin.test']/class[@name='ExtendPublicClass']/method[@name='foo' and count(parameter)=0]"
68+
[Register ("foo", "()V", "GetFooHandler")]
69+
public virtual unsafe void Foo ()
70+
{
71+
const string __id = "foo.()V";
72+
try {
73+
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, null);
74+
} finally {
75+
}
76+
}
77+
78+
}
79+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Android.Runtime;
4+
using Java.Interop;
5+
6+
namespace Xamarin.Test {
7+
8+
// Metadata.xml XPath class reference: path="/api/package[@name='xamarin.test']/class[@name='PublicClass']"
9+
[global::Android.Runtime.Register ("xamarin/test/PublicClass", DoNotGenerateAcw=true)]
10+
public partial class PublicClass : global::Java.Lang.Object {
11+
12+
internal new static readonly JniPeerMembers _members = new JniPeerMembers ("xamarin/test/PublicClass", typeof (PublicClass));
13+
internal static new IntPtr class_ref {
14+
get {
15+
return _members.JniPeerType.PeerReference.Handle;
16+
}
17+
}
18+
19+
public override global::Java.Interop.JniPeerMembers JniPeerMembers {
20+
get { return _members; }
21+
}
22+
23+
protected override IntPtr ThresholdClass {
24+
get { return _members.JniPeerType.PeerReference.Handle; }
25+
}
26+
27+
protected override global::System.Type ThresholdType {
28+
get { return _members.ManagedPeerType; }
29+
}
30+
31+
protected PublicClass (IntPtr javaReference, JniHandleOwnership transfer) : base (javaReference, transfer) {}
32+
33+
// Metadata.xml XPath constructor reference: path="/api/package[@name='xamarin.test']/class[@name='PublicClass']/constructor[@name='PublicClass' and count(parameter)=0]"
34+
[Register (".ctor", "()V", "")]
35+
public unsafe PublicClass ()
36+
: base (IntPtr.Zero, JniHandleOwnership.DoNotTransfer)
37+
{
38+
const string __id = "()V";
39+
40+
if (((global::Java.Lang.Object) this).Handle != IntPtr.Zero)
41+
return;
42+
43+
try {
44+
var __r = _members.InstanceMethods.StartCreateInstance (__id, ((object) this).GetType (), null);
45+
SetHandle (__r.Handle, JniHandleOwnership.TransferLocalRef);
46+
_members.InstanceMethods.FinishCreateInstance (__id, this, null);
47+
} finally {
48+
}
49+
}
50+
51+
static Delegate cb_foo;
52+
#pragma warning disable 0169
53+
static Delegate GetFooHandler ()
54+
{
55+
if (cb_foo == null)
56+
cb_foo = JNINativeWrapper.CreateDelegate ((Action<IntPtr, IntPtr>) n_Foo);
57+
return cb_foo;
58+
}
59+
60+
static void n_Foo (IntPtr jnienv, IntPtr native__this)
61+
{
62+
global::Xamarin.Test.PublicClass __this = global::Java.Lang.Object.GetObject<global::Xamarin.Test.PublicClass> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
63+
__this.Foo ();
64+
}
65+
#pragma warning restore 0169
66+
67+
// Metadata.xml XPath method reference: path="/api/package[@name='xamarin.test']/class[@name='PublicClass']/method[@name='foo' and count(parameter)=0]"
68+
[Register ("foo", "()V", "GetFooHandler")]
69+
public virtual unsafe void Foo ()
70+
{
71+
const string __id = "foo.()V";
72+
try {
73+
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, null);
74+
} finally {
75+
}
76+
}
77+
78+
}
79+
}

0 commit comments

Comments
 (0)