Skip to content

Commit db62a97

Browse files
committed
[generator] Avoid non-blittable types in native callback methods
1 parent 9b1d878 commit db62a97

File tree

6 files changed

+92
-3
lines changed

6 files changed

+92
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System;
2+
using MonoDroid.Generation;
3+
using NUnit.Framework;
4+
using Xamarin.Android.Binder;
5+
6+
namespace generatortests;
7+
8+
[TestFixture]
9+
class BlittableTypeTests : CodeGeneratorTestBase
10+
{
11+
protected override CodeGenerationTarget Target => CodeGenerationTarget.XAJavaInterop1;
12+
13+
[Test]
14+
public void MethodWithBoolReturnType ()
15+
{
16+
var klass = new TestClass ("Object", "java.code.MyClass");
17+
var method = SupportTypeBuilder.CreateMethod (klass, "IsEmpty", options, "boolean");
18+
19+
klass.Methods.Add (method);
20+
21+
var actual = GetGeneratedTypeOutput (klass);
22+
23+
// Return type should be byte
24+
Assert.That (actual, Contains.Substring ("static byte n_IsEmpty"));
25+
26+
// Return statement should convert to 0 or 1
27+
Assert.That (actual, Contains.Substring ("return __this.IsEmpty () ? 1 : 0"));
28+
29+
// Ensure the marshal delegate is byte
30+
Assert.That (actual, Contains.Substring ("new _JniMarshal_PP_B"));
31+
Assert.That (actual, Does.Not.Contains ("new _JniMarshal_PP_Z"));
32+
}
33+
34+
[Test]
35+
public void MethodWithBoolParameter ()
36+
{
37+
var klass = new TestClass ("Object", "java.code.MyClass");
38+
var method = SupportTypeBuilder.CreateMethod (klass, "SetEmpty", options, "void", parameters: new Parameter ("value", "boolean", "bool", false));
39+
40+
klass.Methods.Add (method);
41+
42+
var actual = GetGeneratedTypeOutput (klass);
43+
44+
// Method parameter should be byte
45+
Assert.That (actual, Contains.Substring ("static void n_SetEmpty_Z (IntPtr jnienv, IntPtr native__this, byte native_value)"));
46+
47+
// Method should convert from 0 or 1
48+
Assert.That (actual, Contains.Substring ("var value = native_value != 0;"));
49+
50+
// Ensure the marshal delegate is byte
51+
Assert.That (actual, Contains.Substring ("new _JniMarshal_PPB_V"));
52+
Assert.That (actual, Does.Not.Contains ("new _JniMarshal_PPZ_V"));
53+
}
54+
55+
[Test]
56+
public void BoolProperty ()
57+
{
58+
var klass = SupportTypeBuilder.CreateClassWithProperty ("MyClass", "com.example.myClass", "IsEmpty", "boolean", options);
59+
var actual = GetGeneratedTypeOutput (klass);
60+
61+
// Getter return type should be byte
62+
Assert.That (actual, Contains.Substring ("static byte n_get_IsEmpty"));
63+
64+
// Getter return statement should convert to 0 or 1
65+
Assert.That (actual, Contains.Substring ("return __this.IsEmpty ? 1 : 0"));
66+
67+
// Setter parameter should be byte
68+
Assert.That (actual, Contains.Substring ("static void n_set_IsEmpty_Z (IntPtr jnienv, IntPtr native__this, byte native_value)"));
69+
70+
// Setter should convert from 0 or 1
71+
Assert.That (actual, Contains.Substring ("var value = native_value != 0;"));
72+
73+
// Ensure the marshal delegate is byte
74+
Assert.That (actual, Contains.Substring ("new _JniMarshal_PP_B"));
75+
Assert.That (actual, Does.Not.Contains ("new _JniMarshal_PP_Z"));
76+
}
77+
}

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

+9
Original file line numberDiff line numberDiff line change
@@ -131,5 +131,14 @@ protected static string GetAssertionMessage (string header, string expected, str
131131
$"Expected:\n```\n{expected}\n```\n" +
132132
$"Actual:\n```\n{actual}\n```";
133133
}
134+
135+
protected string GetGeneratedTypeOutput (GenBase gen)
136+
{
137+
generator.Context.ContextTypes.Push (gen);
138+
generator.WriteType (gen, string.Empty, new GenerationInfo ("", "", "MyAssembly"));
139+
generator.Context.ContextTypes.Pop ();
140+
141+
return writer.ToString ();
142+
}
134143
}
135144
}

tools/generator/CodeGenerationOptions.cs

+1
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ string GetJniTypeCode (ISymbol symbol)
217217
case "uint": return "i";
218218
case "ulong": return "j";
219219
case "ushort": return "s";
220+
case "boolean": return "B"; // We marshal boolean (Z) as byte (B)
220221
}
221222

222223
var jni_name = symbol.JniName;

tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/SymbolTable.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public SymbolTable (CodeGenerationTarget target)
5757
this.target = target;
5858

5959
AddType (new SimpleSymbol ("IntPtr.Zero", "void", "void", "V"));
60-
AddType (new SimpleSymbol ("false", "boolean", "bool", "Z"));
60+
AddType (new SimpleSymbol ("false", "boolean", "bool", "Z", "sbyte", from_fmt: "{0} != 0", to_fmt: "{0} ? (sbyte)1 : (sbyte)0"));
6161
AddType (new SimpleSymbol ("0", "byte", "sbyte", "B"));
6262
AddType (new SimpleSymbol ("(char)0", "char", "char", "C"));
6363
AddType (new SimpleSymbol ("0.0", "double", "double", "D"));

tools/generator/SourceWriters/BoundFieldAsProperty.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,11 @@ protected override void WriteGetterBody (CodeWriter writer)
8888
var invokeType = SourceWriterExtensions.GetInvokeType (field.GetMethodPrefix);
8989
var indirect = field.IsStatic ? "StaticFields" : "InstanceFields";
9090
var invoke = "Get{0}Value";
91+
var body_cast = invokeType == "Boolean" ? " ? (sbyte)1 : (sbyte)0" : string.Empty;
9192

9293
invoke = string.Format (invoke, invokeType);
9394

94-
writer.WriteLine ($"var __v = {field.Symbol.ReturnCast}_members.{indirect}.{invoke} (__id{(field.IsStatic ? "" : ", this")});");
95+
writer.WriteLine ($"var __v = {field.Symbol.ReturnCast}_members.{indirect}.{invoke} (__id{(field.IsStatic ? "" : ", this")}){body_cast};");
9596

9697
if (opt.CodeGenerationTarget == CodeGenerationTarget.JavaInterop1) {
9798
if (field.Symbol.NativeType == field.Symbol.FullName) {

tools/generator/SourceWriters/Extensions/SourceWriterExtensions.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,8 @@ public static void AddMethodBodyTryBlock (List<string> body, Method method, Code
277277
var this_param = method.IsStatic ? $"__id{call_args}" : $"__id, this{call_args}";
278278

279279
// Example: var __rm = _members.InstanceMethods.InvokeVirtualObjectMethod (__id, this, __args);
280-
body.Add ($"\t{return_var}{members}.{method_type}.Invoke{virt_type}{invokeType}Method ({this_param});");
280+
var body_cast = invokeType == "Boolean" ? " ? (sbyte)1 : (sbyte)0" : string.Empty;
281+
body.Add ($"\t{return_var}{members}.{method_type}.Invoke{virt_type}{invokeType}Method ({this_param}){body_cast};");
281282

282283
if (!method.IsVoid) {
283284
var r = "__rm";

0 commit comments

Comments
 (0)