Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[generator] Avoid non-blittable types in native callback methods #1296

Merged
merged 2 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions tests/generator-Tests/Unit-Tests/BlittableTypeTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
using System;
using MonoDroid.Generation;
using NUnit.Framework;
using Xamarin.Android.Binder;

namespace generatortests;

[TestFixture]
class BlittableTypeTests : CodeGeneratorTestBase
{
protected override CodeGenerationTarget Target => CodeGenerationTarget.XAJavaInterop1;

[Test]
public void MethodWithBoolReturnType ()
{
var klass = new TestClass ("Object", "java.code.MyClass");
var method = SupportTypeBuilder.CreateMethod (klass, "IsEmpty", options, "boolean");

klass.Methods.Add (method);

var actual = GetGeneratedTypeOutput (klass);

// Return type should be byte
Assert.That (actual, Contains.Substring ("static sbyte n_IsEmpty"));

// Return statement should convert to 0 or 1
Assert.That (actual, Contains.Substring ("return __this.IsEmpty () ? (sbyte)1 : (sbyte)0"));

// Ensure the marshal delegate is byte
Assert.That (actual, Contains.Substring ("new _JniMarshal_PP_B"));
Assert.That (actual, Does.Not.Contains ("new _JniMarshal_PP_Z"));
}

[Test]
public void MethodWithBoolParameter ()
{
var klass = new TestClass ("Object", "java.code.MyClass");
var method = SupportTypeBuilder.CreateMethod (klass, "SetEmpty", options, "void", parameters: new Parameter ("value", "boolean", "bool", false));

klass.Methods.Add (method);

var actual = GetGeneratedTypeOutput (klass);

// Method parameter should be byte
Assert.That (actual, Contains.Substring ("static void n_SetEmpty_Z (IntPtr jnienv, IntPtr native__this, sbyte native_value)"));

// Method should convert from 0 or 1
Assert.That (actual, Contains.Substring ("var value = native_value != 0;"));

// Ensure the marshal delegate is byte
Assert.That (actual, Contains.Substring ("new _JniMarshal_PPB_V"));
Assert.That (actual, Does.Not.Contains ("new _JniMarshal_PPZ_V"));
}

[Test]
public void BoolProperty ()
{
var klass = SupportTypeBuilder.CreateClassWithProperty ("MyClass", "com.example.myClass", "IsEmpty", "boolean", options);
var actual = GetGeneratedTypeOutput (klass);

// Getter return type should be byte
Assert.That (actual, Contains.Substring ("static sbyte n_get_IsEmpty"));

// Getter return statement should convert to 0 or 1
Assert.That (actual, Contains.Substring ("return __this.IsEmpty ? (sbyte)1 : (sbyte)0"));

// Setter parameter should be byte
Assert.That (actual, Contains.Substring ("static void n_set_IsEmpty_Z (IntPtr jnienv, IntPtr native__this, sbyte native_value)"));

// Setter should convert from 0 or 1
Assert.That (actual, Contains.Substring ("var value = native_value != 0;"));

// Ensure the marshal delegate is byte
Assert.That (actual, Contains.Substring ("new _JniMarshal_PP_B"));
Assert.That (actual, Does.Not.Contains ("new _JniMarshal_PP_Z"));
}

[Test]
public void MethodWithCharReturnType ()
{
var klass = new TestClass ("Object", "java.code.MyClass");
var method = SupportTypeBuilder.CreateMethod (klass, "GetFirstLetter", options, "char");

klass.Methods.Add (method);

var actual = GetGeneratedTypeOutput (klass);

// Return type should be ushort
Assert.That (actual, Contains.Substring ("static ushort n_GetFirstLetter"));

// Return statement should convert to ushort
Assert.That (actual, Contains.Substring ("return (ushort)__this.GetFirstLetter ()"));

// Ensure the marshal delegate is ushort
Assert.That (actual, Contains.Substring ("new _JniMarshal_PP_s"));
Assert.That (actual, Does.Not.Contains ("new _JniMarshal_PP_C"));
}

[Test]
public void MethodWithCharParameter ()
{
var klass = new TestClass ("Object", "java.code.MyClass");
var method = SupportTypeBuilder.CreateMethod (klass, "SetFirstLetter", options, "void", parameters: new Parameter ("value", "char", "char", false));

klass.Methods.Add (method);

var actual = GetGeneratedTypeOutput (klass);

// Method parameter should be ushort
Assert.That (actual, Contains.Substring ("static void n_SetFirstLetter_C (IntPtr jnienv, IntPtr native__this, ushort native_value)"));

// Method should convert from ushort to char
Assert.That (actual, Contains.Substring ("var value = (char)native_value;"));

// Ensure the marshal delegate is ushort
Assert.That (actual, Contains.Substring ("new _JniMarshal_PPs_V"));
Assert.That (actual, Does.Not.Contains ("new _JniMarshal_PPC_V"));
}

[Test]
public void CharProperty ()
{
var klass = SupportTypeBuilder.CreateClassWithProperty ("MyClass", "com.example.myClass", "FirstLetter", "char", options);
var actual = GetGeneratedTypeOutput (klass);

// Getter return type should be ushort
Assert.That (actual, Contains.Substring ("static ushort n_get_FirstLetter"));

// Getter return statement should convert to ushort
Assert.That (actual, Contains.Substring ("return (ushort)__this.FirstLetter"));

// Setter parameter should be ushort
Assert.That (actual, Contains.Substring ("static void n_set_FirstLetter_C (IntPtr jnienv, IntPtr native__this, ushort native_value)"));

// Setter should convert from ushort to char
Assert.That (actual, Contains.Substring ("var value = (char)native_value;"));

// Ensure the marshal delegate is ushort
Assert.That (actual, Contains.Substring ("new _JniMarshal_PP_s"));
Assert.That (actual, Does.Not.Contains ("new _JniMarshal_PP_C"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,18 @@ internal partial class AnimatorListenerInvoker : global::Java.Lang.Object, Anima
#pragma warning disable 0169
static Delegate GetOnAnimationEnd_IHandler ()
{
return cb_OnAnimationEnd_OnAnimationEnd_I_Z ??= new _JniMarshal_PPI_Z (n_OnAnimationEnd_I);
return cb_OnAnimationEnd_OnAnimationEnd_I_Z ??= new _JniMarshal_PPI_B (n_OnAnimationEnd_I);
}

[global::System.Diagnostics.DebuggerDisableUserUnhandledExceptions]
static bool n_OnAnimationEnd_I (IntPtr jnienv, IntPtr native__this, int param1)
static sbyte n_OnAnimationEnd_I (IntPtr jnienv, IntPtr native__this, int param1)
{
if (!global::Java.Interop.JniEnvironment.BeginMarshalMethod (jnienv, out var __envp, out var __r))
return default;

try {
var __this = global::Java.Lang.Object.GetObject<java.code.AnimatorListener> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
return __this.OnAnimationEnd (param1);
return __this.OnAnimationEnd (param1) ? (sbyte)1 : (sbyte)0;
} catch (global::System.Exception __e) {
__r.OnUserUnhandledException (ref __envp, __e);
return default;
Expand All @@ -105,18 +105,18 @@ internal partial class AnimatorListenerInvoker : global::Java.Lang.Object, Anima
#pragma warning disable 0169
static Delegate GetOnAnimationEnd_IIHandler ()
{
return cb_OnAnimationEnd_OnAnimationEnd_II_Z ??= new _JniMarshal_PPII_Z (n_OnAnimationEnd_II);
return cb_OnAnimationEnd_OnAnimationEnd_II_Z ??= new _JniMarshal_PPII_B (n_OnAnimationEnd_II);
}

[global::System.Diagnostics.DebuggerDisableUserUnhandledExceptions]
static bool n_OnAnimationEnd_II (IntPtr jnienv, IntPtr native__this, int param1, int param2)
static sbyte n_OnAnimationEnd_II (IntPtr jnienv, IntPtr native__this, int param1, int param2)
{
if (!global::Java.Interop.JniEnvironment.BeginMarshalMethod (jnienv, out var __envp, out var __r))
return default;

try {
var __this = global::Java.Lang.Object.GetObject<java.code.AnimatorListener> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
return __this.OnAnimationEnd (param1, param2);
return __this.OnAnimationEnd (param1, param2) ? (sbyte)1 : (sbyte)0;
} catch (global::System.Exception __e) {
__r.OnUserUnhandledException (ref __envp, __e);
return default;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,18 @@ internal partial class AnimatorListenerInvoker : global::Java.Lang.Object, Anima
#pragma warning disable 0169
static Delegate GetOnAnimationEnd_IHandler ()
{
return cb_OnAnimationEnd_OnAnimationEnd_I_Z ??= new _JniMarshal_PPI_Z (n_OnAnimationEnd_I);
return cb_OnAnimationEnd_OnAnimationEnd_I_Z ??= new _JniMarshal_PPI_B (n_OnAnimationEnd_I);
}

[global::System.Diagnostics.DebuggerDisableUserUnhandledExceptions]
static bool n_OnAnimationEnd_I (IntPtr jnienv, IntPtr native__this, int param1)
static sbyte n_OnAnimationEnd_I (IntPtr jnienv, IntPtr native__this, int param1)
{
if (!global::Java.Interop.JniEnvironment.BeginMarshalMethod (jnienv, out var __envp, out var __r))
return default;

try {
var __this = global::Java.Lang.Object.GetObject<java.code.AnimatorListener> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
return __this.OnAnimationEnd (param1);
return __this.OnAnimationEnd (param1) ? (sbyte)1 : (sbyte)0;
} catch (global::System.Exception __e) {
__r.OnUserUnhandledException (ref __envp, __e);
return default;
Expand All @@ -105,18 +105,18 @@ internal partial class AnimatorListenerInvoker : global::Java.Lang.Object, Anima
#pragma warning disable 0169
static Delegate GetOnAnimationEnd_IIHandler ()
{
return cb_OnAnimationEnd_OnAnimationEnd_II_Z ??= new _JniMarshal_PPII_Z (n_OnAnimationEnd_II);
return cb_OnAnimationEnd_OnAnimationEnd_II_Z ??= new _JniMarshal_PPII_B (n_OnAnimationEnd_II);
}

[global::System.Diagnostics.DebuggerDisableUserUnhandledExceptions]
static bool n_OnAnimationEnd_II (IntPtr jnienv, IntPtr native__this, int param1, int param2)
static sbyte n_OnAnimationEnd_II (IntPtr jnienv, IntPtr native__this, int param1, int param2)
{
if (!global::Java.Interop.JniEnvironment.BeginMarshalMethod (jnienv, out var __envp, out var __r))
return default;

try {
var __this = global::Java.Lang.Object.GetObject<java.code.AnimatorListener> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
return __this.OnAnimationEnd (param1, param2);
return __this.OnAnimationEnd (param1, param2) ? (sbyte)1 : (sbyte)0;
} catch (global::System.Exception __e) {
__r.OnUserUnhandledException (ref __envp, __e);
return default;
Expand Down
9 changes: 9 additions & 0 deletions tests/generator-Tests/Unit-Tests/CodeGeneratorTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,5 +131,14 @@ protected static string GetAssertionMessage (string header, string expected, str
$"Expected:\n```\n{expected}\n```\n" +
$"Actual:\n```\n{actual}\n```";
}

protected string GetGeneratedTypeOutput (GenBase gen)
{
generator.Context.ContextTypes.Push (gen);
generator.WriteType (gen, string.Empty, new GenerationInfo ("", "", "MyAssembly"));
generator.Context.ContextTypes.Pop ();

return writer.ToString ();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,19 @@ public IExoMediaCryptoInvoker (IntPtr handle, JniHandleOwnership transfer) : bas
#pragma warning disable 0169
static Delegate GetRequiresSecureDecoderComponent_Ljava_lang_String_Handler ()
{
return cb_requiresSecureDecoderComponent_RequiresSecureDecoderComponent_Ljava_lang_String__Z ??= new _JniMarshal_PPL_Z (n_RequiresSecureDecoderComponent_Ljava_lang_String_);
return cb_requiresSecureDecoderComponent_RequiresSecureDecoderComponent_Ljava_lang_String__Z ??= new _JniMarshal_PPL_B (n_RequiresSecureDecoderComponent_Ljava_lang_String_);
}

[global::System.Diagnostics.DebuggerDisableUserUnhandledExceptions]
static bool n_RequiresSecureDecoderComponent_Ljava_lang_String_ (IntPtr jnienv, IntPtr native__this, IntPtr native_p0)
static sbyte n_RequiresSecureDecoderComponent_Ljava_lang_String_ (IntPtr jnienv, IntPtr native__this, IntPtr native_p0)
{
if (!global::Java.Interop.JniEnvironment.BeginMarshalMethod (jnienv, out var __envp, out var __r))
return default;

try {
var __this = global::Java.Lang.Object.GetObject<global::Com.Google.Android.Exoplayer.Drm.IExoMediaCrypto> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
var p0 = JNIEnv.GetString (native_p0, JniHandleOwnership.DoNotTransfer);
bool __ret = __this.RequiresSecureDecoderComponent (p0);
sbyte __ret = __this.RequiresSecureDecoderComponent (p0) ? (sbyte)1 : (sbyte)0;
return __ret;
} catch (global::System.Exception __e) {
__r.OnUserUnhandledException (ref __envp, __e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
[assembly:global::Android.Runtime.NamespaceMapping (Java = "com.google.android.exoplayer.drm", Managed="Com.Google.Android.Exoplayer.Drm")]

[global::System.Runtime.InteropServices.UnmanagedFunctionPointer (global::System.Runtime.InteropServices.CallingConvention.Winapi)]
delegate void _JniMarshal_PPL_V (IntPtr jnienv, IntPtr klass, IntPtr p0);
delegate sbyte _JniMarshal_PPL_B (IntPtr jnienv, IntPtr klass, IntPtr p0);
[global::System.Runtime.InteropServices.UnmanagedFunctionPointer (global::System.Runtime.InteropServices.CallingConvention.Winapi)]
delegate bool _JniMarshal_PPL_Z (IntPtr jnienv, IntPtr klass, IntPtr p0);
delegate void _JniMarshal_PPL_V (IntPtr jnienv, IntPtr klass, IntPtr p0);
[global::System.Runtime.InteropServices.UnmanagedFunctionPointer (global::System.Runtime.InteropServices.CallingConvention.Winapi)]
delegate void _JniMarshal_PPLLIIL_V (IntPtr jnienv, IntPtr klass, IntPtr p0, IntPtr p1, int p2, int p3, IntPtr p4);
#if !NET
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,18 +176,18 @@ public virtual unsafe void Mark (int readlimit)
#pragma warning disable 0169
static Delegate GetMarkSupportedHandler ()
{
return cb_markSupported_MarkSupported_Z ??= new _JniMarshal_PP_Z (n_MarkSupported);
return cb_markSupported_MarkSupported_Z ??= new _JniMarshal_PP_B (n_MarkSupported);
}

[global::System.Diagnostics.DebuggerDisableUserUnhandledExceptions]
static bool n_MarkSupported (IntPtr jnienv, IntPtr native__this)
static sbyte n_MarkSupported (IntPtr jnienv, IntPtr native__this)
{
if (!global::Java.Interop.JniEnvironment.BeginMarshalMethod (jnienv, out var __envp, out var __r))
return default;

try {
var __this = global::Java.Lang.Object.GetObject<global::Java.IO.InputStream> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
return __this.MarkSupported ();
return __this.MarkSupported () ? (sbyte)1 : (sbyte)0;
} catch (global::System.Exception __e) {
__r.OnUserUnhandledException (ref __envp, __e);
return default;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
[assembly:global::Android.Runtime.NamespaceMapping (Java = "java.lang", Managed="Java.Lang")]
[assembly:global::Android.Runtime.NamespaceMapping (Java = "java.io", Managed="Java.IO")]

[global::System.Runtime.InteropServices.UnmanagedFunctionPointer (global::System.Runtime.InteropServices.CallingConvention.Winapi)]
delegate sbyte _JniMarshal_PP_B (IntPtr jnienv, IntPtr klass);
[global::System.Runtime.InteropServices.UnmanagedFunctionPointer (global::System.Runtime.InteropServices.CallingConvention.Winapi)]
delegate int _JniMarshal_PP_I (IntPtr jnienv, IntPtr klass);
[global::System.Runtime.InteropServices.UnmanagedFunctionPointer (global::System.Runtime.InteropServices.CallingConvention.Winapi)]
delegate IntPtr _JniMarshal_PP_L (IntPtr jnienv, IntPtr klass);
[global::System.Runtime.InteropServices.UnmanagedFunctionPointer (global::System.Runtime.InteropServices.CallingConvention.Winapi)]
delegate void _JniMarshal_PP_V (IntPtr jnienv, IntPtr klass);
[global::System.Runtime.InteropServices.UnmanagedFunctionPointer (global::System.Runtime.InteropServices.CallingConvention.Winapi)]
delegate bool _JniMarshal_PP_Z (IntPtr jnienv, IntPtr klass);
[global::System.Runtime.InteropServices.UnmanagedFunctionPointer (global::System.Runtime.InteropServices.CallingConvention.Winapi)]
delegate void _JniMarshal_PPI_V (IntPtr jnienv, IntPtr klass, int p0);
[global::System.Runtime.InteropServices.UnmanagedFunctionPointer (global::System.Runtime.InteropServices.CallingConvention.Winapi)]
delegate long _JniMarshal_PPJ_J (IntPtr jnienv, IntPtr klass, long p0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,19 @@ public ICollectionInvoker (IntPtr handle, JniHandleOwnership transfer) : base (h
#pragma warning disable 0169
static Delegate GetAdd_Ljava_lang_Object_Handler ()
{
return cb_add_Add_Ljava_lang_Object__Z ??= new _JniMarshal_PPL_Z (n_Add_Ljava_lang_Object_);
return cb_add_Add_Ljava_lang_Object__Z ??= new _JniMarshal_PPL_B (n_Add_Ljava_lang_Object_);
}

[global::System.Diagnostics.DebuggerDisableUserUnhandledExceptions]
static bool n_Add_Ljava_lang_Object_ (IntPtr jnienv, IntPtr native__this, IntPtr native_e)
static sbyte n_Add_Ljava_lang_Object_ (IntPtr jnienv, IntPtr native__this, IntPtr native_e)
{
if (!global::Java.Interop.JniEnvironment.BeginMarshalMethod (jnienv, out var __envp, out var __r))
return default;

try {
var __this = global::Java.Lang.Object.GetObject<global::Java.Util.ICollection> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
var e = global::Java.Lang.Object.GetObject<global::Java.Lang.Object> (native_e, JniHandleOwnership.DoNotTransfer);
bool __ret = __this.Add (e);
sbyte __ret = __this.Add (e) ? (sbyte)1 : (sbyte)0;
return __ret;
} catch (global::System.Exception __e) {
__r.OnUserUnhandledException (ref __envp, __e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,19 @@ public IDequeInvoker (IntPtr handle, JniHandleOwnership transfer) : base (handle
#pragma warning disable 0169
static Delegate GetAdd_Ljava_lang_Object_Handler ()
{
return cb_add_Add_Ljava_lang_Object__Z ??= new _JniMarshal_PPL_Z (n_Add_Ljava_lang_Object_);
return cb_add_Add_Ljava_lang_Object__Z ??= new _JniMarshal_PPL_B (n_Add_Ljava_lang_Object_);
}

[global::System.Diagnostics.DebuggerDisableUserUnhandledExceptions]
static bool n_Add_Ljava_lang_Object_ (IntPtr jnienv, IntPtr native__this, IntPtr native_e)
static sbyte n_Add_Ljava_lang_Object_ (IntPtr jnienv, IntPtr native__this, IntPtr native_e)
{
if (!global::Java.Interop.JniEnvironment.BeginMarshalMethod (jnienv, out var __envp, out var __r))
return default;

try {
var __this = global::Java.Lang.Object.GetObject<global::Java.Util.IDeque> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
var e = global::Java.Lang.Object.GetObject<global::Java.Lang.Object> (native_e, JniHandleOwnership.DoNotTransfer);
bool __ret = __this.Add (e);
sbyte __ret = __this.Add (e) ? (sbyte)1 : (sbyte)0;
return __ret;
} catch (global::System.Exception __e) {
__r.OnUserUnhandledException (ref __envp, __e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,19 @@ public IQueueInvoker (IntPtr handle, JniHandleOwnership transfer) : base (handle
#pragma warning disable 0169
static Delegate GetAdd_Ljava_lang_Object_Handler ()
{
return cb_add_Add_Ljava_lang_Object__Z ??= new _JniMarshal_PPL_Z (n_Add_Ljava_lang_Object_);
return cb_add_Add_Ljava_lang_Object__Z ??= new _JniMarshal_PPL_B (n_Add_Ljava_lang_Object_);
}

[global::System.Diagnostics.DebuggerDisableUserUnhandledExceptions]
static bool n_Add_Ljava_lang_Object_ (IntPtr jnienv, IntPtr native__this, IntPtr native_e)
static sbyte n_Add_Ljava_lang_Object_ (IntPtr jnienv, IntPtr native__this, IntPtr native_e)
{
if (!global::Java.Interop.JniEnvironment.BeginMarshalMethod (jnienv, out var __envp, out var __r))
return default;

try {
var __this = global::Java.Lang.Object.GetObject<global::Java.Util.IQueue> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
var e = global::Java.Lang.Object.GetObject<global::Java.Lang.Object> (native_e, JniHandleOwnership.DoNotTransfer);
bool __ret = __this.Add (e);
sbyte __ret = __this.Add (e) ? (sbyte)1 : (sbyte)0;
return __ret;
} catch (global::System.Exception __e) {
__r.OnUserUnhandledException (ref __envp, __e);
Expand Down
Loading