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

Avoid non-blittable types in native callback methods #1027

Description

@grendello

While working on implementation of marshal methods (a way to replace the current native JNI method registration with
generated code) which take advantage of the [UnmanagedCallersOnly] attribute, I came across a problem that some of
our registered methods either return a bool or take a parameter which is a bool. The problem here is that bool
is a non-blittable type, while all [UnmanagedCallersOnly] methods must use only blittable types (as they are
called directly by the native code, bypassing marshaling).

Marshal methods convert the code output by the generator from:

staticDelegate?cb_setChildrenDrawingOrderEnabled_Z;staticDelegateGetSetChildrenDrawingOrderEnabled_ZHandler(){if(cb_setChildrenDrawingOrderEnabled_Z==null)cb_setChildrenDrawingOrderEnabled_Z=JNINativeWrapper.CreateDelegate((_JniMarshal_PPZ_V)n_SetChildrenDrawingOrderEnabled_Z);returncb_setChildrenDrawingOrderEnabled_Z;}staticvoidn_SetChildrenDrawingOrderEnabled_Z(IntPtrjnienv,IntPtrnative__this,boolenabled){var__this=global::Java.Lang.Object.GetObject<Android.Views.ViewGroup>(jnienv,native__this,JniHandleOwnership.DoNotTransfer)!;__this.ChildrenDrawingOrderEnabled=enabled;}protectedvirtualunsafeboolChildrenDrawingOrderEnabled{[Register("setChildrenDrawingOrderEnabled","(Z)V","GetSetChildrenDrawingOrderEnabled_ZHandler")]
set {// ...}}

to

[UnmanagedCallersOnly]staticvoidn_SetChildrenDrawingOrderEnabled_Z(IntPtrjnienv,IntPtrnative__this,boolenabled){var__this=global::Java.Lang.Object.GetObject<Android.Views.ViewGroup>(jnienv,native__this,JniHandleOwnership.DoNotTransfer)!;__this.ChildrenDrawingOrderEnabled=enabled;}protectedvirtualunsafeboolChildrenDrawingOrderEnabled{[Register("setChildrenDrawingOrderEnabled","(Z)V","GetSetChildrenDrawingOrderEnabled_ZHandler")]
set {// ...}}

And at run time, a pointer to n_SetChildrenDrawingOrderEnabled_Z is obtained using the Mono embedding API mono_method_get_unmanaged_callers_only_ftnptr.
However, when a non-blittable type is encountered, the function will return an error:

method Android.Views.ViewGroup:n_SetChildrenDrawingOrderEnabled_Z(intptr,intptr,bool) with UnmanagedCallersOnlyAttribute has non-blittable parameters or return type assembly:<unknown assembly> type:<unknown ty
pe> member:(null)

The problem is quite common in MAUI apps, since in a simple Hello World app, out of 183 marshal method candidates, 133 have a bool in their parameter list or as a return value.
I have implemented a workaround for this issue (which we will keep in the future, to deal with 3rd party libraries that weren't regenerated using the new generator) but the real fix is to make the generator instead output code equivalent to:

staticvoidn_SetChildrenDrawingOrderEnabled_Z(IntPtrjnienv,IntPtrnative__this,byteenabled){var__this=global::Java.Lang.Object.GetObject<Android.Views.ViewGroup>(jnienv,native__this,JniHandleOwnership.DoNotTransfer)!;__this.ChildrenDrawingOrderEnabled=enabled!=0;}protectedvirtualunsafeboolChildrenDrawingOrderEnabled{[Register("setChildrenDrawingOrderEnabled","(Z)V","GetSetChildrenDrawingOrderEnabled_ZHandler")]
set {// ...}}

or, for a method which returns a bool:

staticbyten_IsMarginRelative(IntPtrjnienv,IntPtrnative__this){var__this=global::Java.Lang.Object.GetObject<Android.Views.ViewGroup.MarginLayoutParams>(jnienv,native__this,JniHandleOwnership.DoNotTransfer)!;return__this.IsMarginRelative?1:0;}publicvirtualunsafeboolIsMarginRelative{[Register("isMarginRelative","()Z","GetIsMarginRelativeHandler")]
get {// ...}}

The reason to choose byte is that JNI's jboolean type is defined as an unsigned 8-bit value and the reason to explicitly return 1 or 0 instead of
just casting the managed bool value is that the boolean type in dotnet always has value of 0 for false, but can have -1, 1 or != 0 for true,
depending on the VM or context and so it's safer to "downcast" that set to the 0/1 values common in other languages (including Java, C and C++ about
which we care)

In Mono.Android generated MCW code we currently have 4824 native callback methods either returning bool or with at least one bool parameter.

The generator should take into account other System namespace non-blittable types, not just bool (within reason - only those that can potentially happen in bindings). The list can be found in this table,
for reference copied below:

Non-blittable typeDescription
System.ArrayConverts to a C-style array or a SAFEARRAY.
System.BooleanConverts to a 1, 2, or 4-byte value with true as 1 or -1.
System.CharConverts to a Unicode or ANSI character.
System.ClassConverts to a class interface.
System.ObjectConverts to a variant or an interface.
System.MdarrayConverts to a C-style array or a SAFEARRAY.
System.StringConverts to a string terminating in a null reference or to a BSTR.
System.ValuetypeConverts to a structure with a fixed memory layout.
System.SzarrayConverts to a C-style array or a SAFEARRAY.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementProposed change to current functionalitygeneratorIssues binding a Java library (generator, class-parse, etc.)

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions