Skip to content

Commit 5cf0473

Browse files
committed
[generator] Fix xamarin-android/src/Mono.Android build
Context: a65d6fb Context: dotnet/android#6939 dotnet/android#6939 attempted to bump to 05eddd9, which promptly broke the build of src/Mono.Android, e.g. `src/Mono.Android/obj/Debug/net6.0/android-32/mcw/Android.Widget.GridLayout.cs`: /* */ partial class GridLayout { /* */ partial class partial class LayoutParams { /* */ [Register ("rowSpec")] /* */ public Android.Widget.GridLayout.Spec? RowSpec { /* */ get { /* */ const string __id = "rowSpec.Landroid/widget/GridLayout$Spec;"; /* */ var __v = _members.InstanceFields.GetObjectValue (__id, this); /* */ return global::Java.Lang.Object.GetObject<Android.Widget.GridLayout.Spec> (__v.Handle, JniHandleOwnership.TransferLocalRef); /* */ } /* */ set { /* */ const string __id = "rowSpec.Landroid/widget/GridLayout$Spec;"; /* */ IntPtr native_value = global::Android.Runtime.JNIEnv.ToLocalJniHandle (value); /* */ try { /* L 239 */ _members.InstanceFields.SetValue (__id, this, new JniObjectReference (value)); /* */ } finally { /* L 242 */ global::Android.Runtime.JNIEnv.DeleteLocalRef (value); /* */ } /* */ } /* */ } /* */ } /* */ } due to compilation errors: src/Mono.Android/obj/Debug/net6.0/android-32/mcw/Android.Widget.GridLayout.cs(239,77): error CS1503: Argument 1: cannot convert from 'Android.Widget.GridLayout.Spec' to 'System.IntPtr' src/Mono.Android/obj/Debug/net6.0/android-32/mcw/Android.Widget.GridLayout.cs(242,54): error CS1503: Argument 1: cannot convert from 'Android.Widget.GridLayout.Spec' to 'System.IntPtr' This was caused by `BoundFieldAsProperty.cs` not appropriately setting `arg` to `native_arg`, so that the correct variable would be cleaned up in the `finally` block. There was another set of errors: src/Mono.Android/obj/Release/net6.0/android-32/mcw/Android.Widget.ArrayAdapter.cs(525,53): error CS1503: Argument 1: cannot convert from 'Java.Lang.ICharSequence[]' to 'string[]?' which was also caused by a65d6fb mis-refactoring `SourceWriterExtensions.cs`, and overlooking the entire existence of the `CharSequence.ArrayToStringArray()` method (oops). Update `generator` so that xamarin-android once again builds, and add unit tests to hit these particular code paths.
1 parent 05eddd9 commit 5cf0473

File tree

13 files changed

+366
-2
lines changed

13 files changed

+366
-2
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Metadata.xml XPath class reference: path="/api/package[@name='java.code']/class[@name='MyClass']"
2+
[global::Java.Interop.JniTypeSignature ("java/code/MyClass", GenerateJavaPeer=false)]
3+
public partial class MyClass {
4+
static readonly JniPeerMembers _members = new JniPeerMembers ("java/code/MyClass", typeof (MyClass));
5+
6+
protected MyClass (ref JniObjectReference reference, JniObjectReferenceOptions options) : base (ref reference, options)
7+
{
8+
}
9+
10+
// Metadata.xml XPath method reference: path="/api/package[@name='java.code']/class[@name='MyClass']/method[@name='Echo' and count(parameter)=1 and parameter[1][@type='java.lang.CharSequence[]']]"
11+
public static unsafe Java.Interop.JavaObjectArray<Java.Lang.ICharSequence>? Echo (Java.Lang.ICharSequence[]? messages)
12+
{
13+
const string __id = "Echo.([Ljava/lang/CharSequence;)[Ljava/lang/CharSequence;";
14+
var native_messages = global::Java.Interop.JniEnvironment.Arrays.CreateMarshalObjectArray<global::Java.Lang.ICharSequence> (messages);
15+
try {
16+
JniArgumentValue* __args = stackalloc JniArgumentValue [1];
17+
__args [0] = new JniArgumentValue (native_messages);
18+
var __rm = _members.StaticMethods.InvokeObjectMethod (__id, __args);
19+
return global::Java.Interop.JniEnvironment.Runtime.ValueManager.GetValue<global::Java.Interop.JavaObjectArray<Java.Lang.ICharSequence>>(ref __rm, JniObjectReferenceOptions.CopyAndDispose);
20+
} finally {
21+
if (native_messages != null) {
22+
native_messages.DisposeUnlessReferenced ();
23+
}
24+
global::System.GC.KeepAlive (messages);
25+
}
26+
}
27+
28+
public static Java.Interop.JavaObjectArray<string>? Echo (string[]? messages)
29+
{
30+
var jlca_messages = ICharSequenceExtensions.ToCharSequenceArray (messages);
31+
Java.Interop.JavaObjectArray<Java.Lang.ICharSequence>? __result = Echo (jlca_messages);
32+
var __rsval = __result;
33+
if (jlca_messages != null) foreach (var s in jlca_messages) s?.Dispose ();
34+
return __rsval;
35+
}
36+
37+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Metadata.xml XPath class reference: path="/api/package[@name='java.code']/class[@name='MyClass']"
2+
[global::Java.Interop.JniTypeSignature ("java/code/MyClass", GenerateJavaPeer=false)]
3+
public partial class MyClass {
4+
5+
// Metadata.xml XPath field reference: path="/api/package[@name='java.code']/class[@name='MyClass']/field[@name='field']"
6+
public java.code.Example? field {
7+
get {
8+
const string __id = "field.Ljava/code/Example;";
9+
10+
var __v = _members.InstanceFields.GetObjectValue (__id, this);
11+
return global::Java.Interop.JniEnvironment.Runtime.ValueManager.GetValue<java.code.Example? >(ref __v, JniObjectReferenceOptions.Copy);
12+
}
13+
set {
14+
const string __id = "field.Ljava/code/Example;";
15+
16+
try {
17+
_members.InstanceFields.SetValue (__id, this, value?.PeerReference ?? default);
18+
19+
} finally {
20+
GC.KeepAlive (value);
21+
}
22+
}
23+
}
24+
25+
static readonly JniPeerMembers _members = new JniPeerMembers ("java/code/MyClass", typeof (MyClass));
26+
27+
protected MyClass (ref JniObjectReference reference, JniObjectReferenceOptions options) : base (ref reference, options)
28+
{
29+
}
30+
31+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Metadata.xml XPath class reference: path="/api/package[@name='java.code']/class[@name='MyClass']"
2+
[global::Android.Runtime.Register ("java/code/MyClass", DoNotGenerateAcw=true)]
3+
public partial class MyClass {
4+
static readonly JniPeerMembers _members = new XAPeerMembers ("java/code/MyClass", typeof (MyClass));
5+
6+
internal static IntPtr class_ref {
7+
get { return _members.JniPeerType.PeerReference.Handle; }
8+
}
9+
10+
protected MyClass (IntPtr javaReference, JniHandleOwnership transfer) : base (javaReference, transfer)
11+
{
12+
}
13+
14+
// Metadata.xml XPath method reference: path="/api/package[@name='java.code']/class[@name='MyClass']/method[@name='Echo' and count(parameter)=1 and parameter[1][@type='java.lang.CharSequence[]']]"
15+
[Register ("Echo", "([Ljava/lang/CharSequence;)[Ljava/lang/CharSequence;", "")]
16+
public static unsafe Java.Lang.ICharSequence[]? EchoFormatted (Java.Lang.ICharSequence[]? messages)
17+
{
18+
const string __id = "Echo.([Ljava/lang/CharSequence;)[Ljava/lang/CharSequence;";
19+
IntPtr native_messages = JNIEnv.NewArray (messages);
20+
try {
21+
JniArgumentValue* __args = stackalloc JniArgumentValue [1];
22+
__args [0] = new JniArgumentValue (native_messages);
23+
var __rm = _members.StaticMethods.InvokeObjectMethod (__id, __args);
24+
return (Java.Lang.ICharSequence[]?) JNIEnv.GetArray (__rm.Handle, JniHandleOwnership.TransferLocalRef, typeof (Java.Lang.ICharSequence));
25+
} finally {
26+
if (messages != null) {
27+
JNIEnv.CopyArray (native_messages, messages);
28+
JNIEnv.DeleteLocalRef (native_messages);
29+
}
30+
global::System.GC.KeepAlive (messages);
31+
}
32+
}
33+
34+
public static string[]? Echo (string[]? messages)
35+
{
36+
var jlca_messages = CharSequence.ArrayFromStringArray (messages);
37+
Java.Lang.ICharSequence[]? __result = EchoFormatted (jlca_messages);
38+
var __rsval = CharSequence.ArrayToStringArray (__result);
39+
if (jlca_messages != null) foreach (var s in jlca_messages) s?.Dispose ();
40+
return __rsval;
41+
}
42+
43+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Metadata.xml XPath class reference: path="/api/package[@name='java.code']/class[@name='MyClass']"
2+
[global::Android.Runtime.Register ("java/code/MyClass", DoNotGenerateAcw=true)]
3+
public partial class MyClass {
4+
5+
// Metadata.xml XPath field reference: path="/api/package[@name='java.code']/class[@name='MyClass']/field[@name='field']"
6+
[Register ("field")]
7+
public java.code.Example? field {
8+
get {
9+
const string __id = "field.Ljava/code/Example;";
10+
11+
var __v = _members.InstanceFields.GetObjectValue (__id, this);
12+
return global::Java.Lang.Object.GetObject<java.code.Example> (__v.Handle, JniHandleOwnership.TransferLocalRef);
13+
}
14+
set {
15+
const string __id = "field.Ljava/code/Example;";
16+
17+
IntPtr native_value = global::Android.Runtime.JNIEnv.ToLocalJniHandle (value);
18+
try {
19+
_members.InstanceFields.SetValue (__id, this, new JniObjectReference (native_value));
20+
} finally {
21+
global::Android.Runtime.JNIEnv.DeleteLocalRef (native_value);
22+
}
23+
}
24+
}
25+
26+
static readonly JniPeerMembers _members = new XAPeerMembers ("java/code/MyClass", typeof (MyClass));
27+
28+
internal static IntPtr class_ref {
29+
get { return _members.JniPeerType.PeerReference.Handle; }
30+
}
31+
32+
protected MyClass (IntPtr javaReference, JniHandleOwnership transfer) : base (javaReference, transfer)
33+
{
34+
}
35+
36+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Metadata.xml XPath class reference: path="/api/package[@name='java.code']/class[@name='MyClass']"
2+
[global::Android.Runtime.Register ("java/code/MyClass", DoNotGenerateAcw=true)]
3+
public partial class MyClass {
4+
static readonly JniPeerMembers _members = new XAPeerMembers ("java/code/MyClass", typeof (MyClass));
5+
6+
internal static IntPtr class_ref {
7+
get { return _members.JniPeerType.PeerReference.Handle; }
8+
}
9+
10+
protected MyClass (IntPtr javaReference, JniHandleOwnership transfer) : base (javaReference, transfer)
11+
{
12+
}
13+
14+
// Metadata.xml XPath method reference: path="/api/package[@name='java.code']/class[@name='MyClass']/method[@name='Echo' and count(parameter)=1 and parameter[1][@type='java.lang.CharSequence[]']]"
15+
[Register ("Echo", "([Ljava/lang/CharSequence;)[Ljava/lang/CharSequence;", "")]
16+
public static unsafe Java.Lang.ICharSequence[] EchoFormatted (Java.Lang.ICharSequence[] messages)
17+
{
18+
const string __id = "Echo.([Ljava/lang/CharSequence;)[Ljava/lang/CharSequence;";
19+
IntPtr native_messages = JNIEnv.NewArray (messages);
20+
try {
21+
JniArgumentValue* __args = stackalloc JniArgumentValue [1];
22+
__args [0] = new JniArgumentValue (native_messages);
23+
var __rm = _members.StaticMethods.InvokeObjectMethod (__id, __args);
24+
return (Java.Lang.ICharSequence[]) JNIEnv.GetArray (__rm.Handle, JniHandleOwnership.TransferLocalRef, typeof (Java.Lang.ICharSequence));
25+
} finally {
26+
if (messages != null) {
27+
JNIEnv.CopyArray (native_messages, messages);
28+
JNIEnv.DeleteLocalRef (native_messages);
29+
}
30+
global::System.GC.KeepAlive (messages);
31+
}
32+
}
33+
34+
public static string[] Echo (string[] messages)
35+
{
36+
var jlca_messages = CharSequence.ArrayFromStringArray (messages);
37+
Java.Lang.ICharSequence[] __result = EchoFormatted (jlca_messages);
38+
var __rsval = CharSequence.ArrayToStringArray (__result);
39+
if (jlca_messages != null) foreach (var s in jlca_messages) s?.Dispose ();
40+
return __rsval;
41+
}
42+
43+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Metadata.xml XPath class reference: path="/api/package[@name='java.code']/class[@name='MyClass']"
2+
[global::Android.Runtime.Register ("java/code/MyClass", DoNotGenerateAcw=true)]
3+
public partial class MyClass {
4+
5+
// Metadata.xml XPath field reference: path="/api/package[@name='java.code']/class[@name='MyClass']/field[@name='field']"
6+
[Register ("field")]
7+
public java.code.Example field {
8+
get {
9+
const string __id = "field.Ljava/code/Example;";
10+
11+
var __v = _members.InstanceFields.GetObjectValue (__id, this);
12+
return global::Java.Lang.Object.GetObject<java.code.Example> (__v.Handle, JniHandleOwnership.TransferLocalRef);
13+
}
14+
set {
15+
const string __id = "field.Ljava/code/Example;";
16+
17+
IntPtr native_value = global::Android.Runtime.JNIEnv.ToLocalJniHandle (value);
18+
try {
19+
_members.InstanceFields.SetValue (__id, this, new JniObjectReference (native_value));
20+
} finally {
21+
global::Android.Runtime.JNIEnv.DeleteLocalRef (native_value);
22+
}
23+
}
24+
}
25+
26+
static readonly JniPeerMembers _members = new XAPeerMembers ("java/code/MyClass", typeof (MyClass));
27+
28+
internal static IntPtr class_ref {
29+
get { return _members.JniPeerType.PeerReference.Handle; }
30+
}
31+
32+
protected MyClass (IntPtr javaReference, JniHandleOwnership transfer) : base (javaReference, transfer)
33+
{
34+
}
35+
36+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Metadata.xml XPath class reference: path="/api/package[@name='java.code']/class[@name='MyClass']"
2+
[global::Android.Runtime.Register ("java/code/MyClass", DoNotGenerateAcw=true)]
3+
public partial class MyClass {
4+
5+
internal static IntPtr java_class_handle;
6+
internal static IntPtr class_ref {
7+
get {
8+
return JNIEnv.FindClass ("java/code/MyClass", ref java_class_handle);
9+
}
10+
}
11+
12+
protected MyClass (IntPtr javaReference, JniHandleOwnership transfer) : base (javaReference, transfer) {}
13+
14+
static IntPtr id_Echo_arrayLjava_lang_CharSequence_;
15+
// Metadata.xml XPath method reference: path="/api/package[@name='java.code']/class[@name='MyClass']/method[@name='Echo' and count(parameter)=1 and parameter[1][@type='java.lang.CharSequence[]']]"
16+
[Register ("Echo", "([Ljava/lang/CharSequence;)[Ljava/lang/CharSequence;", "")]
17+
public static unsafe Java.Lang.ICharSequence[] EchoFormatted (Java.Lang.ICharSequence[] messages)
18+
{
19+
if (id_Echo_arrayLjava_lang_CharSequence_ == IntPtr.Zero)
20+
id_Echo_arrayLjava_lang_CharSequence_ = JNIEnv.GetStaticMethodID (class_ref, "Echo", "([Ljava/lang/CharSequence;)[Ljava/lang/CharSequence;");
21+
IntPtr native_messages = JNIEnv.NewArray (messages);
22+
try {
23+
JValue* __args = stackalloc JValue [1];
24+
__args [0] = new JValue (native_messages);
25+
Java.Lang.ICharSequence[] __ret = (Java.Lang.ICharSequence[]) JNIEnv.GetArray (JNIEnv.CallStaticObjectMethod (class_ref, id_Echo_arrayLjava_lang_CharSequence_, __args), JniHandleOwnership.TransferLocalRef, typeof (Java.Lang.ICharSequence));
26+
return __ret;
27+
} finally {
28+
if (messages != null) {
29+
JNIEnv.CopyArray (native_messages, messages);
30+
JNIEnv.DeleteLocalRef (native_messages);
31+
}
32+
}
33+
}
34+
35+
public static string[] Echo (string[] messages)
36+
{
37+
var jlca_messages = CharSequence.ArrayFromStringArray(messages);
38+
Java.Lang.ICharSequence[] __result = EchoFormatted (jlca_messages);
39+
var __rsval = CharSequence.ArrayToStringArray (__result);
40+
if (jlca_messages != null) foreach (var s in jlca_messages) s?.Dispose ();
41+
return __rsval;
42+
}
43+
44+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Metadata.xml XPath class reference: path="/api/package[@name='java.code']/class[@name='MyClass']"
2+
[global::Android.Runtime.Register ("java/code/MyClass", DoNotGenerateAcw=true)]
3+
public partial class MyClass {
4+
5+
6+
static IntPtr field_jfieldId;
7+
8+
// Metadata.xml XPath field reference: path="/api/package[@name='java.code']/class[@name='MyClass']/field[@name='field']"
9+
[Register ("field")]
10+
public java.code.Example field {
11+
get {
12+
if (field_jfieldId == IntPtr.Zero)
13+
field_jfieldId = JNIEnv.GetFieldID (class_ref, "field", "Ljava/code/Example;");
14+
IntPtr __ret = JNIEnv.GetObjectField (((global::Java.Lang.Object) this).Handle, field_jfieldId);
15+
return global::Java.Lang.Object.GetObject<java.code.Example> (__ret, JniHandleOwnership.TransferLocalRef);
16+
}
17+
set {
18+
if (field_jfieldId == IntPtr.Zero)
19+
field_jfieldId = JNIEnv.GetFieldID (class_ref, "field", "Ljava/code/Example;");
20+
IntPtr native_value = JNIEnv.ToLocalJniHandle (value);
21+
try {
22+
JNIEnv.SetField (((global::Java.Lang.Object) this).Handle, field_jfieldId, native_value);
23+
} finally {
24+
JNIEnv.DeleteLocalRef (native_value);
25+
}
26+
}
27+
}
28+
internal static IntPtr java_class_handle;
29+
internal static IntPtr class_ref {
30+
get {
31+
return JNIEnv.FindClass ("java/code/MyClass", ref java_class_handle);
32+
}
33+
}
34+
35+
protected MyClass (IntPtr javaReference, JniHandleOwnership transfer) : base (javaReference, transfer) {}
36+
37+
}

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,26 @@ public void WriteClassAbstractMembers ()
10521052
Assert.AreEqual (GetExpected (nameof (WriteClassAbstractMembers)), writer.ToString ().NormalizeLineEndings ());
10531053
}
10541054

1055+
[Test]
1056+
public void WriteObjectField ()
1057+
{
1058+
options.SymbolTable.AddType (new TestClass (null, "Java.Lang.Object"));
1059+
var eClass = new TestClass ("Java.Lang.Object", "java.code.Example");
1060+
options.SymbolTable.AddType (eClass);
1061+
1062+
var @class = new TestClass ("Object", "java.code.MyClass");
1063+
1064+
var field = SupportTypeBuilder.CreateField ("field", options, "java.code.Example");
1065+
1066+
@class.Fields.Add (field);
1067+
1068+
generator.Context.ContextTypes.Push (@class);
1069+
generator.WriteType (@class, string.Empty, new GenerationInfo ("", "", "MyAssembly"));
1070+
generator.Context.ContextTypes.Pop ();
1071+
1072+
AssertTargetedExpected (nameof (WriteObjectField), writer.ToString ());
1073+
}
1074+
10551075
[Test]
10561076
public void WriteInterface ()
10571077
{
@@ -1158,6 +1178,23 @@ public void WriteInterfaceEventHandlerImplContent ()
11581178
AssertOriginalExpected (nameof (WriteInterfaceEventHandlerImplContent), writer.ToString ());
11591179
}
11601180

1181+
[Test]
1182+
public void WriteMethodWithCharSequenceArrays ()
1183+
{
1184+
var @class = new TestClass ("Object", "java.code.MyClass");
1185+
1186+
var echo = SupportTypeBuilder.CreateMethod (@class, "Echo", options, "java.lang.CharSequence[]", isStatic: true, isAbstract: false,
1187+
parameters: new Parameter ("messages", "java.lang.CharSequence[]", "Java.Lang.ICharSequence[]", isEnumified: false));
1188+
1189+
@class.AddMethod (echo);
1190+
1191+
generator.Context.ContextTypes.Push (@class);
1192+
generator.WriteType (@class, string.Empty, new GenerationInfo ("", "", "MyAssembly"));
1193+
generator.Context.ContextTypes.Pop ();
1194+
1195+
AssertOriginalTargetExpected (nameof (WriteMethodWithCharSequenceArrays), writer.ToString ());
1196+
}
1197+
11611198
[Test]
11621199
public void WriteParameterListCallArgs ()
11631200
{

tests/generator-Tests/Unit-Tests/SupportTypes.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,18 @@ public static TestInterface CreateEmptyInterface (string interfaceName)
266266
return iface;
267267
}
268268

269+
public static TestField CreateField (string fieldName, CodeGenerationOptions options, string fieldType, bool isStatic = false)
270+
{
271+
var field = new TestField (fieldType, fieldName);
272+
273+
if (isStatic)
274+
field.SetStatic ();
275+
276+
field.Validate (options, null, new CodeGeneratorContext ());
277+
278+
return field;
279+
}
280+
269281
public static TestInterface CreateInterface (string interfaceName, CodeGenerationOptions options)
270282
{
271283
var iface = CreateEmptyInterface (interfaceName);

0 commit comments

Comments
 (0)