[JNIEnv] Support sbyte jagged arrays - #12115
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0e043be9-9070-43bc-aea4-a48438690bdf
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0e043be9-9070-43bc-aea4-a48438690bdf
|
/review |
|
✅ Android PR Reviewer completed successfully!
|
There was a problem hiding this comment.
Pull request overview
This PR aims to extend Android.Runtime.JNIEnv array marshaling to support sbyte (including jagged arrays) by treating managed sbyte[] as JNI byte[] ([B) storage bit-for-bit, and it adds on-device regression tests for array creation, reading, and copying at ranks 1–3.
Changes:
- Add
sbytehandling to sharedJNIEnvconverter tables used byNewArray,GetArray,CopyArray, and element access. - Add new on-device tests validating
sbyte/bytejagged-array round-trips and expected JNI signatures ([B,[[B,[[[B).
Show a summary per file
| File | Description |
|---|---|
| tests/Mono.Android-Tests/Mono.Android-Tests/Android.Runtime/JnienvArrayMarshaling.cs | Adds regression tests for sbyte jagged arrays and high-bit byte preservation across ranks 1–3. |
| src/Mono.Android/Android.Runtime/JNIEnv.cs | Introduces sbyte entries in marshaling/converter dictionaries used by array creation/copying/reading and element access. |
Review details
Suppressed comments (4)
src/Mono.Android/Android.Runtime/JNIEnv.cs:934
- ❌ error JNI interop —
(byte[])(object)(sbyte[])sourcewill throwInvalidCastExceptionat runtime; arrays ofsbyteare not interchangeable with arrays ofbyte. This path needs a real conversion or ansbyte[]-aware JNI region helper instead of casting.
{ typeof (sbyte), (source, dest) => CopyArray ((byte[]) (object) (sbyte[]) source, dest) },
src/Mono.Android/Android.Runtime/JNIEnv.cs:1057
- ❌ error JNI interop —
CopyArray (source, (byte[])(object)r)will throwInvalidCastExceptionbecauseris ansbyte[]. This will breakJNIEnv.GetArray<sbyte>and jagged-array recursion forsbyte.
{ typeof (sbyte), (type, source, len) => {
var r = new sbyte [len];
CopyArray (source, (byte[]) (object) r);
return r;
src/Mono.Android/Android.Runtime/JNIEnv.cs:1393
- ❌ error JNI interop —
NewArray ((byte[])(object)(sbyte[])source)relies on an invalid runtime cast betweensbyte[]andbyte[]. This will throw and prevent creating JNI arrays from managedsbyte[].
{ typeof (sbyte), (source) => NewArray ((byte[]) (object) (sbyte[]) source) },
src/Mono.Android/Android.Runtime/JNIEnv.cs:1531
- ❌ error JNI interop —
(byte[])(object)_valuewill throwInvalidCastExceptionbecause_valueis ansbyte[]. This breaks setting individualsbytearray elements (and any recursive array creation path that uses this dictionary).
{ typeof (sbyte), (dest, index, value) => {
var _value = new[]{(sbyte) value!};
_SetByteArrayRegion (dest, index, _value.Length, (byte[]) (object) _value);
} },
- Files reviewed: 2/2 changed files
- Comments generated: 2
- Review effort level: Lite
| { typeof (sbyte), (type, source, index) => { | ||
| var r = new sbyte [1]; | ||
| _GetByteArrayRegion (source, index, 1, (byte[]) (object) r); | ||
| return r [0]; |
| [TestCase (false)] | ||
| [TestCase (true)] | ||
| public void NewAndGetArray_SByteJaggedArrays (bool generic) | ||
| { | ||
| AssertArrayRoundTrip ( | ||
| new sbyte[]{sbyte.MinValue, -1, 0, sbyte.MaxValue}, | ||
| new sbyte[]{1, 2, -2, -3}, | ||
| generic); |
There was a problem hiding this comment.
⚠️ Needs Changes
Findings: 0 errors · 0 warnings · 1 suggestion
The sbyte converter additions are otherwise consistent with the existing signed/unsigned primitive-array reinterpretation pattern, and the rank 1–3 round-trip tests verify both signed values and unchanged high-bit byte behavior. Please avoid introducing the new null-forgiving operator noted inline; the focused element-access coverage requested in the existing review thread would also complete coverage of the changed converter tables.
The latest dotnet-android build (#1562046) is still in progress with no failures reported so far, so the PR is not yet fully green.
Generated by Android PR Reviewer for #12115 · gpt56 · 100.7 AIC · ⌖ 8.98 AIC · ⊞ 25.7K
Comment /review to run again
| _SetByteArrayRegion (dest, index, _value.Length, _value); | ||
| } }, | ||
| { typeof (sbyte), (dest, index, value) => { | ||
| var _value = new[]{(sbyte) value!}; |
There was a problem hiding this comment.
🤖 💡 Nullable — This new entry introduces another null-forgiving operator even though value is declared object?. Please pattern-match it to an sbyte (and throw a contextual exception for null or the wrong type) before creating _value; that preserves the invariant instead of suppressing nullable analysis.
Rule: Never use the null-forgiving operator
Summary
sbyteconverters beside the existingbyteconverters used by sharedJNIEnvarray creation, copying, reading, and element accesssbyte[]as JNI byte-array storage bit-for-bit, so negative managed values retain their two's-complement byte representation[Bfallback routing mapped to managedbyte, avoiding any canonicalization of existingbyterequests tosbyteThis affects generic and non-generic
JNIEnv.NewArray,JNIEnv.GetArray, andJNIEnv.CopyArraypaths, including recursive jagged-array handling.Experiment evidence
Both legacy and trimmable typemap experiments produced the expected JNI signatures (
[[Band[[[B) forsbytejagged arrays. Creation then failed during recursive leaf conversion with:The signatures were therefore already correct; the shared converter dictionaries omitted
sbytewhile containingbyte.Tests
Added on-device regression coverage for generic and non-generic
NewArray/GetArray/CopyArrayat ranks 1, 2, and 3:sbyte: minimum, negative, zero, and maximum valuesbyte: values with high bits set, confirming existing unsigned managed behavior remains unchanged[B,[[B, and[[[BValidation:
dotnet build src/Mono.Android/Mono.Android.csproj --no-restore ...— passed (15 existing warnings)