Repository navigation
feat: pointers to records cross JNI as typed handles - #17
Merged
Merged
Conversation
A pointer to a named struct or union was a bare Long: any Long compiled where a tsf* was expected, and a NULL result was 0L that the next call dereferenced. Each such record now gets @JvmInline value class <Tag>Pointer(val address: Long), named by the record's tag (or an anonymous record's typedef), so node_t* and struct node* are one NodePointer. Parameters take the handle non-null; results are nullable, NULL being null. The external keeps the Long behind the <name>$kni wrapper strings already use: Kotlin mangles the JVM name of a function with a value-class parameter, which JNI could not resolve. Typedef'd void*, function pointers and pointers to pointers are unchanged. Bindings that passed these pointers as Long must use the handle type. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Builds a C counter, passes its handle back, and gets NULL as null: a wrapper whose $kni external did not resolve would fail the run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged
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.
Problem
The JNI generator passed every pointer to a struct as a bare
Long. AnyLong, even a channel number, compiled where atsf*was expected. A failed call's NULL came back as0L, ready to be dereferenced by the next call, which crashes the process.Change
Each named struct or union reached by a pointer gets a typed handle:
node_t*andstruct node*are oneNodePointer;constis ignored.externalkeeps theLongbehind the<name>$kniwrapper that strings already use. Kotlin mangles the JVM name of functions with value-class parameters, and JNI couldn't resolve it.void*, function pointers and pointers to pointers (LongArray?).addressgives the raw pointer, andTsfPointer(0L)passes NULL deliberately.Breaking for consumers: code passing these pointers as
Longmust switch to the handle type.Ownership (freeing, use after free) is out of scope here. It needs a DSL to declare the release function.
Tests
GenerateFromNativeIndexTestasserts the value class, the wrapper/external pair, tag naming through a typedef, and a nullable result (sample_createadded tosample.h). Full suite: 66 tests, 0 failures.includeBuild. Delegates moved toTsfPointercompile, and an instrumented test on a physical arm64 device (Android 17) passed 2/2: load, render,tsf_close, and garbage input rejected.🤖 Generated with Claude Code