Repository navigation
Conversation
mrousavy
added this pull request to stack #2000
October 23, 2026 12:23
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
This was referenced Oct 23, 2026
mrousavy
force-pushed
the
mrousavy/v1-kotlin-erased-ulong
branch
from
October 23, 2026 17:34
dbb3032 to
ad1f1e3
Compare
mrousavy
force-pushed
the
mrousavy/v1-kotlin-erased-ulong
branch
from
October 23, 2026 17:36
ad1f1e3 to
d961a25
Compare
mrousavy
force-pushed
the
mrousavy/v1-kotlin-erased-ulong
branch
from
October 23, 2026 17:42
d961a25 to
6a9e366
Compare
mrousavy
force-pushed
the
mrousavy/v1-kotlin-erased-ulong
branch
from
October 23, 2026 17:51
6a9e366 to
f0525bb
Compare
…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
force-pushed
the
mrousavy/v1-kotlin-erased-ulong
branch
from
October 23, 2026 18:13
f0525bb to
be7dd57
Compare
This was referenced Oct 23, 2026
This branch was previously deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Third of five stacked PRs: #1997 → #1998 → #1999 (this one) → #2008 → #2009.
Record<string, UInt64>crossed JNI as aMap<String, Long>, so Kotlin copied the map withmapValueson every crossing.Promise<UInt64>andPromise<UInt64[]>results, includingCallback.callAsyncresults, went through a secondPromisethat converted the value.Map values and Promise results are erased generics, so Kotlin can keep them as
kotlin.ULongandkotlin.ULongArrayboxes, and the JNI types becomeMap<String, ULong>andPromise<ULong>. JNI can't callULong'sbox-impl/unbox-implor name its class, because R8 can renamekotlin.ULongand its members in release builds and Nitro ships no keep rules for Kotlin stdlib classes. JNI sees these values asObjects (JULong,JULongArray) and creates and reads the boxes itself:AllocObjectand a field write to box a value, a field read to unbox it, with no call into Kotlin per value.ULongBox, a@Keepobject in nitro-core, hands JNI the box classes and their value fields once, and finds the fields by type, so R8 renaming them doesn't matter. It also boxes and unboxes in Kotlin, so R8 keeps those fields and doesn't assume they always hold their default value, since it can't see JNI's writes.UInt64?parameters, properties and fields, and(UInt64 | undefined)[], still cross asLong?. Their JVM signatures namekotlin.ULong(Lkotlin/ULong;,[Lkotlin/ULong;), so they would need those keep rules. That costs one box per optional scalar, or a copy per array of optionals.New
TestObjectmethods passUInt64andUInt64[]records andPromise<UInt64>, and have native code callUInt64callbacks withcallAsync. The existingroundtripUnsignedValuestest coversRecord<string, UInt64 | undefined>inside a struct.Validation: Kotlin compiles without warnings,
javapshows theULongBoxsignatures JNI looks up and that kotlin-stdlib'sULongandULongArrayeach have one instancelong/long[]field, and the changed nitro-core and generated JNI sources compile with the NDK. I did not run the runtime tests or an R8 release build locally.🤖 Generated with Claude Code