Skip to content

perf(android): pass UInt64 records and promise results through JNI directly - #1999

Closed
mrousavy wants to merge 3 commits into
mrousavy/v1-kotlin-variant-casesfrom
mrousavy/v1-kotlin-erased-ulong
Closed

mrousavy wants to merge 3 commits into
mrousavy/v1-kotlin-variant-casesfrom
mrousavy/v1-kotlin-erased-ulong

Conversation

@mrousavy

@mrousavy mrousavy commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

Folded into #1997. Direct boxed unsigned handling now covers maps and Promise results alongside arrays, with the regression cases preserved.

The replacement stack is #2017 → #1997 → #2009.

@mrousavy
mrousavy added this pull request to stack #2000 October 23, 2026 12:23
@vercel

vercel Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
nitro-docs Ready Ready Preview Oct 28, 2026 10:59am UTC

Request Review

@mrousavy
mrousavy force-pushed the mrousavy/v1-kotlin-variant-cases branch from 1d637c3 to 1143302 Compare October 23, 2026 17:34
@mrousavy
mrousavy force-pushed the mrousavy/v1-kotlin-erased-ulong branch from dbb3032 to ad1f1e3 Compare October 23, 2026 17:34
@mrousavy
mrousavy force-pushed the mrousavy/v1-kotlin-variant-cases branch from 1143302 to e180990 Compare October 23, 2026 17:36
@mrousavy
mrousavy force-pushed the mrousavy/v1-kotlin-erased-ulong branch from ad1f1e3 to d961a25 Compare October 23, 2026 17:36
@mrousavy
mrousavy force-pushed the mrousavy/v1-kotlin-erased-ulong branch from d961a25 to 6a9e366 Compare October 23, 2026 17:42
@mrousavy
mrousavy force-pushed the mrousavy/v1-kotlin-variant-cases branch from e180990 to 771aed0 Compare October 23, 2026 17:51
@mrousavy
mrousavy force-pushed the mrousavy/v1-kotlin-erased-ulong branch from 6a9e366 to f0525bb Compare October 23, 2026 17:51
@mrousavy
mrousavy force-pushed the mrousavy/v1-kotlin-erased-ulong branch from f0525bb to be7dd57 Compare October 23, 2026 18:13
Kotlin exposed UInt64[] as Array<ULong> while JNI carries a long[], so
every crossing copied the array and boxed each element. Generated
structs copied it again each time C++ read the field.

UInt64[] is now ULongArray, a value class over LongArray, and the bridge
converts it with asLongArray() and asULongArray(), which reuse the same
array. Struct fields hold the long[] that JNI reads directly.
UInt64[][] becomes Array<ULongArray>, so only the outer array is mapped.
The mapToLongArray helpers in nitro-core have no callers left and are
removed.

ULongArray still requires opting in to ExperimentalUnsignedTypes, and
Kotlin warns without it. The generated +autolinking.gradle opts each
Nitro module in, which covers generated code and the module's own spec
implementations. ULongArray boxes compare by identity, so generated
structs compare these fields through their long[].

New TestObject methods pass UInt64 arrays as parameters, nested and
optional arrays, Promise results and callback values.
Kotlin variant case classes stored the type JNI uses instead of the
public Kotlin type. A UInt64 case held a Long, so values above 2^63 read
back negative and X.First(5uL) did not compile. A UInt64[] case held a
LongArray, and a callback case held the generated Callback_* class, so
constructing that case meant typing a Nitrogen specialization name.

Each case now holds its public type, e.g. ULong, ULongArray or
Callback<...>, and fromCpp_<n> converts from the JNI value. JNI reads the
value field directly when it already has the JNI representation, as for
ULong and ULongArray. Otherwise it calls a private getValue_cxx()
accessor, like structs do. A callback case converts with
Callback_*.fromKotlin(), which returns a callback that came from C++
unchanged.

JNI calls these accessors and the struct get<Field>_cxx() getters with
getNonvirtualMethod. Both are private and final, like the hybrid object
_cxx bridges that already use it.

New TestObject methods bounce UInt64 | string and UInt64[] | string
variants. The Kotlin fixture rebuilds the ULong case with its
constructor.
…rectly

Record<string, UInt64> crossed JNI as a Map<String, Long>, so Kotlin
copied the map with mapValues on every crossing. Promise<UInt64> and
Promise<UInt64[]> results went through a second Promise that converted
the value.

Map values and Promise results are erased generics, so Kotlin can keep
them as kotlin.ULong and kotlin.ULongArray boxes. JNI cannot name those
classes, since R8 may rename them in release builds without keep rules,
so it sees them as an Object. It creates and reads the boxes itself with
AllocObject and field access, using the classes and value fields that
ULongBox, a @keep helper in nitro-core, gives it once. ULongBox finds
the fields by type, and also boxes and unboxes in Kotlin so R8 keeps
the fields and does not assume their default value.

UInt64? parameters and fields, and arrays of optional UInt64, still
cross as Long. Their JVM signatures name kotlin.ULong, so they would
need keep rules for Kotlin stdlib classes, which Nitro does not ship.

New TestObject methods pass UInt64 and UInt64[] records and
Promise<UInt64>, and call UInt64 callbacks with callAsync.
@mrousavy
mrousavy force-pushed the mrousavy/v1-kotlin-variant-cases branch from 771aed0 to 56adfa9 Compare October 28, 2026 10:58
@mrousavy
mrousavy force-pushed the mrousavy/v1-kotlin-erased-ulong branch from be7dd57 to 0891153 Compare October 28, 2026 10:58
@mrousavy
mrousavy removed this pull request from stack #2000 October 28, 2026 11:22
@mrousavy
mrousavy force-pushed the mrousavy/v1-kotlin-variant-cases branch from 56adfa9 to 5e44d44 Compare October 28, 2026 11:22
@mrousavy mrousavy closed this Oct 28, 2026

This branch was successfully deployed

1 active deployment
Preview — 08911535 Deployed Oct 28, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant