diff --git a/examples/covertest-kotlin/build.rs b/examples/covertest-kotlin/build.rs index c218d785..e210f31b 100644 --- a/examples/covertest-kotlin/build.rs +++ b/examples/covertest-kotlin/build.rs @@ -21,7 +21,6 @@ //! | `DataClassDecl::jobject_input()` | `ObjectBoundary` (127 `Long` leaves plus JNI infrastructure exceed the JVM's 255-slot method limit) | //! | `PtrClassDecl` | `Storage` / `Summary` / `StorageError` / `Archive` / handlers | //! | `EnumClassDecl` | `Priority` | -//! | `ValueClassDecl` | `Stamp` (+ `Vec` → `List`) | //! | `convert!` + chained source streams | `Millis` ⇄ `Long` via `covertest-helpers` fns | //! | `Source::builder().crate_name()` | the helpers dep is RENAMED to `cov_helpers` in Cargo.toml | //! | `convert!` `.input(from!)`/`.output(into!)` | `Celsius` ⇄ `Int` via `From`/`Into` impls | @@ -54,7 +53,7 @@ //! | binding-local fn `fun!(crate::…)` `.sig(sig!)` as free fn | `describeSummary` ← `crate::summary_describe` | //! | binding-local fn as `.method()` / `.constructor()` | `Summary.mean()` ← `crate::summary_mean` (NO `.name` — derived by the strip hook); `Summary.fromMean` ← `crate::summary_from_mean` (FALLIBLE — sig `Result` → `onError`) | //! | `Result<_, E>` → typed domain `onError` | `storage_try_with_label` | -//! | two-caller split (#45): `onBindingError` + `onError` on one fallible wrapper | `storage_try_from_stamp` (malformed `Stamp` → binding; bad `secs` → domain) | +//! | two-caller split (#45): `onBindingError` + `onError` on one fallible wrapper | `storage_try_from_stamp` (wrong-length `tag` → binding; bad `secs` → domain) | //! | fixed-width unsigned scalars (#108) | `Unsigned` + direct/optional/callback/collection max-value round trips | //! | `Option` | `Option` (in + out) / `Option` / `Option` / `Option` (param + return + field) | //! | non-null enum field under nullable-context (#144) | `Option` → nested `RepliesConfig.priority` (single Elvis default) | @@ -66,7 +65,7 @@ //! | N-ary sorted handle locking | `storage_total_len` (3 handles) + a 4-thread smoke | //! | `Vec` return | `storage_labels` (single-leaf string fold) | //! | `String` return | `string_new` | -//! | binding-error channel (`JniErrorHandler`) | malformed `Stamp` bytes (value-blob length guard) | +//! | binding-error channel (`JniErrorHandler`) | wrong-length `[u8; 2]` (fixed-size array length guard) | //! | callback no-throw contract | a throwing `PayloadCallback` (described + cleared per upcall) | //! | `data_class` instance member | `Payload.labelLen()` (receiver crosses as `this` field leaves) | //! | `JniGen::ignore` (exact) | `string_len` / `storage_put_by_read_and_update` (acknowledged-unbound, no skip warnings) | @@ -99,7 +98,7 @@ use prebindgen::{ constant, convert, core::Registry, data_class, enum_class, expand_param, expand_return, expr, from, fun, into, lang::JniGen, matching, package, path, ptr_class, sealed_class, sig, try_from, - ty, value_class, variant, + ty, variant, }; fn strip_flat_class_prefix(class: &str, name: &str) -> String { @@ -286,14 +285,22 @@ fn main() { // Fixed-width unsigned mappings: Int / Long widening plus // ULong over a raw jlong bit pattern. .class(data_class!(Unsigned)) - // `Stamp` as a `@JvmInline value class` over its raw bytes; its readers - // become instance methods (`secs()` / `nanos()`), and `Vec` - // surfaces as `List`. + // `Stamp` is a small `Copy` struct of two scalars, so it crosses + // as its FIELDS — no array, no raw-memory image. Its readers stay + // instance methods (`secs()` / `nanos()`) whose receiver crosses + // as those field leaves, and `Vec` surfaces as + // `List`. .class( - value_class!(Stamp) + data_class!(Stamp) .method(fun!(stamp_secs)) .method(fun!(stamp_nanos)), - ), + ) + // `BlobValue` is the array-backed EQUALITY probe: a raw-bytes + // field beside a scalar, plus a nested data class. Both compare + // by identity in Kotlin unless the binding says otherwise. + .class(data_class!(BlobValue).jobject_input()) + // Fixed-size arrays of every JNI-primitive element. + .class(data_class!(Arrays)), ) // ── Subpackage `errors`: the Result error channel ─────────────────── .package(package!("errors").class( @@ -501,6 +508,9 @@ fn main() { .fun(fun!(unsigned_data_maybe)) .fun(fun!(unsigned_emit)) .fun(fun!(unsigned_series)) + .fun(fun!(blob_value_new)) + .fun(fun!(blob_value_echo)) + .fun(fun!(arrays_echo)) .fun(fun!(duration_optional)) .fun(fun!(duration_boundary_echo)) // The converted analogue of `unsigned_emit`: a whole-value diff --git a/examples/covertest-kotlin/kotlin/REPORT.md b/examples/covertest-kotlin/kotlin/REPORT.md index 818a10d1..bd25b651 100644 --- a/examples/covertest-kotlin/kotlin/REPORT.md +++ b/examples/covertest-kotlin/kotlin/REPORT.md @@ -57,6 +57,12 @@ Base package: `io.prebindgen.covertest` - `archive_reading_maybe` — `fun archiveReadingMaybe(a: SummaryVault, onError: JniErrorHandler): Reading?` - shaped by: return `Reading` decomposed → [tag, exact_v0, range_low, range_high, tagged_v0, tagged_v1, companion_v0] (Callback delivery) - `archive_set_reading` — `fun archiveSetReading(a: SummaryVault, which: Int, onError: JniErrorHandler)` +- `arrays_echo` — `fun arraysEcho(a: Arrays, onError: JniErrorHandler): Arrays` + - shaped by: return `Arrays` decomposed → [bytes, shorts, ints, longs, doubles, flags, raw] (Callback delivery) +- `blob_value_echo` — `fun blobValueEcho(value: BlobValue, onError: JniErrorHandler): BlobValue` + - shaped by: return `BlobValue` decomposed → [stamp__secs, stamp__nanos, id, chunks] (Callback delivery) +- `blob_value_new` — `fun blobValueNew(secs: Long, id: ByteArray, chunks: List, onError: JniErrorHandler): BlobValue` + - shaped by: return `BlobValue` decomposed → [stamp__secs, stamp__nanos, id, chunks] (Callback delivery) - `cache_config_weight` — `fun cacheConfigWeight(cache: CacheConfig?, onError: JniErrorHandler): Int` - `celsius_double` — `fun celsiusDouble(c: Int, onError: JniErrorHandler): Int` - `duration_boundary_echo` — `fun durationBoundaryEcho(value: DurationBoundary, onError: JniErrorHandler): DurationBoundary` @@ -91,8 +97,9 @@ Base package: `io.prebindgen.covertest` - `reading_series` — `fun readingSeries(n: Int, onError: JniErrorHandler>): List` - shaped by: return `Reading` decomposed → [tag, exact_v0, range_low, range_high, tagged_v0, tagged_v1, companion_v0] (Callback delivery) - `stamp_new` — `fun stampNew(secs: Long, nanos: Long, onError: JniErrorHandler): Stamp` + - shaped by: return `Stamp` decomposed → [secs, nanos] (Callback delivery) - `stamp_series` — `fun stampSeries(count: Long, onError: JniErrorHandler>): List` - - shaped by: return `Stamp` decomposed → [] (Callback delivery) + - shaped by: return `Stamp` decomposed → [secs, nanos] (Callback delivery) - `tagged_new` — `fun taggedNew(which: Int, onError: JniErrorHandler): Tagged` - `tagged_rank` — `fun taggedRank(t: Tagged, onError: JniErrorHandler): Int` - `unsigned_data_maybe` — `fun unsignedDataMaybe(value: Unsigned, onError: JniErrorHandler): ULong?` @@ -128,7 +135,7 @@ Base package: `io.prebindgen.covertest` - `storage_shards_opt` — `fun storageShardsOpt(count: Long, each: Long, onError: JniErrorHandler?>): List?` - shaped by: return `Storage` decomposed → [] (Callback delivery) - `storage_total_len` — `fun storageTotalLen(a: Storage, b: Storage, c: Storage, onError: JniErrorHandler): Long` -- `storage_try_from_stamp` — `fun storageTryFromStamp(s: Stamp, onBindingError: JniErrorHandler, onError: StorageErrorHandler): Storage` +- `storage_try_from_stamp` — `fun storageTryFromStamp(s: Stamp, tag: ByteArray, onBindingError: JniErrorHandler, onError: StorageErrorHandler): Storage` - shaped by: domain error `StorageError` decomposed → onError [message, handle] (binding failures → onBindingError) - `storage_try_with_label` — `fun storageTryWithLabel(label: String, onBindingError: JniErrorHandler, onError: StorageErrorHandler): Storage` - shaped by: domain error `StorageError` decomposed → onError [message, handle] (binding failures → onBindingError) @@ -142,7 +149,7 @@ Base package: `io.prebindgen.covertest` - `payload_label_len` — `fun labelLen(onError: JniErrorHandler): Long?` -## class `io.prebindgen.covertest.model.Stamp` (value_class, Rust `Stamp`) +## class `io.prebindgen.covertest.model.Stamp` (data_class, Rust `Stamp`) - `stamp_nanos` — `fun nanos(onError: JniErrorHandler): Long` - `stamp_secs` — `fun secs(onError: JniErrorHandler): Long` @@ -170,6 +177,8 @@ Base package: `io.prebindgen.covertest` - `Annotated`: data_class → `io.prebindgen.covertest.model.Annotated` (wire `jni :: objects :: JObject`) - `Archive`: ptr_class → `io.prebindgen.covertest.analytics.SummaryVault` (wire `jni :: sys :: jlong`) +- `Arrays`: data_class → `io.prebindgen.covertest.model.Arrays` (wire `jni :: objects :: JObject`) +- `BlobValue`: data_class → `io.prebindgen.covertest.model.BlobValue` (wire `jni :: objects :: JObject`, input `JObject` opt-in) - `CacheConfig`: data_class → `io.prebindgen.covertest.model.CacheConfig` (wire `jni :: objects :: JObject`) - `DurationBoundary`: data_class → `io.prebindgen.covertest.model.DurationBoundary` (wire `jni :: objects :: JObject`, input `JObject` opt-in) - `EscapeProbe`: ptr_class → `io.prebindgen.covertest.esc_pkg.Esc_Probe` (wire `jni :: sys :: jlong`) @@ -193,7 +202,7 @@ Base package: `io.prebindgen.covertest` - `Priority`: enum_class → `io.prebindgen.covertest.model.Priority` (wire `jni :: sys :: jint`) - `Reading`: sealed_class → `io.prebindgen.covertest.model.Reading` (wire `?`) - `RepliesConfig`: data_class → `io.prebindgen.covertest.model.RepliesConfig` (wire `jni :: objects :: JObject`) -- `Stamp`: value_class → `io.prebindgen.covertest.model.Stamp` (wire `jni :: objects :: JByteArray`) +- `Stamp`: data_class → `io.prebindgen.covertest.model.Stamp` (wire `jni :: objects :: JObject`) - `Storage`: ptr_class → `io.prebindgen.covertest.Storage` (wire `jni :: sys :: jlong`) - `StorageError`: ptr_class → `io.prebindgen.covertest.errors.StorageError` (wire `jni :: sys :: jlong`) - `StorageHandler`: ptr_class → `io.prebindgen.covertest.StorageHandler` (wire `jni :: sys :: jlong`) diff --git a/examples/covertest-kotlin/kotlin/generated/io/prebindgen/covertest.kt b/examples/covertest-kotlin/kotlin/generated/io/prebindgen/covertest.kt index 1b637820..03f4a89c 100644 --- a/examples/covertest-kotlin/kotlin/generated/io/prebindgen/covertest.kt +++ b/examples/covertest-kotlin/kotlin/generated/io/prebindgen/covertest.kt @@ -2,6 +2,7 @@ package io.prebindgen.covertest import io.prebindgen.covertest.model.Annotated +import io.prebindgen.covertest.model.BlobValue import io.prebindgen.covertest.model.DurationBoundary import io.prebindgen.covertest.model.Hold import io.prebindgen.covertest.model.HoldPolicy @@ -700,6 +701,28 @@ internal object CovNative { errorSink: Any, ) + external fun arraysEcho( + aBytes: ByteArray, + aShorts: ShortArray, + aInts: IntArray, + aLongs: LongArray, + aDoubles: DoubleArray, + aFlags: BooleanArray, + aRaw: LongArray, + build: Any, + errorSink: Any, + ): Any? + + external fun blobValueEcho(value: BlobValue, build: Any, errorSink: Any): Any? + + external fun blobValueNew( + secs: Long, + id: ByteArray, + chunks: List, + build: Any, + errorSink: Any, + ): Any? + external fun cacheConfigWeight( cachePresent: Boolean, cacheRepliesPriority: Int, @@ -809,11 +832,11 @@ internal object CovNative { external fun readingSeries(n: Int, acc: Any?, fold: Any, errorSink: Any): Any? - external fun stampNanos(s: ByteArray, errorSink: Any): Long + external fun stampNanos(sSecs: Long, sNanos: Long, errorSink: Any): Long - external fun stampNew(secs: Long, nanos: Long, errorSink: Any): ByteArray + external fun stampNew(secs: Long, nanos: Long, build: Any, errorSink: Any): Any? - external fun stampSecs(s: ByteArray, errorSink: Any): Long + external fun stampSecs(sSecs: Long, sNanos: Long, errorSink: Any): Long external fun stampSeries(count: Long, acc: Any?, fold: Any, errorSink: Any): Any? @@ -914,7 +937,13 @@ internal object CovNative { external fun storageTotalLen(a: Long, b: Long, c: Long, errorSink: Any): Long - external fun storageTryFromStamp(s: ByteArray, errorSink: Any, domainSink: Any): Long + external fun storageTryFromStamp( + sSecs: Long, + sNanos: Long, + tag: ByteArray, + errorSink: Any, + domainSink: Any, + ): Long external fun storageTryWithLabel(label: String, errorSink: Any, domainSink: Any): Long diff --git a/examples/covertest-kotlin/kotlin/generated/io/prebindgen/covertest/model.kt b/examples/covertest-kotlin/kotlin/generated/io/prebindgen/covertest/model.kt index e74e2e50..fdcb894c 100644 --- a/examples/covertest-kotlin/kotlin/generated/io/prebindgen/covertest/model.kt +++ b/examples/covertest-kotlin/kotlin/generated/io/prebindgen/covertest/model.kt @@ -210,6 +210,97 @@ public data class Annotated(val payload: Payload, val alternate: Payload?, val t } } +/** + * Fixed-size arrays of every JNI-primitive element type. + * + * Each crosses as the matching Kotlin primitive array — bulk-copied, nothing + * boxed — rather than through the `Vec` -> `List` path. The wider + * unsigned field (`raw`) pins the raw-bit-pattern rule: `[u64; N]` carries its + * bits in a `LongArray`, exactly as a scalar `u64` crosses as a raw `jlong`. + * + * `flags` is the one element type that is NOT a cast: a `jboolean` is a `u8`, + * and reinterpreting an out-of-range byte as a Rust `bool` would be undefined + * behavior, so the decode normalizes instead. + */ +public data class Arrays(val bytes: ByteArray, val shorts: ShortArray, val ints: IntArray, val longs: LongArray, val doubles: DoubleArray, val flags: BooleanArray, val raw: LongArray) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Arrays) return false + return bytes.contentEquals(other.bytes) && shorts.contentEquals(other.shorts) && ints.contentEquals(other.ints) && longs.contentEquals(other.longs) && doubles.contentEquals(other.doubles) && flags.contentEquals(other.flags) && raw.contentEquals(other.raw) + } + + override fun hashCode(): Int { + var result = bytes.contentHashCode() + result = 31 * result + shorts.contentHashCode() + result = 31 * result + ints.contentHashCode() + result = 31 * result + longs.contentHashCode() + result = 31 * result + doubles.contentHashCode() + result = 31 * result + flags.contentHashCode() + result = 31 * result + raw.contentHashCode() + return result + } + + override fun toString(): String = "Arrays(bytes=${bytes.contentToString()}, shorts=${shorts.contentToString()}, ints=${ints.contentToString()}, longs=${longs.contentToString()}, doubles=${doubles.contentToString()}, flags=${flags.contentToString()}, raw=${raw.contentToString()})" + + public companion object { + @JvmStatic + public fun fromParts( + bytes: ByteArray, + shorts: ShortArray, + ints: IntArray, + longs: LongArray, + doubles: DoubleArray, + flags: BooleanArray, + raw: LongArray, + ): Arrays = Arrays(bytes, shorts, ints, longs, doubles, flags, raw) + } +} + +/** + * A value whose equality is **array-backed** on the JVM side: a byte-array + * field beside a nested data class. + * + * Kotlin arrays compare by identity, so the `Vec` field would make two + * equal-content values compare unequal unless the binding emits content-based + * operators. This mirrors the shape that broke downstream (a `Vec` struct + * field), which nothing else here exercised. Two fields are enough: `id` is + * array-backed and the nested [`Stamp`] — which itself crosses as its scalar + * fields — is not, so both comparison branches are covered, and a third of + * either kind would only repeat an emitted form. + * + * Field ORDER is deliberate: the array-backed fields come after `stamp`, so + * the generated `hashCode` folds them as `31 * result + id.contentHashCode()` + * rather than seeding the accumulator with them. That is the shape a real + * value takes (`Timestamp(ntp64, id)`), and it is a different emitted form + * from the array-first one. + */ +public data class BlobValue(val stamp: Stamp, val id: ByteArray, val chunks: List) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is BlobValue) return false + return stamp == other.stamp && id.contentEquals(other.id) && (chunks.size == other.chunks.size && chunks.indices.all { __i -> val __x = chunks[__i]; val __y = other.chunks[__i]; __x.contentEquals(__y) }) + } + + override fun hashCode(): Int { + var result = stamp.hashCode() + result = 31 * result + id.contentHashCode() + result = 31 * result + (chunks.fold(1) { __acc, __e -> 31 * __acc + __e.contentHashCode() }) + return result + } + + override fun toString(): String = "BlobValue(stamp=${stamp}, id=${id.contentToString()}, chunks=${chunks.joinToString(", ", "[", "]") { __e -> "${__e.contentToString()}" }})" + + public companion object { + @JvmStatic + public fun fromParts( + stamp_secs: Long, + stamp_nanos: Long, + id: ByteArray, + chunks: List, + ): BlobValue = BlobValue(Stamp.fromParts(stamp_secs, stamp_nanos), id, chunks) + } +} + /** * Outer cache config crossed as `Option`. Its optional-ness * propagates into the non-optional nested [`RepliesConfig`], whose non-null @@ -713,6 +804,34 @@ public data class RepliesConfig(val priority: Priority, val maxSamples: Long) { } } +/** + * A plain `Copy` timestamp. Declared `data_class` in the binding, so it + * crosses **by value as its two scalar fields** (no heap handle, no + * `close()`), and `Vec` surfaces as `List`. + */ +public data class Stamp(val secs: Long, val nanos: Long) { + /** Seconds component (data-class **accessor**, receiver = its field leaves). */ + public fun secs(onError: JniErrorHandler): Long { + val __bcap = JniErrorHandlerCapture.acquire() + val __ret = CovNative.stampSecs(this.secs, this.nanos, __bcap) + if (__bcap.failed) return onError.run(__bcap.ze0) + return __ret + } + + /** Nanoseconds component (data-class **accessor**). */ + public fun nanos(onError: JniErrorHandler): Long { + val __bcap = JniErrorHandlerCapture.acquire() + val __ret = CovNative.stampNanos(this.secs, this.nanos, __bcap) + if (__bcap.failed) return onError.run(__bcap.ze0) + return __ret + } + + public companion object { + @JvmStatic + public fun fromParts(secs: Long, nanos: Long): Stamp = Stamp(secs, nanos) + } +} + /** A data class carrying the object-shaped sum. */ public data class Tagged(val id: Long, val marker: Marker) { public companion object { @@ -739,33 +858,6 @@ public data class Unsigned(val byte: Int, val short: Int, val int: Long, val lon } } -/** - * A plain `Copy` timestamp. Declared `value_class` in the binding, so it - * crosses **by value as its raw bytes** in a `ByteArray` (no heap handle, no - * `close()`), and `Vec` surfaces as `List`. - * - * Typed by-value wrapper for the native Rust `Stamp` (a `Copy` blob carried - * as its raw bytes; `@JvmInline`-erased to `ByteArray` at the JNI boundary). - */ -@JvmInline -public value class Stamp(public val bytes: ByteArray) { - /** Seconds component (value-class **accessor**, receiver = the value bytes). */ - public fun secs(onError: JniErrorHandler): Long { - val __bcap = JniErrorHandlerCapture.acquire() - val __ret = CovNative.stampSecs(this.bytes, __bcap) - if (__bcap.failed) return onError.run(__bcap.ze0) - return __ret - } - - /** Nanoseconds component (value-class **accessor**). */ - public fun nanos(onError: JniErrorHandler): Long { - val __bcap = JniErrorHandlerCapture.acquire() - val __ret = CovNative.stampNanos(this.bytes, __bcap) - if (__bcap.failed) return onError.run(__bcap.ze0) - return __ret - } -} - public fun interface LookupCallback { public fun run(lookup: Lookup) } @@ -814,6 +906,28 @@ public fun ReadingCallback.asRaw(): ReadingCallbackRaw = ) } +public fun interface ArraysBuilder { + public fun run( + bytes: ByteArray, + shorts: ShortArray, + ints: IntArray, + longs: LongArray, + doubles: DoubleArray, + flags: BooleanArray, + raw: LongArray, + ): R +} + +internal val __ArraysBuilder: ArraysBuilder = +ArraysBuilder { bytes, shorts, ints, longs, doubles, flags, raw -> Arrays.fromParts(bytes, shorts, ints, longs, doubles, flags, raw) } + +public fun interface BlobValueBuilder { + public fun run(stamp__secs: Long, stamp__nanos: Long, id: ByteArray, chunks: List): R +} + +internal val __BlobValueBuilder: BlobValueBuilder = +BlobValueBuilder { stamp__secs, stamp__nanos, id, chunks -> BlobValue.fromParts(stamp__secs, stamp__nanos, id, chunks) } + public fun interface DurationBoundaryBuilderRaw { public fun run(required: Long, delay: Long): R } @@ -865,6 +979,13 @@ ReadingBuilder { tag, exact_v0, range_low, range_high, tagged_v0, tagged_v1, com when (tag) { 0 -> Reading.Missing; 1 -> Reading.Exact(exact_v0); 2 -> Reading.Range(range_low, range_high); 3 -> Reading.Tagged(tagged_v0!!, Priority.fromInt(tagged_v1)); 4 -> Reading.Companion(companion_v0); else -> throw IllegalArgumentException("Reading: invalid tag $tag") } } +public fun interface StampBuilder { + public fun run(secs: Long, nanos: Long): R +} + +internal val __StampBuilder: StampBuilder = +StampBuilder { secs, nanos -> Stamp.fromParts(secs, nanos) } + public fun interface UnsignedBuilderRaw { public fun run(byte: Int, short: Int, int: Long, long: Long, maybeLong: Long?): R } @@ -892,13 +1013,13 @@ internal object __ReadingFolderRawHolder { } public fun interface StampFolderRaw { - public fun run(acc: A, element: ByteArray): A + public fun run(acc: A, secs: Long, nanos: Long): A } internal object __StampFolderRawHolder { @JvmField val instance: StampFolderRaw> = - StampFolderRaw { acc, element -> acc.add(Stamp(element)); acc } + StampFolderRaw { acc, secs, nanos -> acc.add(Stamp.fromParts(secs, nanos)); acc } } /** Classify a payload by magnitude of its `value` (enum **return**). */ @@ -929,17 +1050,24 @@ public fun priorityOr( return io.prebindgen.covertest.model.Priority.fromInt(__ret) } -/** Build a [`Stamp`] (value-class **return**). */ +/** + * Build a [`Stamp`] (data-class **return**). + * + * The Rust `Stamp` result is delivered decomposed: the builder callback receives (`secs`, `nanos`). + */ +@Suppress("UNCHECKED_CAST") public fun stampNew(secs: Long, nanos: Long, onError: JniErrorHandler): Stamp { val __bcap = JniErrorHandlerCapture.acquire() - val __ret = CovNative.stampNew(secs, nanos, __bcap) + val __ret = CovNative.stampNew(secs, nanos, __StampBuilder, __bcap) if (__bcap.failed) return onError.run(__bcap.ze0) - return Stamp(__ret) + return __ret as Stamp } /** - * A monotonically increasing run of stamps (`Vec` → - * `List`). + * A monotonically increasing run of stamps (`Vec` → + * `List`). + * + * The Rust `Stamp` result is delivered decomposed: the builder callback receives (`secs`, `nanos`). */ @Suppress("UNCHECKED_CAST") public fun stampSeries(count: Long, onError: JniErrorHandler>): List { @@ -1528,6 +1656,66 @@ public fun unsignedSeries(onError: JniErrorHandler>): List { return __ret as List } +/** + * Build a [`BlobValue`] (its equality is asserted from Kotlin). + * + * The Rust `BlobValue` result is delivered decomposed: the builder callback receives (`stamp__secs`, `stamp__nanos`, `id`, `chunks`). + */ +@Suppress("UNCHECKED_CAST") +public fun blobValueNew( + secs: Long, + id: ByteArray, + chunks: List, + onError: JniErrorHandler, +): BlobValue { + val __bcap = JniErrorHandlerCapture.acquire() + val __ret = CovNative.blobValueNew(secs, id, chunks, __BlobValueBuilder, __bcap) + if (__bcap.failed) return onError.run(__bcap.ze0) + return __ret as BlobValue +} + +/** + * Round-trip a [`BlobValue`] through the WHOLE-OBJECT input decoder. + * + * The binding marks this class `.jobject_input()`, so the decoder reads each + * field off the Kotlin object by JVM descriptor — including the nested + * [`Stamp`], whose slot is its own class rather than the scalar leaves it + * flattens to everywhere else. Getting that descriptor wrong is a + * `NoSuchFieldError` on the first decode. + * + * The Rust `BlobValue` result is delivered decomposed: the builder callback receives (`stamp__secs`, `stamp__nanos`, `id`, `chunks`). + */ +@Suppress("UNCHECKED_CAST") +public fun blobValueEcho(value: BlobValue, onError: JniErrorHandler): BlobValue { + val __bcap = JniErrorHandlerCapture.acquire() + val __ret = CovNative.blobValueEcho(value, __BlobValueBuilder, __bcap) + if (__bcap.failed) return onError.run(__bcap.ze0) + return __ret as BlobValue +} + +/** + * Round-trip every fixed-size array shape, both directions. + * + * The Rust `Arrays` result is delivered decomposed: the builder callback receives (`bytes`, `shorts`, `ints`, `longs`, `doubles`, `flags`, `raw`). + */ +@Suppress("UNCHECKED_CAST") +public fun arraysEcho(a: Arrays, onError: JniErrorHandler): Arrays { + val __bcap = JniErrorHandlerCapture.acquire() + val __ret = CovNative.arraysEcho( + a.bytes, + a.shorts, + a.ints, + a.longs, + a.doubles, + a.flags, + a.raw, + __ArraysBuilder, + __bcap, + ) + if (__bcap.failed) return onError.run(__bcap.ze0) + return __ret as Arrays +} + /** * Round-trip an optional standard-library duration. The source API remains * semantic (`Option`); only the binding declares its millisecond diff --git a/examples/covertest-kotlin/kotlin/generated/io/prebindgen/covertest/storage.kt b/examples/covertest-kotlin/kotlin/generated/io/prebindgen/covertest/storage.kt index d9c73268..a8dceed4 100644 --- a/examples/covertest-kotlin/kotlin/generated/io/prebindgen/covertest/storage.kt +++ b/examples/covertest-kotlin/kotlin/generated/io/prebindgen/covertest/storage.kt @@ -252,9 +252,11 @@ public fun storageTryWithLabel( /** * Build a storage stamped with `s`, **failing** on a non-positive `secs` (a - * domain [`StorageError`]). This takes a `Stamp` **by value** (a value-blob - * input), so a malformed `Stamp` blob fails the input decode FIRST — the - * binding channel — while a well-formed but rejected value fails in the domain + * domain [`StorageError`]). + * + * `tag` is a fixed-size array purely so the two error channels stay separately + * provable: a wrong-length array fails the input DECODE first — the binding + * channel — while a well-formed but rejected `secs` fails in the domain * channel. It is the covertest exercise for issue #45's two-caller split: one * wrapper, both `onBindingError` and `onError` provable independently. * @@ -262,12 +264,13 @@ public fun storageTryWithLabel( */ public fun storageTryFromStamp( s: Stamp, + tag: ByteArray, onBindingError: JniErrorHandler, onError: StorageErrorHandler, ): Storage { val __bcap = JniErrorHandlerCapture.acquire() val __dcap = StorageErrorHandlerRawCapture.acquire() - val __ret = CovNative.storageTryFromStamp(s.bytes, __bcap, __dcap) + val __ret = CovNative.storageTryFromStamp(s.secs, s.nanos, tag, __bcap, __dcap) if (__bcap.failed) return onBindingError.run(__bcap.ze0) if (__dcap.failed) return onError.run(__dcap.ze0!!, StorageError(__dcap.ze1!!)) return Storage(__ret) diff --git a/examples/covertest-kotlin/kotlin/io/prebindgen/covertest/Test.kt b/examples/covertest-kotlin/kotlin/io/prebindgen/covertest/Test.kt index aec0a9a4..f6bd381f 100644 --- a/examples/covertest-kotlin/kotlin/io/prebindgen/covertest/Test.kt +++ b/examples/covertest-kotlin/kotlin/io/prebindgen/covertest/Test.kt @@ -21,6 +21,7 @@ import io.prebindgen.covertest.analytics.summaryTotalRaw import io.prebindgen.covertest.errors.StorageErrorHandler import io.prebindgen.covertest.esc_pkg.Esc_Probe import io.prebindgen.covertest.model.Annotated +import io.prebindgen.covertest.model.Arrays import io.prebindgen.covertest.model.CacheConfig import io.prebindgen.covertest.model.RepliesConfig import io.prebindgen.covertest.model.DurationBoundary @@ -41,6 +42,9 @@ import io.prebindgen.covertest.model.Reading import io.prebindgen.covertest.model.Stamp import io.prebindgen.covertest.model.Unsigned import io.prebindgen.covertest.model.annotatedNew +import io.prebindgen.covertest.model.arraysEcho +import io.prebindgen.covertest.model.blobValueEcho +import io.prebindgen.covertest.model.blobValueNew import io.prebindgen.covertest.model.annotatedAlternateValue import io.prebindgen.covertest.model.celsiusDouble import io.prebindgen.covertest.model.durationOptional @@ -654,6 +658,107 @@ fun main() { check(stampSeries(0L, boom).isEmpty()) } + // ── array-backed VALUE EQUALITY ───────────────────────────────────────── + // The Rust types derive `Eq`, so the Kotlin mirrors must compare by + // content. Kotlin arrays compare by IDENTITY, which silently breaks that + // for every `ByteArray`-backed property — a value blob's `bytes`, a + // `Vec` field, and any value carrying one of those. Kotlin's own + // `data class` codegen is inconsistent here (its `hashCode`/`toString` DO + // special-case arrays, its `equals` does not), so `==` is the assertion + // that matters and `hashCode` alone would not have caught the defect. + section("array-backed value equality (content, not identity)") { + // The NEGATIVE case first: a data class with no array property must keep + // the compiler's own equality. The generator emits nothing for it, so + // this pins that the content operators do not churn ordinary classes. + val s1 = stampNew(7L, 42L, boom) + val s2 = stampNew(7L, 42L, boom) + check(s1 == s2) { "scalar data class must compare by value: $s1 vs $s2" } + check(s1.hashCode() == s2.hashCode()) + check(stampNew(8L, 42L, boom) != s1) { "different content must not compare equal" } + check(hashSetOf(s1, s2).size == 1) + check(s1.toString() == "Stamp(secs=7, nanos=42)") { "got $s1" } + + // A data class with a DIRECT `ByteArray` field plus a NESTED value + // blob — the two shapes that broke downstream. The bytes sit LAST, so + // this also covers the `31 * result + …contentHashCode()` fold form + // that a real value (`Timestamp(ntp64, id)`) produces. + fun blob(secs: Long, id: ByteArray, chunks: List) = + blobValueNew(secs, id, chunks, boom) + + val chunks = listOf(byteArrayOf(9), byteArrayOf(8, 7)) + val b1 = blob(7L, byteArrayOf(1, 2, 3), chunks) + val b2 = blob(7L, byteArrayOf(1, 2, 3), listOf(byteArrayOf(9), byteArrayOf(8, 7))) + check(b1 == b2) { "array-backed data class must compare by content: $b1 vs $b2" } + check(b1.hashCode() == b2.hashCode()) + check(hashSetOf(b1, b2).size == 1) + // Every component must actually participate — a comparison that ignored + // any of them would still pass the equality checks above. + check(blob(7L, byteArrayOf(1, 2, 4), chunks) != b1) { "id must matter" } + check(blob(8L, byteArrayOf(1, 2, 3), chunks) != b1) { "nested blob must matter" } + // A CONTAINER of arrays: `List` inherits `ByteArray`'s + // identity equality, so the operators must dig through the container. + check(blob(7L, byteArrayOf(1, 2, 3), listOf(byteArrayOf(9), byteArrayOf(8, 6))) != b1) { + "chunk contents must matter" + } + check(blob(7L, byteArrayOf(1, 2, 3), listOf(byteArrayOf(9))) != b1) { + "chunk count must matter" + } + check(b1.toString().contains("id=[1, 2, 3]")) { "toString must render bytes, got $b1" } + check(b1.toString().contains("chunks=[[9], [8, 7]]")) { + "toString must render nested bytes, got $b1" + } + + // ── fixed-size arrays -> Kotlin primitive arrays ───────────────────── + // Every `[T; N]` crosses as the matching primitive array (bulk-copied, + // nothing boxed), and every one of them compares by IDENTITY in Kotlin + // — so each needs the content operators, not just the byte case. + val a1 = Arrays( + byteArrayOf(1, 2, 3, 4), + shortArrayOf(-1, 2), + intArrayOf(3, -4, 5), + longArrayOf(6, -7), + doubleArrayOf(0.5, -1.25), + booleanArrayOf(true, false, true), + longArrayOf(-1L, 0L), // [u64; 2] carries raw bits: -1L == u64::MAX + ) + val a2 = arraysEcho(a1, boom) + check(a2 == a1) { "fixed-size arrays must round-trip by content: $a2 vs $a1" } + check(a2.hashCode() == a1.hashCode()) + check(hashSetOf(a1, a2).size == 1) + // The round trip must preserve VALUES, not just shapes — a per-element + // cast error would survive an equality-only check between two echoes. + check(a2.bytes.contentEquals(byteArrayOf(1, 2, 3, 4))) + check(a2.shorts.contentEquals(shortArrayOf(-1, 2))) + check(a2.ints.contentEquals(intArrayOf(3, -4, 5))) + check(a2.longs.contentEquals(longArrayOf(6, -7))) + check(a2.doubles.contentEquals(doubleArrayOf(0.5, -1.25))) + check(a2.flags.contentEquals(booleanArrayOf(true, false, true))) + // `u64::MAX` survives as the raw bit pattern rather than saturating. + check(a2.raw.contentEquals(longArrayOf(-1L, 0L))) { "raw bits: ${a2.raw.toList()}" } + // Each component participates in equality. + check(arraysEcho(a1.copy(ints = intArrayOf(3, -4, 6)), boom) != a1) { "ints must matter" } + check(arraysEcho(a1.copy(flags = booleanArrayOf(true, true, true)), boom) != a1) { + "flags must matter" + } + check(a1.toString().contains("flags=[true, false, true]")) { "got $a1" } + + // Wrong length is a BINDING ERROR, not a panic — the decode's `try_into` + // is the length check (the fixed-size-array successor to the value + // blob's byte-length guard). + var lenErr: String? = null + arraysEcho(a1.copy(ints = intArrayOf(1, 2))) { je -> lenErr = je; a1 } + check(lenErr?.contains("fixed-size array decode") == true) { + "wrong-length array must report a binding error, got: $lenErr" + } + + // WHOLE-OBJECT input decode (`.jobject_input()`): the decoder reads each + // field off the Kotlin object by JVM descriptor. A value-blob field's + // slot is the wrapper class, not `[B` — reading the old descriptor threw + // `NoSuchFieldError` on the first decode. + check(blobValueEcho(b1, boom) == b1) { "jobject-input round trip must preserve the value" } + check(blobValueEcho(blob(0L, ByteArray(0), emptyList()), boom).chunks.isEmpty()) + } + // ── Option nullable primitive return + data_class instance // member (I5): the receiver crosses as `this`'s field leaves ──────────── section("Option Payload.labelLen") { @@ -974,7 +1079,7 @@ fun main() { // ── #45: both channels of ONE fallible wrapper, each fires independently ── section("two-caller split storageTryFromStamp") { // Happy path: neither channel fires. - val ok = storageTryFromStamp(stampNew(5L, 0L, boom), boom, boomStorage) + val ok = storageTryFromStamp(stampNew(5L, 0L, boom), byteArrayOf(1, 2), boom, boomStorage) check(ok.len(boom) == 1L) ok.close() @@ -983,6 +1088,7 @@ fun main() { var domainMsg: String? = null val domainRet = storageTryFromStamp( stampNew(-1L, 0L, boom), + byteArrayOf(1, 2), JniErrorHandler { je -> throw AssertionError("binding channel must not fire on a domain error: $je") }, @@ -996,11 +1102,12 @@ fun main() { check(domainMsg == "stamp secs must be positive") { "domain onError did not fire: $domainMsg" } domainRet.close() - // BINDING error (malformed Stamp value-blob): `onBindingError` fires, + // BINDING error (wrong-length `tag` array): `onBindingError` fires, // the domain `onError` must NOT. var bindingJe: String? = null val bindingRet = storageTryFromStamp( - Stamp(ByteArray(3)), // Stamp is 16 bytes; 3 must be rejected on decode + Stamp(1L, 0L), + byteArrayOf(1, 2, 3), // `tag` is [u8; 2]; 3 must be rejected on decode JniErrorHandler { je -> bindingJe = je storageNew(boom) @@ -1010,7 +1117,7 @@ fun main() { throw AssertionError("domain channel must not fire on a binding error") }, ) - check(bindingJe != null && bindingJe!!.contains("wrong byte length")) { + check(bindingJe != null && bindingJe!!.contains("fixed-size array decode")) { "binding onBindingError did not fire: $bindingJe" } bindingRet.close() @@ -1226,16 +1333,22 @@ fun main() { s.close() } - // ── binding error: je != null (value-blob length guard) ────────────────── - section("binding error je != null (malformed Stamp bytes)") { - val bogus = Stamp(ByteArray(3)) // Stamp is 16 bytes; 3 must be rejected + // ── binding error: je != null (fixed-size array length guard) ─────────── + section("binding error je != null (wrong-length fixed-size array)") { var je: String? = null - val fallback = bogus.secs(JniErrorHandler { e -> - je = e - -1L - }) - check(fallback == -1L) - check(je != null && je!!.contains("wrong byte length")) { "unexpected je: $je" } + val fallback = storageTryFromStamp( + stampNew(1L, 0L, boom), + byteArrayOf(1, 2, 3), // `tag` is [u8; 2]; 3 is rejected on decode + JniErrorHandler { e -> + je = e + storageNew(boom) + }, + StorageErrorHandler { _, handle -> + throw AssertionError("domain channel must not fire on a decode failure") + }, + ) + fallback.close() + check(je != null && je!!.contains("fixed-size array decode")) { "unexpected je: $je" } } // ── callback exceptions: swallowed per upcall (no-throw contract) ──────── diff --git a/examples/covertest-kotlin/src/generated_bindings.rs b/examples/covertest-kotlin/src/generated_bindings.rs index c20cb44e..a6edac35 100644 --- a/examples/covertest-kotlin/src/generated_bindings.rs +++ b/examples/covertest-kotlin/src/generated_bindings.rs @@ -293,10 +293,6 @@ pub(crate) unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_payloadVe let __vec = &mut *(handle as *mut Vec); __vec.push(__elem); } -const _: () = { - const fn __assert_copy() {} - __assert_copy::(); -}; #[no_mangle] #[allow(non_snake_case, unused_mut, unused_variables, dead_code)] pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_constGetCoverVersion<'a>( @@ -509,6 +505,131 @@ pub(crate) unsafe fn Archive_to_jlong_cd73502c<'a>( clippy::nonminimal_bool, clippy::eq_op )] +pub(crate) unsafe fn Arrays_to_JObject_71120c08<'a>( + env: &mut jni::JNIEnv<'a>, + v: perftest_flat::Arrays, +) -> ::core::result::Result, __JniErr> { + Ok({ + let ___bytes: jni::objects::JObject = u8_4_to_JByteArray_39abedfa( + env, + v.bytes.clone(), + )? + .into(); + let ___shorts: jni::objects::JObject = i16_2_to_JShortArray_098f4ad5( + env, + v.shorts.clone(), + )? + .into(); + let ___ints: jni::objects::JObject = i32_3_to_JIntArray_60e5e35a( + env, + v.ints.clone(), + )? + .into(); + let ___longs: jni::objects::JObject = i64_2_to_JLongArray_73596912( + env, + v.longs.clone(), + )? + .into(); + let ___doubles: jni::objects::JObject = f64_2_to_JDoubleArray_dc30d1f9( + env, + v.doubles.clone(), + )? + .into(); + let ___flags: jni::objects::JObject = bool_3_to_JBooleanArray_3f960c58( + env, + v.flags.clone(), + )? + .into(); + let ___raw: jni::objects::JObject = u64_2_to_JLongArray_60bcc6a5( + env, + v.raw.clone(), + )? + .into(); + let __obj = env + .call_static_method( + "io/prebindgen/covertest/model/Arrays", + "fromParts", + "([B[S[I[J[D[Z[J)Lio/prebindgen/covertest/model/Arrays;", + &[ + jni::objects::JValue::Object(&___bytes), + jni::objects::JValue::Object(&___shorts), + jni::objects::JValue::Object(&___ints), + jni::objects::JValue::Object(&___longs), + jni::objects::JValue::Object(&___doubles), + jni::objects::JValue::Object(&___flags), + jni::objects::JValue::Object(&___raw), + ], + ) + .and_then(|__v| __v.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("encode struct via fromParts: {}", e)))?; + __obj + }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn BlobValue_to_JObject_89b5dab7<'a>( + env: &mut jni::JNIEnv<'a>, + v: perftest_flat::BlobValue, +) -> ::core::result::Result, __JniErr> { + Ok({ + let ___stamp_secs: jni::sys::jlong = i64_to_jlong_fbf9a9bc( + env, + v.stamp.secs.clone(), + )?; + let ___stamp_nanos: jni::sys::jlong = i64_to_jlong_fbf9a9bc( + env, + v.stamp.nanos.clone(), + )?; + let ___id: jni::objects::JObject = Vec_u8_to_JByteArray_7936d5de( + env, + v.id.clone(), + )? + .into(); + let ___chunks: jni::objects::JObject = Vec_Vec_u8_to_JObject_43404875( + env, + v.chunks.clone(), + )?; + let __obj = env + .call_static_method( + "io/prebindgen/covertest/model/BlobValue", + "fromParts", + "(JJ[BLjava/util/List;)Lio/prebindgen/covertest/model/BlobValue;", + &[ + jni::objects::JValue::from(___stamp_secs), + jni::objects::JValue::from(___stamp_nanos), + jni::objects::JValue::Object(&___id), + jni::objects::JValue::Object(&___chunks), + ], + ) + .and_then(|__v| __v.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("encode struct via fromParts: {}", e)))?; + __obj + }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] pub(crate) unsafe fn CacheConfig_to_JObject_db89a97c<'a>( env: &mut jni::JNIEnv<'a>, v: perftest_flat::CacheConfig, @@ -760,28 +881,40 @@ pub(crate) unsafe fn HoldPolicy_to_JObject_d2a5bcc4<'a>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JByteArray_to_Stamp_2fc9bd18<'env, 'v>( +pub(crate) unsafe fn JBooleanArray_to_bool_3_3f960c58<'env, 'v>( env: &mut jni::JNIEnv<'env>, - v: &jni::objects::JByteArray<'v>, -) -> ::core::result::Result { + v: &jni::objects::JBooleanArray<'v>, +) -> ::core::result::Result<[bool; 3], __JniErr> { Ok({ - let __bytes = env - .convert_byte_array(v) + let __len = env + .get_array_length(v) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array decode: {}", e)) + })? as usize; + let mut __buf: ::std::vec::Vec = ::std::vec![ + 0 as jni::sys::jboolean; __len + ]; + env.get_boolean_array_region(v, 0, &mut __buf) .map_err(|e| { <__JniErr as ::core::convert::From< String, - >>::from(format!("value-blob decode: {}", e)) + >>::from(format!("fixed-size array decode: {}", e)) })?; - if __bytes.len() != ::core::mem::size_of::() { - return ::core::result::Result::Err( + let __vals: ::std::vec::Vec = __buf.iter().map(|__x| *__x != 0).collect(); + let __arr: [bool; 3] = __vals + .as_slice() + .try_into() + .map_err(|_| { <__JniErr as ::core::convert::From< String, - >>::from("value-blob decode: wrong byte length".to_string()), - ); - } - unsafe { - ::core::ptr::read_unaligned(__bytes.as_ptr() as *const perftest_flat::Stamp) - } + >>::from( + "fixed-size array decode: `[bool ; 3]` expects a different length" + .to_string(), + ) + })?; + __arr }) } #[allow( @@ -795,57 +928,17 @@ pub(crate) unsafe fn JByteArray_to_Stamp_2fc9bd18<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_Annotated_b543f0d9<'env, 'v>( +pub(crate) unsafe fn JByteArray_to_Vec_u8_7936d5de<'env, 'v>( env: &mut jni::JNIEnv<'env>, - v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { + v: &jni::objects::JByteArray<'v>, +) -> ::core::result::Result, __JniErr> { Ok({ - let __payload_raw: jni::objects::JObject = env - .get_field(v, "payload", "Lio/prebindgen/covertest/Payload;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Annotated.payload: {}", e)))?; - let payload = JObject_to_Payload_98f64326(env, &__payload_raw)?; - let __alternate_raw: jni::objects::JObject = env - .get_field(v, "alternate", "Lio/prebindgen/covertest/Payload;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Annotated.alternate: {}", e)))?; - let alternate = JObject_to_Option_Payload_97036642(env, &__alternate_raw)?; - let __ttl_raw: jni::objects::JObject = env - .get_field(v, "ttl", "Ljava/lang/Long;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Annotated.ttl: {}", e)))?; - let ttl = JObject_to_Option_i64_2ba9a5ed(env, &__ttl_raw)?; - let __priority_jobj: jni::objects::JObject = env - .get_field(v, "priority", "Lio/prebindgen/covertest/model/Priority;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Annotated.priority: {}", e)))?; - let priority = if __priority_jobj.is_null() { - ::core::option::Option::None - } else { - let __priority_raw: jni::sys::jint = env - .call_method(&__priority_jobj, "getValue", "()I", &[]) - .and_then(|val| val.i()) - .map_err(|e| <__JniErr as ::core::convert::From< + env.convert_byte_array(v) + .map_err(|e| { + <__JniErr as ::core::convert::From< String, - >>::from(format!("Annotated.priority: {}", e)))?; - ::core::option::Option::Some( - jint_to_Priority_447102d2(env, &__priority_raw)?, - ) - }; - perftest_flat::Annotated { - payload, - alternate, - ttl, - priority, - } + >>::from(format!("decode_byte_array: {}", e)) + })? }) } #[allow( @@ -859,29 +952,30 @@ pub(crate) unsafe fn JObject_to_Annotated_b543f0d9<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_CacheConfig_db89a97c<'env, 'v>( +pub(crate) unsafe fn JByteArray_to_u8_2_9ca14e44<'env, 'v>( env: &mut jni::JNIEnv<'env>, - v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { + v: &jni::objects::JByteArray<'v>, +) -> ::core::result::Result<[u8; 2], __JniErr> { Ok({ - let __replies_raw: jni::objects::JObject = env - .get_field(v, "replies", "Lio/prebindgen/covertest/model/RepliesConfig;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("CacheConfig.replies: {}", e)))?; - let replies = JObject_to_RepliesConfig_eb8e9079(env, &__replies_raw)?; - let __ttl_raw: jni::sys::jlong = env - .get_field(v, "ttl", "J") - .and_then(|val| val.j()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("CacheConfig.ttl: {}", e)))? as _; - let ttl = jlong_to_i64_fbf9a9bc(env, &__ttl_raw)?; - perftest_flat::CacheConfig { - replies, - ttl, - } + let __buf = env + .convert_byte_array(v) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array decode: {}", e)) + })?; + let __arr: [u8; 2] = __buf + .as_slice() + .try_into() + .map_err(|_| { + <__JniErr as ::core::convert::From< + String, + >>::from( + "fixed-size array decode: `[u8 ; 2]` expects a different length" + .to_string(), + ) + })?; + __arr }) } #[allow( @@ -895,46 +989,30 @@ pub(crate) unsafe fn JObject_to_CacheConfig_db89a97c<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_DurationBoundary_9c5bf9bc<'env, 'v>( +pub(crate) unsafe fn JByteArray_to_u8_4_39abedfa<'env, 'v>( env: &mut jni::JNIEnv<'env>, - v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { + v: &jni::objects::JByteArray<'v>, +) -> ::core::result::Result<[u8; 4], __JniErr> { Ok({ - let __required_raw: jni::sys::jlong = env - .get_field(v, "required", "J") - .and_then(|val| val.j()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("DurationBoundary.required: {}", e)))?; - let required = { - let required_s0 = jlong_to_u64_4384a5d6(env, &__required_raw)?; - let required_s1 = u64_to_Duration_7c0845f9(env, required_s0) - .map_err(|__e| <__JniErr as ::core::convert::From< + let __buf = env + .convert_byte_array(v) + .map_err(|e| { + <__JniErr as ::core::convert::From< String, - >>::from(__e.to_string()))?; - required_s1 - }; - let __delay_jobj: jni::objects::JObject = env - .get_field(v, "delay", "Lkotlin/ULong;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("DurationBoundary.delay: {}", e)))?; - let delay = if __delay_jobj.is_null() { - ::core::option::Option::None - } else { - let __delay_raw: jni::sys::jlong = env - .call_method(&__delay_jobj, "unbox-impl", "()J", &[]) - .and_then(|val| val.j()) - .map_err(|e| <__JniErr as ::core::convert::From< + >>::from(format!("fixed-size array decode: {}", e)) + })?; + let __arr: [u8; 4] = __buf + .as_slice() + .try_into() + .map_err(|_| { + <__JniErr as ::core::convert::From< String, - >>::from(format!("DurationBoundary.delay: {}", e)))?; - jlong_to_Option_Duration_1cfa4d44(env, &__delay_raw)? - }; - perftest_flat::DurationBoundary { - required, - delay, - } + >>::from( + "fixed-size array decode: `[u8 ; 4]` expects a different length" + .to_string(), + ) + })?; + __arr }) } #[allow( @@ -948,33 +1026,44 @@ pub(crate) unsafe fn JObject_to_DurationBoundary_9c5bf9bc<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_HoldPolicy_d2a5bcc4<'env, 'v>( +pub(crate) unsafe fn JDoubleArray_to_f64_2_dc30d1f9<'env, 'v>( env: &mut jni::JNIEnv<'env>, - v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { + v: &jni::objects::JDoubleArray<'v>, +) -> ::core::result::Result<[f64; 2], __JniErr> { Ok({ - let __hold_raw: jni::objects::JObject = env - .get_field(v, "hold", "Lio/prebindgen/covertest/model/Hold;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("HoldPolicy.hold: {}", e)))?; - let hold = JObject_to_Hold_5f85caaf(env, &__hold_raw)?; - let __grace_raw: jni::objects::JObject = env - .get_field(v, "grace", "Lio/prebindgen/covertest/model/Hold;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("HoldPolicy.grace: {}", e)))?; - let grace = JObject_to_Option_Hold_230d7f9b(env, &__grace_raw)?; - perftest_flat::HoldPolicy { - hold, - grace, - } - }) -} -#[allow( - non_snake_case, + let __len = env + .get_array_length(v) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array decode: {}", e)) + })? as usize; + let mut __buf: ::std::vec::Vec = ::std::vec![ + 0 as jni::sys::jdouble; __len + ]; + env.get_double_array_region(v, 0, &mut __buf) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array decode: {}", e)) + })?; + let __vals: ::std::vec::Vec = __buf.iter().map(|__x| *__x as f64).collect(); + let __arr: [f64; 2] = __vals + .as_slice() + .try_into() + .map_err(|_| { + <__JniErr as ::core::convert::From< + String, + >>::from( + "fixed-size array decode: `[f64 ; 2]` expects a different length" + .to_string(), + ) + })?; + __arr + }) +} +#[allow( + non_snake_case, unused_mut, unused_variables, unused_braces, @@ -984,66 +1073,40 @@ pub(crate) unsafe fn JObject_to_HoldPolicy_d2a5bcc4<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_Hold_5f85caaf<'env, 'v>( +pub(crate) unsafe fn JIntArray_to_i32_3_60e5e35a<'env, 'v>( env: &mut jni::JNIEnv<'env>, - v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { + v: &jni::objects::JIntArray<'v>, +) -> ::core::result::Result<[i32; 3], __JniErr> { Ok({ - let __obj = v; - (|| -> ::core::result::Result { - if __obj.is_null() { - return ::core::result::Result::Err( - <__JniErr as ::core::convert::From< - String, - >>::from("Hold: null value where a variant was required".to_string()), - ); - } - if env - .is_instance_of(__obj, "io/prebindgen/covertest/model/Hold$Indefinite") - .map_err(|e| <__JniErr as ::core::convert::From< + let __len = env + .get_array_length(v) + .map_err(|e| { + <__JniErr as ::core::convert::From< String, - >>::from( - format!( - concat!("Hold", ": instanceof ", - "io/prebindgen/covertest/model/Hold$Indefinite", ": {}"), e - ), - ))? - { - return ::core::result::Result::Ok(perftest_flat::Hold::Indefinite); - } - if env - .is_instance_of(__obj, "io/prebindgen/covertest/model/Hold$For") - .map_err(|e| <__JniErr as ::core::convert::From< + >>::from(format!("fixed-size array decode: {}", e)) + })? as usize; + let mut __buf: ::std::vec::Vec = ::std::vec![ + 0 as jni::sys::jint; __len + ]; + env.get_int_array_region(v, 0, &mut __buf) + .map_err(|e| { + <__JniErr as ::core::convert::From< String, - >>::from( - format!( - concat!("Hold", ": instanceof ", - "io/prebindgen/covertest/model/Hold$For", ": {}"), e - ), - ))? - { - let __p_v0_raw: jni::sys::jlong = env - .get_field(__obj, "v0", "J") - .and_then(|val| val.j()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Hold.For.v0: {}", e)))? as _; - let __p_v0 = { - let __p_v0_s0 = jlong_to_u64_4384a5d6(env, &__p_v0_raw)?; - let __p_v0_s1 = u64_to_Duration_7c0845f9(env, __p_v0_s0) - .map_err(|__e| <__JniErr as ::core::convert::From< - String, - >>::from(__e.to_string()))?; - __p_v0_s1 - }; - return ::core::result::Result::Ok(perftest_flat::Hold::For(__p_v0)); - } - ::core::result::Result::Err( + >>::from(format!("fixed-size array decode: {}", e)) + })?; + let __vals: ::std::vec::Vec = __buf.iter().map(|__x| *__x as i32).collect(); + let __arr: [i32; 3] = __vals + .as_slice() + .try_into() + .map_err(|_| { <__JniErr as ::core::convert::From< String, - >>::from("Hold: value is not one of its declared variants".to_string()), - ) - })()? + >>::from( + "fixed-size array decode: `[i32 ; 3]` expects a different length" + .to_string(), + ) + })?; + __arr }) } #[allow( @@ -1057,104 +1120,40 @@ pub(crate) unsafe fn JObject_to_Hold_5f85caaf<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_Lookup_94ada15e<'env, 'v>( +pub(crate) unsafe fn JLongArray_to_i64_2_73596912<'env, 'v>( env: &mut jni::JNIEnv<'env>, - v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { + v: &jni::objects::JLongArray<'v>, +) -> ::core::result::Result<[i64; 2], __JniErr> { Ok({ - let __obj = v; - (|| -> ::core::result::Result { - if __obj.is_null() { - return ::core::result::Result::Err( - <__JniErr as ::core::convert::From< - String, - >>::from( - "Lookup: null value where a variant was required".to_string(), - ), - ); - } - if env - .is_instance_of(__obj, "io/prebindgen/covertest/model/Lookup$Absent") - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from( - format!( - concat!("Lookup", ": instanceof ", - "io/prebindgen/covertest/model/Lookup$Absent", ": {}"), e - ), - ))? - { - return ::core::result::Result::Ok(perftest_flat::Lookup::Absent); - } - if env - .is_instance_of(__obj, "io/prebindgen/covertest/model/Lookup$Found") - .map_err(|e| <__JniErr as ::core::convert::From< + let __len = env + .get_array_length(v) + .map_err(|e| { + <__JniErr as ::core::convert::From< String, - >>::from( - format!( - concat!("Lookup", ": instanceof ", - "io/prebindgen/covertest/model/Lookup$Found", ": {}"), e - ), - ))? - { - let __p_v0_obj: jni::objects::JObject = env - .get_field( - __obj, - "v0", - "Lio/prebindgen/covertest/analytics/Summary;", - ) - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Lookup.Found.v0: {}", e)))?; - let __p_v0_raw: jni::sys::jlong = if __p_v0_obj.is_null() { - 0 - } else { - env.call_method(&__p_v0_obj, "peek", "()J", &[]) - .and_then(|val| val.j()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Lookup.Found.v0: {}", e)))? - }; - if __p_v0_raw == 0 || (__p_v0_raw & 1) == 1 { - return ::core::result::Result::Err( - <__JniErr as ::core::convert::From< - String, - >>::from("Operation on a closed native handle.".to_string()), - ); - } - let __p_v0: perftest_flat::Summary = unsafe { - *std::boxed::Box::from_raw(__p_v0_raw as *mut perftest_flat::Summary) - }; - return ::core::result::Result::Ok(perftest_flat::Lookup::Found(__p_v0)); - } - if env - .is_instance_of(__obj, "io/prebindgen/covertest/model/Lookup$Failed") - .map_err(|e| <__JniErr as ::core::convert::From< + >>::from(format!("fixed-size array decode: {}", e)) + })? as usize; + let mut __buf: ::std::vec::Vec = ::std::vec![ + 0 as jni::sys::jlong; __len + ]; + env.get_long_array_region(v, 0, &mut __buf) + .map_err(|e| { + <__JniErr as ::core::convert::From< String, - >>::from( - format!( - concat!("Lookup", ": instanceof ", - "io/prebindgen/covertest/model/Lookup$Failed", ": {}"), e - ), - ))? - { - let __p_v0_obj: jni::objects::JObject = env - .get_field(__obj, "v0", "Ljava/lang/String;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Lookup.Failed.v0: {}", e)))?; - let __p_v0_raw: jni::objects::JString = __p_v0_obj.into(); - let __p_v0 = JString_to_String_c7f3ca43(env, &__p_v0_raw)?; - return ::core::result::Result::Ok(perftest_flat::Lookup::Failed(__p_v0)); - } - ::core::result::Result::Err( + >>::from(format!("fixed-size array decode: {}", e)) + })?; + let __vals: ::std::vec::Vec = __buf.iter().map(|__x| *__x as i64).collect(); + let __arr: [i64; 2] = __vals + .as_slice() + .try_into() + .map_err(|_| { <__JniErr as ::core::convert::From< String, - >>::from("Lookup: value is not one of its declared variants".to_string()), - ) - })()? + >>::from( + "fixed-size array decode: `[i64 ; 2]` expects a different length" + .to_string(), + ) + })?; + __arr }) } #[allow( @@ -1168,73 +1167,40 @@ pub(crate) unsafe fn JObject_to_Lookup_94ada15e<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_Marker_3dc81334<'env, 'v>( +pub(crate) unsafe fn JLongArray_to_u64_2_60bcc6a5<'env, 'v>( env: &mut jni::JNIEnv<'env>, - v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { + v: &jni::objects::JLongArray<'v>, +) -> ::core::result::Result<[u64; 2], __JniErr> { Ok({ - let __obj = v; - (|| -> ::core::result::Result { - if __obj.is_null() { - return ::core::result::Result::Err( - <__JniErr as ::core::convert::From< - String, - >>::from( - "Marker: null value where a variant was required".to_string(), - ), - ); - } - if env - .is_instance_of(__obj, "io/prebindgen/covertest/model/Marker$None_") - .map_err(|e| <__JniErr as ::core::convert::From< + let __len = env + .get_array_length(v) + .map_err(|e| { + <__JniErr as ::core::convert::From< String, - >>::from( - format!( - concat!("Marker", ": instanceof ", - "io/prebindgen/covertest/model/Marker$None_", ": {}"), e - ), - ))? - { - return ::core::result::Result::Ok(perftest_flat::Marker::None_); - } - if env - .is_instance_of(__obj, "io/prebindgen/covertest/model/Marker$Ranked") - .map_err(|e| <__JniErr as ::core::convert::From< + >>::from(format!("fixed-size array decode: {}", e)) + })? as usize; + let mut __buf: ::std::vec::Vec = ::std::vec![ + 0 as jni::sys::jlong; __len + ]; + env.get_long_array_region(v, 0, &mut __buf) + .map_err(|e| { + <__JniErr as ::core::convert::From< String, - >>::from( - format!( - concat!("Marker", ": instanceof ", - "io/prebindgen/covertest/model/Marker$Ranked", ": {}"), e - ), - ))? - { - let __p_v0_obj: jni::objects::JObject = env - .get_field(__obj, "v0", "Lio/prebindgen/covertest/model/Priority;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Marker.Ranked.v0: {}", e)))?; - let __p_v0 = if __p_v0_obj.is_null() { - ::core::option::Option::None - } else { - let __p_v0_raw: jni::sys::jint = env - .call_method(&__p_v0_obj, "getValue", "()I", &[]) - .and_then(|val| val.i()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Marker.Ranked.v0: {}", e)))?; - ::core::option::Option::Some( - jint_to_Priority_447102d2(env, &__p_v0_raw)?, - ) - }; - return ::core::result::Result::Ok(perftest_flat::Marker::Ranked(__p_v0)); - } - ::core::result::Result::Err( + >>::from(format!("fixed-size array decode: {}", e)) + })?; + let __vals: ::std::vec::Vec = __buf.iter().map(|__x| *__x as u64).collect(); + let __arr: [u64; 2] = __vals + .as_slice() + .try_into() + .map_err(|_| { <__JniErr as ::core::convert::From< String, - >>::from("Marker: value is not one of its declared variants".to_string()), - ) - })()? + >>::from( + "fixed-size array decode: `[u64 ; 2]` expects a different length" + .to_string(), + ) + })?; + __arr }) } #[allow( @@ -1248,64 +1214,56 @@ pub(crate) unsafe fn JObject_to_Marker_3dc81334<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_ObjectBoundary16_e9d41606<'env, 'v>( +pub(crate) unsafe fn JObject_to_Annotated_b543f0d9<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { +) -> ::core::result::Result { Ok({ - let __left_raw: jni::objects::JObject = env - .get_field(v, "left", "Lio/prebindgen/covertest/model/ObjectBoundary8;") + let __payload_raw: jni::objects::JObject = env + .get_field(v, "payload", "Lio/prebindgen/covertest/Payload;") .and_then(|val| val.l()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("ObjectBoundary16.left: {}", e)))?; - let left = JObject_to_ObjectBoundary8_55b82b02(env, &__left_raw)?; - let __right_raw: jni::objects::JObject = env - .get_field(v, "right", "Lio/prebindgen/covertest/model/ObjectBoundary8;") + >>::from(format!("Annotated.payload: {}", e)))?; + let payload = JObject_to_Payload_98f64326(env, &__payload_raw)?; + let __alternate_raw: jni::objects::JObject = env + .get_field(v, "alternate", "Lio/prebindgen/covertest/Payload;") .and_then(|val| val.l()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("ObjectBoundary16.right: {}", e)))?; - let right = JObject_to_ObjectBoundary8_55b82b02(env, &__right_raw)?; - perftest_flat::ObjectBoundary16 { - left, - right, - } - }) -} -#[allow( - non_snake_case, - unused_mut, - unused_variables, - unused_braces, - dead_code, - clippy::needless_question_mark, - clippy::let_and_return, - clippy::nonminimal_bool, - clippy::eq_op -)] -pub(crate) unsafe fn JObject_to_ObjectBoundary2_a8f288cc<'env, 'v>( - env: &mut jni::JNIEnv<'env>, - v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { - Ok({ - let __left_raw: jni::objects::JObject = env - .get_field(v, "left", "Lio/prebindgen/covertest/model/ObjectBoundaryLeaf;") + >>::from(format!("Annotated.alternate: {}", e)))?; + let alternate = JObject_to_Option_Payload_97036642(env, &__alternate_raw)?; + let __ttl_raw: jni::objects::JObject = env + .get_field(v, "ttl", "Ljava/lang/Long;") .and_then(|val| val.l()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("ObjectBoundary2.left: {}", e)))?; - let left = JObject_to_ObjectBoundaryLeaf_93531764(env, &__left_raw)?; - let __right_raw: jni::objects::JObject = env - .get_field(v, "right", "Lio/prebindgen/covertest/model/ObjectBoundaryLeaf;") + >>::from(format!("Annotated.ttl: {}", e)))?; + let ttl = JObject_to_Option_i64_2ba9a5ed(env, &__ttl_raw)?; + let __priority_jobj: jni::objects::JObject = env + .get_field(v, "priority", "Lio/prebindgen/covertest/model/Priority;") .and_then(|val| val.l()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("ObjectBoundary2.right: {}", e)))?; - let right = JObject_to_ObjectBoundaryLeaf_93531764(env, &__right_raw)?; - perftest_flat::ObjectBoundary2 { - left, - right, + >>::from(format!("Annotated.priority: {}", e)))?; + let priority = if __priority_jobj.is_null() { + ::core::option::Option::None + } else { + let __priority_raw: jni::sys::jint = env + .call_method(&__priority_jobj, "getValue", "()I", &[]) + .and_then(|val| val.i()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Annotated.priority: {}", e)))?; + ::core::option::Option::Some( + jint_to_Priority_447102d2(env, &__priority_raw)?, + ) + }; + perftest_flat::Annotated { + payload, + alternate, + ttl, + priority, } }) } @@ -1320,28 +1278,75 @@ pub(crate) unsafe fn JObject_to_ObjectBoundary2_a8f288cc<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_ObjectBoundary32_ed80fac3<'env, 'v>( +pub(crate) unsafe fn JObject_to_Arrays_71120c08<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { +) -> ::core::result::Result { Ok({ - let __left_raw: jni::objects::JObject = env - .get_field(v, "left", "Lio/prebindgen/covertest/model/ObjectBoundary16;") + let __bytes_jobj: jni::objects::JObject = env + .get_field(v, "bytes", "[B") .and_then(|val| val.l()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("ObjectBoundary32.left: {}", e)))?; - let left = JObject_to_ObjectBoundary16_e9d41606(env, &__left_raw)?; - let __right_raw: jni::objects::JObject = env - .get_field(v, "right", "Lio/prebindgen/covertest/model/ObjectBoundary16;") + >>::from(format!("Arrays.bytes: {}", e)))?; + let __bytes_raw: jni::objects::JByteArray = __bytes_jobj.into(); + let bytes = JByteArray_to_u8_4_39abedfa(env, &__bytes_raw)?; + let __shorts_jobj: jni::objects::JObject = env + .get_field(v, "shorts", "[S") .and_then(|val| val.l()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("ObjectBoundary32.right: {}", e)))?; - let right = JObject_to_ObjectBoundary16_e9d41606(env, &__right_raw)?; - perftest_flat::ObjectBoundary32 { - left, - right, + >>::from(format!("Arrays.shorts: {}", e)))?; + let __shorts_raw: jni::objects::JShortArray = __shorts_jobj.into(); + let shorts = JShortArray_to_i16_2_098f4ad5(env, &__shorts_raw)?; + let __ints_jobj: jni::objects::JObject = env + .get_field(v, "ints", "[I") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Arrays.ints: {}", e)))?; + let __ints_raw: jni::objects::JIntArray = __ints_jobj.into(); + let ints = JIntArray_to_i32_3_60e5e35a(env, &__ints_raw)?; + let __longs_jobj: jni::objects::JObject = env + .get_field(v, "longs", "[J") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Arrays.longs: {}", e)))?; + let __longs_raw: jni::objects::JLongArray = __longs_jobj.into(); + let longs = JLongArray_to_i64_2_73596912(env, &__longs_raw)?; + let __doubles_jobj: jni::objects::JObject = env + .get_field(v, "doubles", "[D") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Arrays.doubles: {}", e)))?; + let __doubles_raw: jni::objects::JDoubleArray = __doubles_jobj.into(); + let doubles = JDoubleArray_to_f64_2_dc30d1f9(env, &__doubles_raw)?; + let __flags_jobj: jni::objects::JObject = env + .get_field(v, "flags", "[Z") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Arrays.flags: {}", e)))?; + let __flags_raw: jni::objects::JBooleanArray = __flags_jobj.into(); + let flags = JBooleanArray_to_bool_3_3f960c58(env, &__flags_raw)?; + let __raw_jobj: jni::objects::JObject = env + .get_field(v, "raw", "[J") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Arrays.raw: {}", e)))?; + let __raw_raw: jni::objects::JLongArray = __raw_jobj.into(); + let raw = JLongArray_to_u64_2_60bcc6a5(env, &__raw_raw)?; + perftest_flat::Arrays { + bytes, + shorts, + ints, + longs, + doubles, + flags, + raw, } }) } @@ -1356,28 +1361,37 @@ pub(crate) unsafe fn JObject_to_ObjectBoundary32_ed80fac3<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_ObjectBoundary4_ea3fd497<'env, 'v>( +pub(crate) unsafe fn JObject_to_BlobValue_89b5dab7<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { +) -> ::core::result::Result { Ok({ - let __left_raw: jni::objects::JObject = env - .get_field(v, "left", "Lio/prebindgen/covertest/model/ObjectBoundary2;") + let __stamp_raw: jni::objects::JObject = env + .get_field(v, "stamp", "Lio/prebindgen/covertest/model/Stamp;") .and_then(|val| val.l()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("ObjectBoundary4.left: {}", e)))?; - let left = JObject_to_ObjectBoundary2_a8f288cc(env, &__left_raw)?; - let __right_raw: jni::objects::JObject = env - .get_field(v, "right", "Lio/prebindgen/covertest/model/ObjectBoundary2;") + >>::from(format!("BlobValue.stamp: {}", e)))?; + let stamp = JObject_to_Stamp_f6b1e942(env, &__stamp_raw)?; + let __id_jobj: jni::objects::JObject = env + .get_field(v, "id", "[B") .and_then(|val| val.l()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("ObjectBoundary4.right: {}", e)))?; - let right = JObject_to_ObjectBoundary2_a8f288cc(env, &__right_raw)?; - perftest_flat::ObjectBoundary4 { - left, - right, + >>::from(format!("BlobValue.id: {}", e)))?; + let __id_raw: jni::objects::JByteArray = __id_jobj.into(); + let id = JByteArray_to_Vec_u8_7936d5de(env, &__id_raw)?; + let __chunks_raw: jni::objects::JObject = env + .get_field(v, "chunks", "Ljava/util/List;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("BlobValue.chunks: {}", e)))?; + let chunks = JObject_to_Vec_Vec_u8_43404875(env, &__chunks_raw)?; + perftest_flat::BlobValue { + stamp, + id, + chunks, } }) } @@ -1392,60 +1406,28 @@ pub(crate) unsafe fn JObject_to_ObjectBoundary4_ea3fd497<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_ObjectBoundary63_29aa82ff<'env, 'v>( +pub(crate) unsafe fn JObject_to_CacheConfig_db89a97c<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { +) -> ::core::result::Result { Ok({ - let __leaves32_raw: jni::objects::JObject = env - .get_field(v, "leaves32", "Lio/prebindgen/covertest/model/ObjectBoundary32;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("ObjectBoundary63.leaves32: {}", e)))?; - let leaves32 = JObject_to_ObjectBoundary32_ed80fac3(env, &__leaves32_raw)?; - let __leaves16_raw: jni::objects::JObject = env - .get_field(v, "leaves16", "Lio/prebindgen/covertest/model/ObjectBoundary16;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("ObjectBoundary63.leaves16: {}", e)))?; - let leaves16 = JObject_to_ObjectBoundary16_e9d41606(env, &__leaves16_raw)?; - let __leaves8_raw: jni::objects::JObject = env - .get_field(v, "leaves8", "Lio/prebindgen/covertest/model/ObjectBoundary8;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("ObjectBoundary63.leaves8: {}", e)))?; - let leaves8 = JObject_to_ObjectBoundary8_55b82b02(env, &__leaves8_raw)?; - let __leaves4_raw: jni::objects::JObject = env - .get_field(v, "leaves4", "Lio/prebindgen/covertest/model/ObjectBoundary4;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("ObjectBoundary63.leaves4: {}", e)))?; - let leaves4 = JObject_to_ObjectBoundary4_ea3fd497(env, &__leaves4_raw)?; - let __leaves2_raw: jni::objects::JObject = env - .get_field(v, "leaves2", "Lio/prebindgen/covertest/model/ObjectBoundary2;") + let __replies_raw: jni::objects::JObject = env + .get_field(v, "replies", "Lio/prebindgen/covertest/model/RepliesConfig;") .and_then(|val| val.l()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("ObjectBoundary63.leaves2: {}", e)))?; - let leaves2 = JObject_to_ObjectBoundary2_a8f288cc(env, &__leaves2_raw)?; - let __leaf_raw: jni::objects::JObject = env - .get_field(v, "leaf", "Lio/prebindgen/covertest/model/ObjectBoundaryLeaf;") - .and_then(|val| val.l()) + >>::from(format!("CacheConfig.replies: {}", e)))?; + let replies = JObject_to_RepliesConfig_eb8e9079(env, &__replies_raw)?; + let __ttl_raw: jni::sys::jlong = env + .get_field(v, "ttl", "J") + .and_then(|val| val.j()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("ObjectBoundary63.leaf: {}", e)))?; - let leaf = JObject_to_ObjectBoundaryLeaf_93531764(env, &__leaf_raw)?; - perftest_flat::ObjectBoundary63 { - leaves32, - leaves16, - leaves8, - leaves4, - leaves2, - leaf, + >>::from(format!("CacheConfig.ttl: {}", e)))? as _; + let ttl = jlong_to_i64_fbf9a9bc(env, &__ttl_raw)?; + perftest_flat::CacheConfig { + replies, + ttl, } }) } @@ -1460,28 +1442,45 @@ pub(crate) unsafe fn JObject_to_ObjectBoundary63_29aa82ff<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_ObjectBoundary64_b2751ca5<'env, 'v>( +pub(crate) unsafe fn JObject_to_DurationBoundary_9c5bf9bc<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { +) -> ::core::result::Result { Ok({ - let __left_raw: jni::objects::JObject = env - .get_field(v, "left", "Lio/prebindgen/covertest/model/ObjectBoundary32;") - .and_then(|val| val.l()) + let __required_raw: jni::sys::jlong = env + .get_field(v, "required", "J") + .and_then(|val| val.j()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("ObjectBoundary64.left: {}", e)))?; - let left = JObject_to_ObjectBoundary32_ed80fac3(env, &__left_raw)?; - let __right_raw: jni::objects::JObject = env - .get_field(v, "right", "Lio/prebindgen/covertest/model/ObjectBoundary32;") + >>::from(format!("DurationBoundary.required: {}", e)))?; + let required = { + let required_s0 = jlong_to_u64_4384a5d6(env, &__required_raw)?; + let required_s1 = u64_to_Duration_7c0845f9(env, required_s0) + .map_err(|__e| <__JniErr as ::core::convert::From< + String, + >>::from(__e.to_string()))?; + required_s1 + }; + let __delay_jobj: jni::objects::JObject = env + .get_field(v, "delay", "Lkotlin/ULong;") .and_then(|val| val.l()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("ObjectBoundary64.right: {}", e)))?; - let right = JObject_to_ObjectBoundary32_ed80fac3(env, &__right_raw)?; - perftest_flat::ObjectBoundary64 { - left, - right, + >>::from(format!("DurationBoundary.delay: {}", e)))?; + let delay = if __delay_jobj.is_null() { + ::core::option::Option::None + } else { + let __delay_raw: jni::sys::jlong = env + .call_method(&__delay_jobj, "unbox-impl", "()J", &[]) + .and_then(|val| val.j()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("DurationBoundary.delay: {}", e)))?; + jlong_to_Option_Duration_1cfa4d44(env, &__delay_raw)? + }; + perftest_flat::DurationBoundary { + required, + delay, } }) } @@ -1496,28 +1495,28 @@ pub(crate) unsafe fn JObject_to_ObjectBoundary64_b2751ca5<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_ObjectBoundary8_55b82b02<'env, 'v>( +pub(crate) unsafe fn JObject_to_HoldPolicy_d2a5bcc4<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { +) -> ::core::result::Result { Ok({ - let __left_raw: jni::objects::JObject = env - .get_field(v, "left", "Lio/prebindgen/covertest/model/ObjectBoundary4;") + let __hold_raw: jni::objects::JObject = env + .get_field(v, "hold", "Lio/prebindgen/covertest/model/Hold;") .and_then(|val| val.l()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("ObjectBoundary8.left: {}", e)))?; - let left = JObject_to_ObjectBoundary4_ea3fd497(env, &__left_raw)?; - let __right_raw: jni::objects::JObject = env - .get_field(v, "right", "Lio/prebindgen/covertest/model/ObjectBoundary4;") + >>::from(format!("HoldPolicy.hold: {}", e)))?; + let hold = JObject_to_Hold_5f85caaf(env, &__hold_raw)?; + let __grace_raw: jni::objects::JObject = env + .get_field(v, "grace", "Lio/prebindgen/covertest/model/Hold;") .and_then(|val| val.l()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("ObjectBoundary8.right: {}", e)))?; - let right = JObject_to_ObjectBoundary4_ea3fd497(env, &__right_raw)?; - perftest_flat::ObjectBoundary8 { - left, - right, + >>::from(format!("HoldPolicy.grace: {}", e)))?; + let grace = JObject_to_Option_Hold_230d7f9b(env, &__grace_raw)?; + perftest_flat::HoldPolicy { + hold, + grace, } }) } @@ -1532,57 +1531,66 @@ pub(crate) unsafe fn JObject_to_ObjectBoundary8_55b82b02<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_ObjectBoundaryLeaf_93531764<'env, 'v>( +pub(crate) unsafe fn JObject_to_Hold_5f85caaf<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { +) -> ::core::result::Result { Ok({ - let __value_raw: jni::sys::jlong = env - .get_field(v, "value", "J") - .and_then(|val| val.j()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("ObjectBoundaryLeaf.value: {}", e)))? as _; - let value = jlong_to_i64_fbf9a9bc(env, &__value_raw)?; - perftest_flat::ObjectBoundaryLeaf { - value, - } - }) -} -#[allow( - non_snake_case, - unused_mut, - unused_variables, - unused_braces, - dead_code, - clippy::needless_question_mark, - clippy::let_and_return, - clippy::nonminimal_bool, - clippy::eq_op -)] -pub(crate) unsafe fn JObject_to_ObjectBoundary_dc5ac22b<'env, 'v>( - env: &mut jni::JNIEnv<'env>, - v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { - Ok({ - let __left_raw: jni::objects::JObject = env - .get_field(v, "left", "Lio/prebindgen/covertest/model/ObjectBoundary64;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("ObjectBoundary.left: {}", e)))?; - let left = JObject_to_ObjectBoundary64_b2751ca5(env, &__left_raw)?; - let __right_raw: jni::objects::JObject = env - .get_field(v, "right", "Lio/prebindgen/covertest/model/ObjectBoundary63;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("ObjectBoundary.right: {}", e)))?; - let right = JObject_to_ObjectBoundary63_29aa82ff(env, &__right_raw)?; - perftest_flat::ObjectBoundary { - left, - right, - } + let __obj = v; + (|| -> ::core::result::Result { + if __obj.is_null() { + return ::core::result::Result::Err( + <__JniErr as ::core::convert::From< + String, + >>::from("Hold: null value where a variant was required".to_string()), + ); + } + if env + .is_instance_of(__obj, "io/prebindgen/covertest/model/Hold$Indefinite") + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from( + format!( + concat!("Hold", ": instanceof ", + "io/prebindgen/covertest/model/Hold$Indefinite", ": {}"), e + ), + ))? + { + return ::core::result::Result::Ok(perftest_flat::Hold::Indefinite); + } + if env + .is_instance_of(__obj, "io/prebindgen/covertest/model/Hold$For") + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from( + format!( + concat!("Hold", ": instanceof ", + "io/prebindgen/covertest/model/Hold$For", ": {}"), e + ), + ))? + { + let __p_v0_raw: jni::sys::jlong = env + .get_field(__obj, "v0", "J") + .and_then(|val| val.j()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Hold.For.v0: {}", e)))? as _; + let __p_v0 = { + let __p_v0_s0 = jlong_to_u64_4384a5d6(env, &__p_v0_raw)?; + let __p_v0_s1 = u64_to_Duration_7c0845f9(env, __p_v0_s0) + .map_err(|__e| <__JniErr as ::core::convert::From< + String, + >>::from(__e.to_string()))?; + __p_v0_s1 + }; + return ::core::result::Result::Ok(perftest_flat::Hold::For(__p_v0)); + } + ::core::result::Result::Err( + <__JniErr as ::core::convert::From< + String, + >>::from("Hold: value is not one of its declared variants".to_string()), + ) + })()? }) } #[allow( @@ -1596,46 +1604,104 @@ pub(crate) unsafe fn JObject_to_ObjectBoundary_dc5ac22b<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_Observation_435b0724<'env, 'v>( +pub(crate) unsafe fn JObject_to_Lookup_94ada15e<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { +) -> ::core::result::Result { Ok({ - let __id_raw: jni::sys::jlong = env - .get_field(v, "id", "J") - .and_then(|val| val.j()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Observation.id: {}", e)))? as _; - let id = jlong_to_i64_fbf9a9bc(env, &__id_raw)?; - let __reading_raw: jni::objects::JObject = env - .get_field(v, "reading", "Lio/prebindgen/covertest/model/Reading;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Observation.reading: {}", e)))?; - let reading = JObject_to_Reading_2261050f(env, &__reading_raw)?; - let __fallback_raw: jni::objects::JObject = env - .get_field(v, "fallback", "Lio/prebindgen/covertest/model/Reading;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Observation.fallback: {}", e)))?; - let fallback = JObject_to_Option_Reading_80df84a9(env, &__fallback_raw)?; - let __note_jobj: jni::objects::JObject = env - .get_field(v, "note", "Ljava/lang/String;") - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Observation.note: {}", e)))?; - let __note_raw: jni::objects::JString = __note_jobj.into(); - let note = JString_to_String_c7f3ca43(env, &__note_raw)?; - perftest_flat::Observation { - id, - reading, - fallback, - note, - } + let __obj = v; + (|| -> ::core::result::Result { + if __obj.is_null() { + return ::core::result::Result::Err( + <__JniErr as ::core::convert::From< + String, + >>::from( + "Lookup: null value where a variant was required".to_string(), + ), + ); + } + if env + .is_instance_of(__obj, "io/prebindgen/covertest/model/Lookup$Absent") + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from( + format!( + concat!("Lookup", ": instanceof ", + "io/prebindgen/covertest/model/Lookup$Absent", ": {}"), e + ), + ))? + { + return ::core::result::Result::Ok(perftest_flat::Lookup::Absent); + } + if env + .is_instance_of(__obj, "io/prebindgen/covertest/model/Lookup$Found") + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from( + format!( + concat!("Lookup", ": instanceof ", + "io/prebindgen/covertest/model/Lookup$Found", ": {}"), e + ), + ))? + { + let __p_v0_obj: jni::objects::JObject = env + .get_field( + __obj, + "v0", + "Lio/prebindgen/covertest/analytics/Summary;", + ) + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Lookup.Found.v0: {}", e)))?; + let __p_v0_raw: jni::sys::jlong = if __p_v0_obj.is_null() { + 0 + } else { + env.call_method(&__p_v0_obj, "peek", "()J", &[]) + .and_then(|val| val.j()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Lookup.Found.v0: {}", e)))? + }; + if __p_v0_raw == 0 || (__p_v0_raw & 1) == 1 { + return ::core::result::Result::Err( + <__JniErr as ::core::convert::From< + String, + >>::from("Operation on a closed native handle.".to_string()), + ); + } + let __p_v0: perftest_flat::Summary = unsafe { + *std::boxed::Box::from_raw(__p_v0_raw as *mut perftest_flat::Summary) + }; + return ::core::result::Result::Ok(perftest_flat::Lookup::Found(__p_v0)); + } + if env + .is_instance_of(__obj, "io/prebindgen/covertest/model/Lookup$Failed") + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from( + format!( + concat!("Lookup", ": instanceof ", + "io/prebindgen/covertest/model/Lookup$Failed", ": {}"), e + ), + ))? + { + let __p_v0_obj: jni::objects::JObject = env + .get_field(__obj, "v0", "Ljava/lang/String;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Lookup.Failed.v0: {}", e)))?; + let __p_v0_raw: jni::objects::JString = __p_v0_obj.into(); + let __p_v0 = JString_to_String_c7f3ca43(env, &__p_v0_raw)?; + return ::core::result::Result::Ok(perftest_flat::Lookup::Failed(__p_v0)); + } + ::core::result::Result::Err( + <__JniErr as ::core::convert::From< + String, + >>::from("Lookup: value is not one of its declared variants".to_string()), + ) + })()? }) } #[allow( @@ -1649,33 +1715,77 @@ pub(crate) unsafe fn JObject_to_Observation_435b0724<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_Option_CacheConfig_a6be794d<'env, 'v>( +pub(crate) unsafe fn JObject_to_Marker_3dc81334<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result, __JniErr> { +) -> ::core::result::Result { Ok({ - if v.is_null() { None } else { Some(JObject_to_CacheConfig_db89a97c(env, v)?) } - }) -} -#[allow( - non_snake_case, - unused_mut, - unused_variables, - unused_braces, - dead_code, - clippy::needless_question_mark, - clippy::let_and_return, - clippy::nonminimal_bool, - clippy::eq_op -)] -pub(crate) unsafe fn JObject_to_Option_Hold_230d7f9b<'env, 'v>( - env: &mut jni::JNIEnv<'env>, - v: &jni::objects::JObject<'v>, -) -> ::core::result::Result, __JniErr> { - Ok({ if v.is_null() { None } else { Some(JObject_to_Hold_5f85caaf(env, v)?) } }) -} -#[allow( - non_snake_case, + let __obj = v; + (|| -> ::core::result::Result { + if __obj.is_null() { + return ::core::result::Result::Err( + <__JniErr as ::core::convert::From< + String, + >>::from( + "Marker: null value where a variant was required".to_string(), + ), + ); + } + if env + .is_instance_of(__obj, "io/prebindgen/covertest/model/Marker$None_") + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from( + format!( + concat!("Marker", ": instanceof ", + "io/prebindgen/covertest/model/Marker$None_", ": {}"), e + ), + ))? + { + return ::core::result::Result::Ok(perftest_flat::Marker::None_); + } + if env + .is_instance_of(__obj, "io/prebindgen/covertest/model/Marker$Ranked") + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from( + format!( + concat!("Marker", ": instanceof ", + "io/prebindgen/covertest/model/Marker$Ranked", ": {}"), e + ), + ))? + { + let __p_v0_obj: jni::objects::JObject = env + .get_field(__obj, "v0", "Lio/prebindgen/covertest/model/Priority;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Marker.Ranked.v0: {}", e)))?; + let __p_v0 = if __p_v0_obj.is_null() { + ::core::option::Option::None + } else { + let __p_v0_raw: jni::sys::jint = env + .call_method(&__p_v0_obj, "getValue", "()I", &[]) + .and_then(|val| val.i()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Marker.Ranked.v0: {}", e)))?; + ::core::option::Option::Some( + jint_to_Priority_447102d2(env, &__p_v0_raw)?, + ) + }; + return ::core::result::Result::Ok(perftest_flat::Marker::Ranked(__p_v0)); + } + ::core::result::Result::Err( + <__JniErr as ::core::convert::From< + String, + >>::from("Marker: value is not one of its declared variants".to_string()), + ) + })()? + }) +} +#[allow( + non_snake_case, unused_mut, unused_variables, unused_braces, @@ -1685,11 +1795,30 @@ pub(crate) unsafe fn JObject_to_Option_Hold_230d7f9b<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_Option_Payload_97036642<'env, 'v>( +pub(crate) unsafe fn JObject_to_ObjectBoundary16_e9d41606<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result, __JniErr> { - Ok({ if v.is_null() { None } else { Some(JObject_to_Payload_98f64326(env, v)?) } }) +) -> ::core::result::Result { + Ok({ + let __left_raw: jni::objects::JObject = env + .get_field(v, "left", "Lio/prebindgen/covertest/model/ObjectBoundary8;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary16.left: {}", e)))?; + let left = JObject_to_ObjectBoundary8_55b82b02(env, &__left_raw)?; + let __right_raw: jni::objects::JObject = env + .get_field(v, "right", "Lio/prebindgen/covertest/model/ObjectBoundary8;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary16.right: {}", e)))?; + let right = JObject_to_ObjectBoundary8_55b82b02(env, &__right_raw)?; + perftest_flat::ObjectBoundary16 { + left, + right, + } + }) } #[allow( non_snake_case, @@ -1702,29 +1831,28 @@ pub(crate) unsafe fn JObject_to_Option_Payload_97036642<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_Option_Percent_544dd364<'env, 'v>( +pub(crate) unsafe fn JObject_to_ObjectBoundary2_a8f288cc<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result, __JniErr> { +) -> ::core::result::Result { Ok({ - if !v.is_null() { - let __unboxed: jni::sys::jint = env - .call_method(&v, "intValue", "()I", &[]) - .and_then(|val| val.i()) - .map(|__x| __x as jni::sys::jint) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Option unbox: {}", e)))?; - Some({ - let __inner_s0 = jint_to_i32_a3e3b6ef(env, &__unboxed)?; - let __inner_s1 = i32_to_Percent_db3641cc(env, __inner_s0) - .map_err(|__e| <__JniErr as ::core::convert::From< - String, - >>::from(__e.to_string()))?; - __inner_s1 - }) - } else { - None + let __left_raw: jni::objects::JObject = env + .get_field(v, "left", "Lio/prebindgen/covertest/model/ObjectBoundaryLeaf;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary2.left: {}", e)))?; + let left = JObject_to_ObjectBoundaryLeaf_93531764(env, &__left_raw)?; + let __right_raw: jni::objects::JObject = env + .get_field(v, "right", "Lio/prebindgen/covertest/model/ObjectBoundaryLeaf;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary2.right: {}", e)))?; + let right = JObject_to_ObjectBoundaryLeaf_93531764(env, &__right_raw)?; + perftest_flat::ObjectBoundary2 { + left, + right, } }) } @@ -1739,22 +1867,28 @@ pub(crate) unsafe fn JObject_to_Option_Percent_544dd364<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_Option_Priority_ad5cbb32<'env, 'v>( +pub(crate) unsafe fn JObject_to_ObjectBoundary32_ed80fac3<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result, __JniErr> { +) -> ::core::result::Result { Ok({ - if !v.is_null() { - let __unboxed: jni::sys::jint = env - .call_method(&v, "intValue", "()I", &[]) - .and_then(|val| val.i()) - .map(|__x| __x as jni::sys::jint) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Option unbox: {}", e)))?; - Some(jint_to_Priority_447102d2(env, &__unboxed)?) - } else { - None + let __left_raw: jni::objects::JObject = env + .get_field(v, "left", "Lio/prebindgen/covertest/model/ObjectBoundary16;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary32.left: {}", e)))?; + let left = JObject_to_ObjectBoundary16_e9d41606(env, &__left_raw)?; + let __right_raw: jni::objects::JObject = env + .get_field(v, "right", "Lio/prebindgen/covertest/model/ObjectBoundary16;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary32.right: {}", e)))?; + let right = JObject_to_ObjectBoundary16_e9d41606(env, &__right_raw)?; + perftest_flat::ObjectBoundary32 { + left, + right, } }) } @@ -1769,11 +1903,30 @@ pub(crate) unsafe fn JObject_to_Option_Priority_ad5cbb32<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_Option_Reading_80df84a9<'env, 'v>( +pub(crate) unsafe fn JObject_to_ObjectBoundary4_ea3fd497<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result, __JniErr> { - Ok({ if v.is_null() { None } else { Some(JObject_to_Reading_2261050f(env, v)?) } }) +) -> ::core::result::Result { + Ok({ + let __left_raw: jni::objects::JObject = env + .get_field(v, "left", "Lio/prebindgen/covertest/model/ObjectBoundary2;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary4.left: {}", e)))?; + let left = JObject_to_ObjectBoundary2_a8f288cc(env, &__left_raw)?; + let __right_raw: jni::objects::JObject = env + .get_field(v, "right", "Lio/prebindgen/covertest/model/ObjectBoundary2;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary4.right: {}", e)))?; + let right = JObject_to_ObjectBoundary2_a8f288cc(env, &__right_raw)?; + perftest_flat::ObjectBoundary4 { + left, + right, + } + }) } #[allow( non_snake_case, @@ -1786,22 +1939,60 @@ pub(crate) unsafe fn JObject_to_Option_Reading_80df84a9<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_Option_f64_b3f3e9a9<'env, 'v>( +pub(crate) unsafe fn JObject_to_ObjectBoundary63_29aa82ff<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result, __JniErr> { +) -> ::core::result::Result { Ok({ - if !v.is_null() { - let __unboxed: jni::sys::jdouble = env - .call_method(&v, "doubleValue", "()D", &[]) - .and_then(|val| val.d()) - .map(|__x| __x as jni::sys::jdouble) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Option unbox: {}", e)))?; - Some(jdouble_to_f64_9e4a8f70(env, &__unboxed)?) - } else { - None + let __leaves32_raw: jni::objects::JObject = env + .get_field(v, "leaves32", "Lio/prebindgen/covertest/model/ObjectBoundary32;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary63.leaves32: {}", e)))?; + let leaves32 = JObject_to_ObjectBoundary32_ed80fac3(env, &__leaves32_raw)?; + let __leaves16_raw: jni::objects::JObject = env + .get_field(v, "leaves16", "Lio/prebindgen/covertest/model/ObjectBoundary16;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary63.leaves16: {}", e)))?; + let leaves16 = JObject_to_ObjectBoundary16_e9d41606(env, &__leaves16_raw)?; + let __leaves8_raw: jni::objects::JObject = env + .get_field(v, "leaves8", "Lio/prebindgen/covertest/model/ObjectBoundary8;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary63.leaves8: {}", e)))?; + let leaves8 = JObject_to_ObjectBoundary8_55b82b02(env, &__leaves8_raw)?; + let __leaves4_raw: jni::objects::JObject = env + .get_field(v, "leaves4", "Lio/prebindgen/covertest/model/ObjectBoundary4;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary63.leaves4: {}", e)))?; + let leaves4 = JObject_to_ObjectBoundary4_ea3fd497(env, &__leaves4_raw)?; + let __leaves2_raw: jni::objects::JObject = env + .get_field(v, "leaves2", "Lio/prebindgen/covertest/model/ObjectBoundary2;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary63.leaves2: {}", e)))?; + let leaves2 = JObject_to_ObjectBoundary2_a8f288cc(env, &__leaves2_raw)?; + let __leaf_raw: jni::objects::JObject = env + .get_field(v, "leaf", "Lio/prebindgen/covertest/model/ObjectBoundaryLeaf;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary63.leaf: {}", e)))?; + let leaf = JObject_to_ObjectBoundaryLeaf_93531764(env, &__leaf_raw)?; + perftest_flat::ObjectBoundary63 { + leaves32, + leaves16, + leaves8, + leaves4, + leaves2, + leaf, } }) } @@ -1816,22 +2007,28 @@ pub(crate) unsafe fn JObject_to_Option_f64_b3f3e9a9<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_Option_i64_2ba9a5ed<'env, 'v>( +pub(crate) unsafe fn JObject_to_ObjectBoundary64_b2751ca5<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result, __JniErr> { +) -> ::core::result::Result { Ok({ - if !v.is_null() { - let __unboxed: jni::sys::jlong = env - .call_method(&v, "longValue", "()J", &[]) - .and_then(|val| val.j()) - .map(|__x| __x as jni::sys::jlong) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Option unbox: {}", e)))?; - Some(jlong_to_i64_fbf9a9bc(env, &__unboxed)?) - } else { - None + let __left_raw: jni::objects::JObject = env + .get_field(v, "left", "Lio/prebindgen/covertest/model/ObjectBoundary32;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary64.left: {}", e)))?; + let left = JObject_to_ObjectBoundary32_ed80fac3(env, &__left_raw)?; + let __right_raw: jni::objects::JObject = env + .get_field(v, "right", "Lio/prebindgen/covertest/model/ObjectBoundary32;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary64.right: {}", e)))?; + let right = JObject_to_ObjectBoundary32_ed80fac3(env, &__right_raw)?; + perftest_flat::ObjectBoundary64 { + left, + right, } }) } @@ -1846,22 +2043,28 @@ pub(crate) unsafe fn JObject_to_Option_i64_2ba9a5ed<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_Option_u64_32be16a2<'env, 'v>( +pub(crate) unsafe fn JObject_to_ObjectBoundary8_55b82b02<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result, __JniErr> { +) -> ::core::result::Result { Ok({ - if !v.is_null() { - let __unboxed: jni::sys::jlong = env - .call_method(&v, "longValue", "()J", &[]) - .and_then(|val| val.j()) - .map(|__x| __x as jni::sys::jlong) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Option unbox: {}", e)))?; - Some(jlong_to_u64_4384a5d6(env, &__unboxed)?) - } else { - None + let __left_raw: jni::objects::JObject = env + .get_field(v, "left", "Lio/prebindgen/covertest/model/ObjectBoundary4;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary8.left: {}", e)))?; + let left = JObject_to_ObjectBoundary4_ea3fd497(env, &__left_raw)?; + let __right_raw: jni::objects::JObject = env + .get_field(v, "right", "Lio/prebindgen/covertest/model/ObjectBoundary4;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary8.right: {}", e)))?; + let right = JObject_to_ObjectBoundary4_ea3fd497(env, &__right_raw)?; + perftest_flat::ObjectBoundary8 { + left, + right, } }) } @@ -1876,53 +2079,109 @@ pub(crate) unsafe fn JObject_to_Option_u64_32be16a2<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_Payload_98f64326<'env, 'v>( +pub(crate) unsafe fn JObject_to_ObjectBoundaryLeaf_93531764<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { +) -> ::core::result::Result { + Ok({ + let __value_raw: jni::sys::jlong = env + .get_field(v, "value", "J") + .and_then(|val| val.j()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundaryLeaf.value: {}", e)))? as _; + let value = jlong_to_i64_fbf9a9bc(env, &__value_raw)?; + perftest_flat::ObjectBoundaryLeaf { + value, + } + }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn JObject_to_ObjectBoundary_dc5ac22b<'env, 'v>( + env: &mut jni::JNIEnv<'env>, + v: &jni::objects::JObject<'v>, +) -> ::core::result::Result { + Ok({ + let __left_raw: jni::objects::JObject = env + .get_field(v, "left", "Lio/prebindgen/covertest/model/ObjectBoundary64;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary.left: {}", e)))?; + let left = JObject_to_ObjectBoundary64_b2751ca5(env, &__left_raw)?; + let __right_raw: jni::objects::JObject = env + .get_field(v, "right", "Lio/prebindgen/covertest/model/ObjectBoundary63;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("ObjectBoundary.right: {}", e)))?; + let right = JObject_to_ObjectBoundary63_29aa82ff(env, &__right_raw)?; + perftest_flat::ObjectBoundary { + left, + right, + } + }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn JObject_to_Observation_435b0724<'env, 'v>( + env: &mut jni::JNIEnv<'env>, + v: &jni::objects::JObject<'v>, +) -> ::core::result::Result { Ok({ let __id_raw: jni::sys::jlong = env .get_field(v, "id", "J") .and_then(|val| val.j()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("Payload.id: {}", e)))? as _; + >>::from(format!("Observation.id: {}", e)))? as _; let id = jlong_to_i64_fbf9a9bc(env, &__id_raw)?; - let __seq_raw: jni::sys::jint = env - .get_field(v, "seq", "I") - .and_then(|val| val.i()) - .map_err(|e| <__JniErr as ::core::convert::From< - String, - >>::from(format!("Payload.seq: {}", e)))? as _; - let seq = jint_to_i32_a3e3b6ef(env, &__seq_raw)?; - let __value_raw: jni::sys::jdouble = env - .get_field(v, "value", "D") - .and_then(|val| val.d()) + let __reading_raw: jni::objects::JObject = env + .get_field(v, "reading", "Lio/prebindgen/covertest/model/Reading;") + .and_then(|val| val.l()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("Payload.value: {}", e)))? as _; - let value = jdouble_to_f64_9e4a8f70(env, &__value_raw)?; - let __flag_raw: jni::sys::jboolean = env - .get_field(v, "flag", "Z") - .and_then(|val| val.z()) + >>::from(format!("Observation.reading: {}", e)))?; + let reading = JObject_to_Reading_2261050f(env, &__reading_raw)?; + let __fallback_raw: jni::objects::JObject = env + .get_field(v, "fallback", "Lio/prebindgen/covertest/model/Reading;") + .and_then(|val| val.l()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("Payload.flag: {}", e)))? as _; - let flag = jboolean_to_bool_31306d98(env, &__flag_raw)?; - let __label_jobj: jni::objects::JObject = env - .get_field(v, "label", "Ljava/lang/String;") + >>::from(format!("Observation.fallback: {}", e)))?; + let fallback = JObject_to_Option_Reading_80df84a9(env, &__fallback_raw)?; + let __note_jobj: jni::objects::JObject = env + .get_field(v, "note", "Ljava/lang/String;") .and_then(|val| val.l()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("Payload.label: {}", e)))?; - let __label_raw: jni::objects::JString = __label_jobj.into(); - let label = JString_to_Option_Box_String_071e4c8c(env, &__label_raw)?; - perftest_flat::Payload { + >>::from(format!("Observation.note: {}", e)))?; + let __note_raw: jni::objects::JString = __note_jobj.into(); + let note = JString_to_String_c7f3ca43(env, &__note_raw)?; + perftest_flat::Observation { id, - seq, - value, - flag, - label, + reading, + fallback, + note, } }) } @@ -1937,32 +2196,320 @@ pub(crate) unsafe fn JObject_to_Payload_98f64326<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_Reading_2261050f<'env, 'v>( +pub(crate) unsafe fn JObject_to_Option_CacheConfig_a6be794d<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { +) -> ::core::result::Result, __JniErr> { Ok({ - let __obj = v; - (|| -> ::core::result::Result { - if __obj.is_null() { - return ::core::result::Result::Err( - <__JniErr as ::core::convert::From< - String, - >>::from( - "Reading: null value where a variant was required".to_string(), - ), - ); - } - if env - .is_instance_of(__obj, "io/prebindgen/covertest/model/Reading$Missing") + if v.is_null() { None } else { Some(JObject_to_CacheConfig_db89a97c(env, v)?) } + }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn JObject_to_Option_Hold_230d7f9b<'env, 'v>( + env: &mut jni::JNIEnv<'env>, + v: &jni::objects::JObject<'v>, +) -> ::core::result::Result, __JniErr> { + Ok({ if v.is_null() { None } else { Some(JObject_to_Hold_5f85caaf(env, v)?) } }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn JObject_to_Option_Payload_97036642<'env, 'v>( + env: &mut jni::JNIEnv<'env>, + v: &jni::objects::JObject<'v>, +) -> ::core::result::Result, __JniErr> { + Ok({ if v.is_null() { None } else { Some(JObject_to_Payload_98f64326(env, v)?) } }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn JObject_to_Option_Percent_544dd364<'env, 'v>( + env: &mut jni::JNIEnv<'env>, + v: &jni::objects::JObject<'v>, +) -> ::core::result::Result, __JniErr> { + Ok({ + if !v.is_null() { + let __unboxed: jni::sys::jint = env + .call_method(&v, "intValue", "()I", &[]) + .and_then(|val| val.i()) + .map(|__x| __x as jni::sys::jint) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from( - format!( - concat!("Reading", ": instanceof ", - "io/prebindgen/covertest/model/Reading$Missing", ": {}"), e - ), - ))? + >>::from(format!("Option unbox: {}", e)))?; + Some({ + let __inner_s0 = jint_to_i32_a3e3b6ef(env, &__unboxed)?; + let __inner_s1 = i32_to_Percent_db3641cc(env, __inner_s0) + .map_err(|__e| <__JniErr as ::core::convert::From< + String, + >>::from(__e.to_string()))?; + __inner_s1 + }) + } else { + None + } + }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn JObject_to_Option_Priority_ad5cbb32<'env, 'v>( + env: &mut jni::JNIEnv<'env>, + v: &jni::objects::JObject<'v>, +) -> ::core::result::Result, __JniErr> { + Ok({ + if !v.is_null() { + let __unboxed: jni::sys::jint = env + .call_method(&v, "intValue", "()I", &[]) + .and_then(|val| val.i()) + .map(|__x| __x as jni::sys::jint) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Option unbox: {}", e)))?; + Some(jint_to_Priority_447102d2(env, &__unboxed)?) + } else { + None + } + }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn JObject_to_Option_Reading_80df84a9<'env, 'v>( + env: &mut jni::JNIEnv<'env>, + v: &jni::objects::JObject<'v>, +) -> ::core::result::Result, __JniErr> { + Ok({ if v.is_null() { None } else { Some(JObject_to_Reading_2261050f(env, v)?) } }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn JObject_to_Option_f64_b3f3e9a9<'env, 'v>( + env: &mut jni::JNIEnv<'env>, + v: &jni::objects::JObject<'v>, +) -> ::core::result::Result, __JniErr> { + Ok({ + if !v.is_null() { + let __unboxed: jni::sys::jdouble = env + .call_method(&v, "doubleValue", "()D", &[]) + .and_then(|val| val.d()) + .map(|__x| __x as jni::sys::jdouble) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Option unbox: {}", e)))?; + Some(jdouble_to_f64_9e4a8f70(env, &__unboxed)?) + } else { + None + } + }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn JObject_to_Option_i64_2ba9a5ed<'env, 'v>( + env: &mut jni::JNIEnv<'env>, + v: &jni::objects::JObject<'v>, +) -> ::core::result::Result, __JniErr> { + Ok({ + if !v.is_null() { + let __unboxed: jni::sys::jlong = env + .call_method(&v, "longValue", "()J", &[]) + .and_then(|val| val.j()) + .map(|__x| __x as jni::sys::jlong) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Option unbox: {}", e)))?; + Some(jlong_to_i64_fbf9a9bc(env, &__unboxed)?) + } else { + None + } + }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn JObject_to_Option_u64_32be16a2<'env, 'v>( + env: &mut jni::JNIEnv<'env>, + v: &jni::objects::JObject<'v>, +) -> ::core::result::Result, __JniErr> { + Ok({ + if !v.is_null() { + let __unboxed: jni::sys::jlong = env + .call_method(&v, "longValue", "()J", &[]) + .and_then(|val| val.j()) + .map(|__x| __x as jni::sys::jlong) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Option unbox: {}", e)))?; + Some(jlong_to_u64_4384a5d6(env, &__unboxed)?) + } else { + None + } + }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn JObject_to_Payload_98f64326<'env, 'v>( + env: &mut jni::JNIEnv<'env>, + v: &jni::objects::JObject<'v>, +) -> ::core::result::Result { + Ok({ + let __id_raw: jni::sys::jlong = env + .get_field(v, "id", "J") + .and_then(|val| val.j()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Payload.id: {}", e)))? as _; + let id = jlong_to_i64_fbf9a9bc(env, &__id_raw)?; + let __seq_raw: jni::sys::jint = env + .get_field(v, "seq", "I") + .and_then(|val| val.i()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Payload.seq: {}", e)))? as _; + let seq = jint_to_i32_a3e3b6ef(env, &__seq_raw)?; + let __value_raw: jni::sys::jdouble = env + .get_field(v, "value", "D") + .and_then(|val| val.d()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Payload.value: {}", e)))? as _; + let value = jdouble_to_f64_9e4a8f70(env, &__value_raw)?; + let __flag_raw: jni::sys::jboolean = env + .get_field(v, "flag", "Z") + .and_then(|val| val.z()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Payload.flag: {}", e)))? as _; + let flag = jboolean_to_bool_31306d98(env, &__flag_raw)?; + let __label_jobj: jni::objects::JObject = env + .get_field(v, "label", "Ljava/lang/String;") + .and_then(|val| val.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Payload.label: {}", e)))?; + let __label_raw: jni::objects::JString = __label_jobj.into(); + let label = JString_to_Option_Box_String_071e4c8c(env, &__label_raw)?; + perftest_flat::Payload { + id, + seq, + value, + flag, + label, + } + }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn JObject_to_Reading_2261050f<'env, 'v>( + env: &mut jni::JNIEnv<'env>, + v: &jni::objects::JObject<'v>, +) -> ::core::result::Result { + Ok({ + let __obj = v; + (|| -> ::core::result::Result { + if __obj.is_null() { + return ::core::result::Result::Err( + <__JniErr as ::core::convert::From< + String, + >>::from( + "Reading: null value where a variant was required".to_string(), + ), + ); + } + if env + .is_instance_of(__obj, "io/prebindgen/covertest/model/Reading$Missing") + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from( + format!( + concat!("Reading", ": instanceof ", + "io/prebindgen/covertest/model/Reading$Missing", ": {}"), e + ), + ))? { return ::core::result::Result::Ok(perftest_flat::Reading::Missing); } @@ -2137,18 +2684,54 @@ pub(crate) unsafe fn JObject_to_RepliesConfig_eb8e9079<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn JObject_to_Tagged_641b984c<'env, 'v>( +pub(crate) unsafe fn JObject_to_Stamp_f6b1e942<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, -) -> ::core::result::Result { +) -> ::core::result::Result { Ok({ - let __id_raw: jni::sys::jlong = env - .get_field(v, "id", "J") + let __secs_raw: jni::sys::jlong = env + .get_field(v, "secs", "J") .and_then(|val| val.j()) .map_err(|e| <__JniErr as ::core::convert::From< String, - >>::from(format!("Tagged.id: {}", e)))? as _; - let id = jlong_to_i64_fbf9a9bc(env, &__id_raw)?; + >>::from(format!("Stamp.secs: {}", e)))? as _; + let secs = jlong_to_i64_fbf9a9bc(env, &__secs_raw)?; + let __nanos_raw: jni::sys::jlong = env + .get_field(v, "nanos", "J") + .and_then(|val| val.j()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Stamp.nanos: {}", e)))? as _; + let nanos = jlong_to_i64_fbf9a9bc(env, &__nanos_raw)?; + perftest_flat::Stamp { + secs, + nanos, + } + }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn JObject_to_Tagged_641b984c<'env, 'v>( + env: &mut jni::JNIEnv<'env>, + v: &jni::objects::JObject<'v>, +) -> ::core::result::Result { + Ok({ + let __id_raw: jni::sys::jlong = env + .get_field(v, "id", "J") + .and_then(|val| val.j()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Tagged.id: {}", e)))? as _; + let id = jlong_to_i64_fbf9a9bc(env, &__id_raw)?; let __marker_raw: jni::objects::JObject = env .get_field(v, "marker", "Lio/prebindgen/covertest/model/Marker;") .and_then(|val| val.l()) @@ -2331,6 +2914,45 @@ pub(crate) unsafe fn JObject_to_Vec_Payload_8b7084d2<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] +pub(crate) unsafe fn JObject_to_Vec_Vec_u8_43404875<'env, 'v>( + env: &mut jni::JNIEnv<'env>, + v: &jni::objects::JObject<'v>, +) -> ::core::result::Result>, __JniErr> { + Ok({ + let __list = jni::objects::JList::from_env(env, v) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Vec<_>: list-from-env: {}", e)))?; + let mut __it = __list + .iter(env) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Vec<_>: list-iter: {}", e)))?; + let mut __out: Vec> = Vec::new(); + while let Some(__obj) = __it + .next(env) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Vec<_>: list-next: {}", e)))? + { + let __elem_wire: jni::objects::JByteArray = __obj.into(); + let __elem: Vec = JByteArray_to_Vec_u8_7936d5de(env, &__elem_wire)?; + __out.push(__elem); + } + __out + }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] pub(crate) unsafe fn JObject_to_impl_Fn_Duration_Send_Sync_static_98c9f460<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JObject<'v>, @@ -3412,6 +4034,53 @@ pub(crate) unsafe fn JObject_to_impl_Fn_u64_Send_Sync_static_c7830b57<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] +pub(crate) unsafe fn JShortArray_to_i16_2_098f4ad5<'env, 'v>( + env: &mut jni::JNIEnv<'env>, + v: &jni::objects::JShortArray<'v>, +) -> ::core::result::Result<[i16; 2], __JniErr> { + Ok({ + let __len = env + .get_array_length(v) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array decode: {}", e)) + })? as usize; + let mut __buf: ::std::vec::Vec = ::std::vec![ + 0 as jni::sys::jshort; __len + ]; + env.get_short_array_region(v, 0, &mut __buf) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array decode: {}", e)) + })?; + let __vals: ::std::vec::Vec = __buf.iter().map(|__x| *__x as i16).collect(); + let __arr: [i16; 2] = __vals + .as_slice() + .try_into() + .map_err(|_| { + <__JniErr as ::core::convert::From< + String, + >>::from( + "fixed-size array decode: `[i16 ; 2]` expects a different length" + .to_string(), + ) + })?; + __arr + }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] pub(crate) unsafe fn JString_to_Option_Box_String_071e4c8c<'env, 'v>( env: &mut jni::JNIEnv<'env>, v: &jni::objects::JString<'v>, @@ -6477,23 +7146,28 @@ pub(crate) unsafe fn Result_Summary_String_to_Summary_dfdf7f9e<'a>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn Stamp_to_JByteArray_2fc9bd18<'a>( +pub(crate) unsafe fn Stamp_to_JObject_f6b1e942<'a>( env: &mut jni::JNIEnv<'a>, v: perftest_flat::Stamp, -) -> ::core::result::Result, __JniErr> { +) -> ::core::result::Result, __JniErr> { Ok({ - let __bytes: &[u8] = unsafe { - ::core::slice::from_raw_parts( - (&v as *const perftest_flat::Stamp) as *const u8, - ::core::mem::size_of::(), + let ___secs: jni::sys::jlong = i64_to_jlong_fbf9a9bc(env, v.secs.clone())?; + let ___nanos: jni::sys::jlong = i64_to_jlong_fbf9a9bc(env, v.nanos.clone())?; + let __obj = env + .call_static_method( + "io/prebindgen/covertest/model/Stamp", + "fromParts", + "(JJ)Lio/prebindgen/covertest/model/Stamp;", + &[ + jni::objects::JValue::from(___secs), + jni::objects::JValue::from(___nanos), + ], ) - }; - env.byte_array_from_slice(__bytes) - .map_err(|e| { - <__JniErr as ::core::convert::From< - String, - >>::from(format!("value-blob encode: {}", e)) - })? + .and_then(|__v| __v.l()) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("encode struct via fromParts: {}", e)))?; + __obj }) } #[allow( @@ -6823,7 +7497,7 @@ pub(crate) unsafe fn Vec_Stamp_to_JObject_8954d9be<'a>( String, >>::from(format!("Vec<_>: list-from-env: {}", e)))?; for __elem in v.into_iter() { - let __elem_wire = Stamp_to_JByteArray_2fc9bd18(env, __elem)?; + let __elem_wire = Stamp_to_JObject_f6b1e942(env, __elem)?; let __elem_obj: jni::objects::JObject = __elem_wire.into(); __list .add(env, &__elem_obj) @@ -6882,11 +7556,31 @@ pub(crate) unsafe fn Vec_String_to_JObject_1e282499<'a>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn bool_to_jboolean_31306d98<'a>( +pub(crate) unsafe fn Vec_Vec_u8_to_JObject_43404875<'a>( env: &mut jni::JNIEnv<'a>, - v: bool, -) -> ::core::result::Result { - Ok(v as jni::sys::jboolean) + v: Vec>, +) -> ::core::result::Result, __JniErr> { + Ok({ + let __list_obj = env + .new_object("java/util/ArrayList", "()V", &[]) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Vec<_>: new ArrayList: {}", e)))?; + let __list = jni::objects::JList::from_env(env, &__list_obj) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Vec<_>: list-from-env: {}", e)))?; + for __elem in v.into_iter() { + let __elem_wire = Vec_u8_to_JByteArray_7936d5de(env, __elem)?; + let __elem_obj: jni::objects::JObject = __elem_wire.into(); + __list + .add(env, &__elem_obj) + .map_err(|e| <__JniErr as ::core::convert::From< + String, + >>::from(format!("Vec<_>: list-add: {}", e)))?; + } + __list_obj + }) } #[allow( non_snake_case, @@ -6899,11 +7593,18 @@ pub(crate) unsafe fn bool_to_jboolean_31306d98<'a>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn f64_to_jdouble_9e4a8f70<'a>( +pub(crate) unsafe fn Vec_u8_to_JByteArray_7936d5de<'a>( env: &mut jni::JNIEnv<'a>, - v: f64, -) -> ::core::result::Result { - Ok(v as jni::sys::jdouble) + v: Vec, +) -> ::core::result::Result, __JniErr> { + Ok({ + env.byte_array_from_slice(v.as_slice()) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("encode_byte_array: {}", e)) + })? + }) } #[allow( non_snake_case, @@ -6916,11 +7617,30 @@ pub(crate) unsafe fn f64_to_jdouble_9e4a8f70<'a>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn i32_to_Celsius_8c363100<'a>( +pub(crate) unsafe fn bool_3_to_JBooleanArray_3f960c58<'a>( env: &mut jni::JNIEnv<'a>, - v: i32, -) -> ::core::result::Result { - Ok(>::into(v)) + v: [bool; 3], +) -> ::core::result::Result, __JniErr> { + Ok({ + let __buf: ::std::vec::Vec = v + .iter() + .map(|__x| *__x as jni::sys::jboolean) + .collect(); + let __arr = env + .new_boolean_array(__buf.len() as jni::sys::jsize) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array encode: {}", e)) + })?; + env.set_boolean_array_region(&__arr, 0, &__buf) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array encode: {}", e)) + })?; + __arr + }) } #[allow( non_snake_case, @@ -6933,14 +7653,11 @@ pub(crate) unsafe fn i32_to_Celsius_8c363100<'a>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn i32_to_Percent_db3641cc<'a>( +pub(crate) unsafe fn bool_to_jboolean_31306d98<'a>( env: &mut jni::JNIEnv<'a>, - v: i32, -) -> ::core::result::Result< - perftest_flat::Percent, - >::Error, -> { - >::try_into(v) + v: bool, +) -> ::core::result::Result { + Ok(v as jni::sys::jboolean) } #[allow( non_snake_case, @@ -6953,11 +7670,30 @@ pub(crate) unsafe fn i32_to_Percent_db3641cc<'a>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn i32_to_jint_a3e3b6ef<'a>( +pub(crate) unsafe fn f64_2_to_JDoubleArray_dc30d1f9<'a>( env: &mut jni::JNIEnv<'a>, - v: i32, -) -> ::core::result::Result { - Ok(v as jni::sys::jint) + v: [f64; 2], +) -> ::core::result::Result, __JniErr> { + Ok({ + let __buf: ::std::vec::Vec = v + .iter() + .map(|__x| *__x as jni::sys::jdouble) + .collect(); + let __arr = env + .new_double_array(__buf.len() as jni::sys::jsize) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array encode: {}", e)) + })?; + env.set_double_array_region(&__arr, 0, &__buf) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array encode: {}", e)) + })?; + __arr + }) } #[allow( non_snake_case, @@ -6970,11 +7706,11 @@ pub(crate) unsafe fn i32_to_jint_a3e3b6ef<'a>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn i64_to_Millis_bb88777a<'a>( +pub(crate) unsafe fn f64_to_jdouble_9e4a8f70<'a>( env: &mut jni::JNIEnv<'a>, - v: i64, -) -> ::core::result::Result { - Ok(cov_helpers::millis_from_long(v)) + v: f64, +) -> ::core::result::Result { + Ok(v as jni::sys::jdouble) } #[allow( non_snake_case, @@ -6987,11 +7723,30 @@ pub(crate) unsafe fn i64_to_Millis_bb88777a<'a>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn i64_to_jlong_fbf9a9bc<'a>( +pub(crate) unsafe fn i16_2_to_JShortArray_098f4ad5<'a>( env: &mut jni::JNIEnv<'a>, - v: i64, -) -> ::core::result::Result { - Ok(v as jni::sys::jlong) + v: [i16; 2], +) -> ::core::result::Result, __JniErr> { + Ok({ + let __buf: ::std::vec::Vec = v + .iter() + .map(|__x| *__x as jni::sys::jshort) + .collect(); + let __arr = env + .new_short_array(__buf.len() as jni::sys::jsize) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array encode: {}", e)) + })?; + env.set_short_array_region(&__arr, 0, &__buf) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array encode: {}", e)) + })?; + __arr + }) } #[allow( non_snake_case, @@ -7004,11 +7759,30 @@ pub(crate) unsafe fn i64_to_jlong_fbf9a9bc<'a>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn jboolean_to_bool_31306d98<'env, 'v>( - env: &mut jni::JNIEnv<'env>, - v: &jni::sys::jboolean, -) -> ::core::result::Result { - Ok(*v != 0) +pub(crate) unsafe fn i32_3_to_JIntArray_60e5e35a<'a>( + env: &mut jni::JNIEnv<'a>, + v: [i32; 3], +) -> ::core::result::Result, __JniErr> { + Ok({ + let __buf: ::std::vec::Vec = v + .iter() + .map(|__x| *__x as jni::sys::jint) + .collect(); + let __arr = env + .new_int_array(__buf.len() as jni::sys::jsize) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array encode: {}", e)) + })?; + env.set_int_array_region(&__arr, 0, &__buf) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array encode: {}", e)) + })?; + __arr + }) } #[allow( non_snake_case, @@ -7021,11 +7795,11 @@ pub(crate) unsafe fn jboolean_to_bool_31306d98<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn jdouble_to_f64_9e4a8f70<'env, 'v>( - env: &mut jni::JNIEnv<'env>, - v: &jni::sys::jdouble, -) -> ::core::result::Result { - Ok(*v) +pub(crate) unsafe fn i32_to_Celsius_8c363100<'a>( + env: &mut jni::JNIEnv<'a>, + v: i32, +) -> ::core::result::Result { + Ok(>::into(v)) } #[allow( non_snake_case, @@ -7038,31 +7812,172 @@ pub(crate) unsafe fn jdouble_to_f64_9e4a8f70<'env, 'v>( clippy::nonminimal_bool, clippy::eq_op )] -pub(crate) unsafe fn jint_to_Priority_447102d2<'env, 'v>( - env: &mut jni::JNIEnv<'env>, - v: &jni::sys::jint, -) -> ::core::result::Result { - Ok({ - match *v as i64 { - 0 => perftest_flat::Priority::Low, - 1 => perftest_flat::Priority::Normal, - 2 => perftest_flat::Priority::High, - other => { - return ::core::result::Result::Err( - <__JniErr as ::core::convert::From< - String, - >>::from(format!("invalid {} discriminant: {}", "Priority", other)), - ); - } - } - }) -} -#[allow( - non_snake_case, - unused_mut, - unused_variables, - unused_braces, - dead_code, +pub(crate) unsafe fn i32_to_Percent_db3641cc<'a>( + env: &mut jni::JNIEnv<'a>, + v: i32, +) -> ::core::result::Result< + perftest_flat::Percent, + >::Error, +> { + >::try_into(v) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn i32_to_jint_a3e3b6ef<'a>( + env: &mut jni::JNIEnv<'a>, + v: i32, +) -> ::core::result::Result { + Ok(v as jni::sys::jint) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn i64_2_to_JLongArray_73596912<'a>( + env: &mut jni::JNIEnv<'a>, + v: [i64; 2], +) -> ::core::result::Result, __JniErr> { + Ok({ + let __buf: ::std::vec::Vec = v + .iter() + .map(|__x| *__x as jni::sys::jlong) + .collect(); + let __arr = env + .new_long_array(__buf.len() as jni::sys::jsize) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array encode: {}", e)) + })?; + env.set_long_array_region(&__arr, 0, &__buf) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array encode: {}", e)) + })?; + __arr + }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn i64_to_Millis_bb88777a<'a>( + env: &mut jni::JNIEnv<'a>, + v: i64, +) -> ::core::result::Result { + Ok(cov_helpers::millis_from_long(v)) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn i64_to_jlong_fbf9a9bc<'a>( + env: &mut jni::JNIEnv<'a>, + v: i64, +) -> ::core::result::Result { + Ok(v as jni::sys::jlong) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn jboolean_to_bool_31306d98<'env, 'v>( + env: &mut jni::JNIEnv<'env>, + v: &jni::sys::jboolean, +) -> ::core::result::Result { + Ok(*v != 0) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn jdouble_to_f64_9e4a8f70<'env, 'v>( + env: &mut jni::JNIEnv<'env>, + v: &jni::sys::jdouble, +) -> ::core::result::Result { + Ok(*v) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] +pub(crate) unsafe fn jint_to_Priority_447102d2<'env, 'v>( + env: &mut jni::JNIEnv<'env>, + v: &jni::sys::jint, +) -> ::core::result::Result { + Ok({ + match *v as i64 { + 0 => perftest_flat::Priority::Low, + 1 => perftest_flat::Priority::Normal, + 2 => perftest_flat::Priority::High, + other => { + return ::core::result::Result::Err( + <__JniErr as ::core::convert::From< + String, + >>::from(format!("invalid {} discriminant: {}", "Priority", other)), + ); + } + } + }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, clippy::needless_question_mark, clippy::let_and_return, clippy::nonminimal_bool, @@ -7541,6 +8456,42 @@ pub(crate) unsafe fn u32_to_jlong_9594a230<'a>( clippy::nonminimal_bool, clippy::eq_op )] +pub(crate) unsafe fn u64_2_to_JLongArray_60bcc6a5<'a>( + env: &mut jni::JNIEnv<'a>, + v: [u64; 2], +) -> ::core::result::Result, __JniErr> { + Ok({ + let __buf: ::std::vec::Vec = v + .iter() + .map(|__x| *__x as jni::sys::jlong) + .collect(); + let __arr = env + .new_long_array(__buf.len() as jni::sys::jsize) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array encode: {}", e)) + })?; + env.set_long_array_region(&__arr, 0, &__buf) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array encode: {}", e)) + })?; + __arr + }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] pub(crate) unsafe fn u64_to_Duration_7c0845f9<'a>( env: &mut jni::JNIEnv<'a>, v: u64, @@ -7589,6 +8540,30 @@ pub(crate) unsafe fn u64_to_jlong_4384a5d6<'a>( clippy::nonminimal_bool, clippy::eq_op )] +pub(crate) unsafe fn u8_4_to_JByteArray_39abedfa<'a>( + env: &mut jni::JNIEnv<'a>, + v: [u8; 4], +) -> ::core::result::Result, __JniErr> { + Ok({ + env.byte_array_from_slice(&v) + .map_err(|e| { + <__JniErr as ::core::convert::From< + String, + >>::from(format!("fixed-size array encode: {}", e)) + })? + }) +} +#[allow( + non_snake_case, + unused_mut, + unused_variables, + unused_braces, + dead_code, + clippy::needless_question_mark, + clippy::let_and_return, + clippy::nonminimal_bool, + clippy::eq_op +)] pub(crate) unsafe fn u8_to_jint_553cf6ec<'a>( env: &mut jni::JNIEnv<'a>, v: u8, @@ -9387,23 +10362,558 @@ pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_archiveReadingMa } } } - ::core::option::Option::None => jni::objects::JObject::null().into(), + ::core::option::Option::None => jni::objects::JObject::null().into(), + } +} +#[no_mangle] +#[allow(non_snake_case, unused_mut, unused_variables, dead_code)] +pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_archiveSetReading<'a>( + mut env: jni::JNIEnv<'a>, + _class: jni::objects::JClass<'a>, + a: jni::sys::jlong, + which: jni::sys::jint, + __error_sink: jni::objects::JObject<'a>, +) -> () { + #[allow(non_upper_case_globals)] + static __SINK_MID: ::prebindgen::lang::CachedIfaceMethod = ::prebindgen::lang::CachedIfaceMethod::new(); + const __SINK_FQN: &str = "io/prebindgen/covertest/JniErrorHandler"; + const __SINK_DESCR: &str = "(Ljava/lang/String;)Ljava/lang/Object;"; + let mut a = match jlong_to_Archive_cd73502c(&mut env, &a) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return (); + } + }; + let which = match jint_to_i32_a3e3b6ef(&mut env, &which) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return (); + } + }; + let __out = perftest_flat::archive_set_reading(&mut a, which); + match unit_to_unit_9ecccf8e(&mut env, __out) { + ::core::result::Result::Ok(__w) => __w, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + () + } + } +} +#[no_mangle] +#[allow(non_snake_case, unused_mut, unused_variables, dead_code)] +pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_archiveStore<'a>( + mut env: jni::JNIEnv<'a>, + _class: jni::objects::JClass<'a>, + a: jni::sys::jlong, + s_sel: jni::sys::jint, + s_0_0_present: jni::sys::jboolean, + s_0_0_value: jni::sys::jlong, + s_0_1_present: jni::sys::jboolean, + s_0_1_value: jni::sys::jdouble, + s_1: jni::sys::jlong, + __error_sink: jni::objects::JObject<'a>, +) -> () { + #[allow(non_upper_case_globals)] + static __SINK_MID: ::prebindgen::lang::CachedIfaceMethod = ::prebindgen::lang::CachedIfaceMethod::new(); + const __SINK_FQN: &str = "io/prebindgen/covertest/JniErrorHandler"; + const __SINK_DESCR: &str = "(Ljava/lang/String;)Ljava/lang/Object;"; + let mut a = match jlong_to_Archive_cd73502c(&mut env, &a) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return (); + } + }; + let __exp_s_sel = match jint_to_i32_a3e3b6ef(&mut env, &s_sel) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return (); + } + }; + let __exp_s_0_0: Option = if s_0_0_present != 0u8 { + let __v = match jlong_to_i64_fbf9a9bc(&mut env, &s_0_0_value) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return (); + } + }; + ::core::option::Option::Some(__v) + } else { + ::core::option::Option::None + }; + let __exp_s_0_1: Option = if s_0_1_present != 0u8 { + let __v = match jdouble_to_f64_9e4a8f70(&mut env, &s_0_1_value) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return (); + } + }; + ::core::option::Option::Some(__v) + } else { + ::core::option::Option::None + }; + let __exp_s_1 = match jlong_to_Option_Summary_252ef2ba(&mut env, &s_1) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return (); + } + }; + let __folded_s = match { + match __exp_s_sel { + 0i32 => { + match (__exp_s_0_0, __exp_s_0_1) { + ( + ::core::option::Option::Some(__p0), + ::core::option::Option::Some(__p1), + ) => { + ::core::result::Result::Ok( + perftest_flat::summary_new(__p0, __p1), + ) + } + _ => { + ::core::result::Result::Err( + ::std::string::String::from( + "constructor variant input missing", + ), + ) + } + } + } + 1i32 => { + match __exp_s_1 { + ::core::option::Option::Some(__v) => ::core::result::Result::Ok(__v), + ::core::option::Option::None => { + ::core::result::Result::Err( + ::std::string::String::from("identity variant value missing"), + ) + } + } + } + __sel => { + ::core::result::Result::Err( + ::std::format!("invalid constructor selector: {}", __sel), + ) + } + } + } { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + let __je = <__JniErr as ::core::convert::From< + ::std::string::String, + >>::from(__e); + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__je.to_string(), + ); + return (); + } + }; + let __out = perftest_flat::archive_store(&mut a, __folded_s); + match unit_to_unit_9ecccf8e(&mut env, __out) { + ::core::result::Result::Ok(__w) => __w, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + () + } + } +} +#[no_mangle] +#[allow(non_snake_case, unused_mut, unused_variables, dead_code)] +pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_arraysEcho<'a>( + mut env: jni::JNIEnv<'a>, + _class: jni::objects::JClass<'a>, + a_bytes: jni::objects::JByteArray<'a>, + a_shorts: jni::objects::JShortArray<'a>, + a_ints: jni::objects::JIntArray<'a>, + a_longs: jni::objects::JLongArray<'a>, + a_doubles: jni::objects::JDoubleArray<'a>, + a_flags: jni::objects::JBooleanArray<'a>, + a_raw: jni::objects::JLongArray<'a>, + __builder: jni::objects::JObject<'a>, + __error_sink: jni::objects::JObject<'a>, +) -> jni::objects::JObject<'a> { + #[allow(non_upper_case_globals)] + static __SINK_MID: ::prebindgen::lang::CachedIfaceMethod = ::prebindgen::lang::CachedIfaceMethod::new(); + const __SINK_FQN: &str = "io/prebindgen/covertest/JniErrorHandler"; + const __SINK_DESCR: &str = "(Ljava/lang/String;)Ljava/lang/Object;"; + let __flat_a_bytes = match JByteArray_to_u8_4_39abedfa(&mut env, &a_bytes) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + let __flat_a_shorts = match JShortArray_to_i16_2_098f4ad5(&mut env, &a_shorts) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + let __flat_a_ints = match JIntArray_to_i32_3_60e5e35a(&mut env, &a_ints) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + let __flat_a_longs = match JLongArray_to_i64_2_73596912(&mut env, &a_longs) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + let __flat_a_doubles = match JDoubleArray_to_f64_2_dc30d1f9(&mut env, &a_doubles) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + let __flat_a_flags = match JBooleanArray_to_bool_3_3f960c58(&mut env, &a_flags) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + let __flat_a_raw = match JLongArray_to_u64_2_60bcc6a5(&mut env, &a_raw) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + let __flat_a = perftest_flat::Arrays { + bytes: __flat_a_bytes, + shorts: __flat_a_shorts, + ints: __flat_a_ints, + longs: __flat_a_longs, + doubles: __flat_a_doubles, + flags: __flat_a_flags, + raw: __flat_a_raw, + }; + let a = __flat_a; + #[allow(non_upper_case_globals)] + static __CB_MID: ::prebindgen::lang::CachedIfaceMethod = ::prebindgen::lang::CachedIfaceMethod::new(); + const __CB_FQN: &str = "io/prebindgen/covertest/model/ArraysBuilder"; + const __CB_DESCR: &str = "([B[S[I[J[D[Z[J)Ljava/lang/Object;"; + let __out = perftest_flat::arrays_echo(a); + let __obj0: jni::objects::JObject = { + let __enc0 = match u8_4_to_JByteArray_39abedfa(&mut env, __out.bytes.clone()) { + ::core::result::Result::Ok(__w) => __w, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + __enc0.into() + }; + let __obj1: jni::objects::JObject = { + let __enc1 = match i16_2_to_JShortArray_098f4ad5( + &mut env, + __out.shorts.clone(), + ) { + ::core::result::Result::Ok(__w) => __w, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + __enc1.into() + }; + let __obj2: jni::objects::JObject = { + let __enc2 = match i32_3_to_JIntArray_60e5e35a(&mut env, __out.ints.clone()) { + ::core::result::Result::Ok(__w) => __w, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + __enc2.into() + }; + let __obj3: jni::objects::JObject = { + let __enc3 = match i64_2_to_JLongArray_73596912(&mut env, __out.longs.clone()) { + ::core::result::Result::Ok(__w) => __w, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + __enc3.into() + }; + let __obj4: jni::objects::JObject = { + let __enc4 = match f64_2_to_JDoubleArray_dc30d1f9( + &mut env, + __out.doubles.clone(), + ) { + ::core::result::Result::Ok(__w) => __w, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + __enc4.into() + }; + let __obj5: jni::objects::JObject = { + let __enc5 = match bool_3_to_JBooleanArray_3f960c58( + &mut env, + __out.flags.clone(), + ) { + ::core::result::Result::Ok(__w) => __w, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + __enc5.into() + }; + let __obj6: jni::objects::JObject = { + let __enc6 = match u64_2_to_JLongArray_60bcc6a5(&mut env, __out.raw.clone()) { + ::core::result::Result::Ok(__w) => __w, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + __enc6.into() + }; + match __CB_MID + .call_object( + &mut env, + __CB_FQN, + "run", + __CB_DESCR, + &__builder, + &[ + jni::sys::jvalue { + l: __obj0.as_raw(), + }, + jni::sys::jvalue { + l: __obj1.as_raw(), + }, + jni::sys::jvalue { + l: __obj2.as_raw(), + }, + jni::sys::jvalue { + l: __obj3.as_raw(), + }, + jni::sys::jvalue { + l: __obj4.as_raw(), + }, + jni::sys::jvalue { + l: __obj5.as_raw(), + }, + jni::sys::jvalue { + l: __obj6.as_raw(), + }, + ], + ) + { + ::core::result::Result::Ok(__o) => __o, + ::core::result::Result::Err(__e) => { + let _ = env.exception_describe(); + let __e2 = <__JniErr as ::core::convert::From< + String, + >>::from(__e.to_string()); + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e2.to_string(), + ); + jni::objects::JObject::null().into() + } } } #[no_mangle] #[allow(non_snake_case, unused_mut, unused_variables, dead_code)] -pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_archiveSetReading<'a>( +pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_blobValueEcho<'a>( mut env: jni::JNIEnv<'a>, _class: jni::objects::JClass<'a>, - a: jni::sys::jlong, - which: jni::sys::jint, + value: jni::objects::JObject<'a>, + __builder: jni::objects::JObject<'a>, __error_sink: jni::objects::JObject<'a>, -) -> () { +) -> jni::objects::JObject<'a> { #[allow(non_upper_case_globals)] static __SINK_MID: ::prebindgen::lang::CachedIfaceMethod = ::prebindgen::lang::CachedIfaceMethod::new(); const __SINK_FQN: &str = "io/prebindgen/covertest/JniErrorHandler"; const __SINK_DESCR: &str = "(Ljava/lang/String;)Ljava/lang/Object;"; - let mut a = match jlong_to_Archive_cd73502c(&mut env, &a) { + let value = match JObject_to_BlobValue_89b5dab7(&mut env, &value) { ::core::result::Result::Ok(__v) => __v, ::core::result::Result::Err(__e) => { signal_binding_error( @@ -9414,58 +10924,138 @@ pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_archiveSetReadin __SINK_DESCR, &__e.to_string(), ); - return (); + return jni::objects::JObject::null().into(); } }; - let which = match jint_to_i32_a3e3b6ef(&mut env, &which) { - ::core::result::Result::Ok(__v) => __v, - ::core::result::Result::Err(__e) => { - signal_binding_error( - &mut env, - &__error_sink, - &__SINK_MID, - __SINK_FQN, - __SINK_DESCR, - &__e.to_string(), - ); - return (); - } + #[allow(non_upper_case_globals)] + static __CB_MID: ::prebindgen::lang::CachedIfaceMethod = ::prebindgen::lang::CachedIfaceMethod::new(); + const __CB_FQN: &str = "io/prebindgen/covertest/model/BlobValueBuilder"; + const __CB_DESCR: &str = "(JJ[BLjava/util/List;)Ljava/lang/Object;"; + let __out = perftest_flat::blob_value_echo(value); + let __obj0: jni::sys::jvalue = { + let __enc0 = match i64_to_jlong_fbf9a9bc(&mut env, __out.stamp.secs.clone()) { + ::core::result::Result::Ok(__w) => __w, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + jni::sys::jvalue { j: __enc0 } }; - let __out = perftest_flat::archive_set_reading(&mut a, which); - match unit_to_unit_9ecccf8e(&mut env, __out) { - ::core::result::Result::Ok(__w) => __w, + let __obj1: jni::sys::jvalue = { + let __enc1 = match i64_to_jlong_fbf9a9bc(&mut env, __out.stamp.nanos.clone()) { + ::core::result::Result::Ok(__w) => __w, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + jni::sys::jvalue { j: __enc1 } + }; + let __obj2: jni::objects::JObject = { + let __enc2 = match Vec_u8_to_JByteArray_7936d5de(&mut env, __out.id.clone()) { + ::core::result::Result::Ok(__w) => __w, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + __enc2.into() + }; + let __obj3: jni::objects::JObject = { + let __enc3 = match Vec_Vec_u8_to_JObject_43404875( + &mut env, + __out.chunks.clone(), + ) { + ::core::result::Result::Ok(__w) => __w, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + __enc3 + }; + match __CB_MID + .call_object( + &mut env, + __CB_FQN, + "run", + __CB_DESCR, + &__builder, + &[ + __obj0, + __obj1, + jni::sys::jvalue { + l: __obj2.as_raw(), + }, + jni::sys::jvalue { + l: __obj3.as_raw(), + }, + ], + ) + { + ::core::result::Result::Ok(__o) => __o, ::core::result::Result::Err(__e) => { + let _ = env.exception_describe(); + let __e2 = <__JniErr as ::core::convert::From< + String, + >>::from(__e.to_string()); signal_binding_error( &mut env, &__error_sink, &__SINK_MID, __SINK_FQN, __SINK_DESCR, - &__e.to_string(), + &__e2.to_string(), ); - () + jni::objects::JObject::null().into() } } } #[no_mangle] #[allow(non_snake_case, unused_mut, unused_variables, dead_code)] -pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_archiveStore<'a>( +pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_blobValueNew<'a>( mut env: jni::JNIEnv<'a>, _class: jni::objects::JClass<'a>, - a: jni::sys::jlong, - s_sel: jni::sys::jint, - s_0_0_present: jni::sys::jboolean, - s_0_0_value: jni::sys::jlong, - s_0_1_present: jni::sys::jboolean, - s_0_1_value: jni::sys::jdouble, - s_1: jni::sys::jlong, + secs: jni::sys::jlong, + id: jni::objects::JByteArray<'a>, + chunks: jni::objects::JObject<'a>, + __builder: jni::objects::JObject<'a>, __error_sink: jni::objects::JObject<'a>, -) -> () { +) -> jni::objects::JObject<'a> { #[allow(non_upper_case_globals)] static __SINK_MID: ::prebindgen::lang::CachedIfaceMethod = ::prebindgen::lang::CachedIfaceMethod::new(); const __SINK_FQN: &str = "io/prebindgen/covertest/JniErrorHandler"; const __SINK_DESCR: &str = "(Ljava/lang/String;)Ljava/lang/Object;"; - let mut a = match jlong_to_Archive_cd73502c(&mut env, &a) { + let secs = match jlong_to_i64_fbf9a9bc(&mut env, &secs) { ::core::result::Result::Ok(__v) => __v, ::core::result::Result::Err(__e) => { signal_binding_error( @@ -9476,10 +11066,10 @@ pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_archiveStore<'a> __SINK_DESCR, &__e.to_string(), ); - return (); + return jni::objects::JObject::null().into(); } }; - let __exp_s_sel = match jint_to_i32_a3e3b6ef(&mut env, &s_sel) { + let id = match JByteArray_to_Vec_u8_7936d5de(&mut env, &id) { ::core::result::Result::Ok(__v) => __v, ::core::result::Result::Err(__e) => { signal_binding_error( @@ -9490,12 +11080,65 @@ pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_archiveStore<'a> __SINK_DESCR, &__e.to_string(), ); - return (); + return jni::objects::JObject::null().into(); + } + }; + let chunks = match JObject_to_Vec_Vec_u8_43404875(&mut env, &chunks) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); } }; - let __exp_s_0_0: Option = if s_0_0_present != 0u8 { - let __v = match jlong_to_i64_fbf9a9bc(&mut env, &s_0_0_value) { - ::core::result::Result::Ok(__v) => __v, + #[allow(non_upper_case_globals)] + static __CB_MID: ::prebindgen::lang::CachedIfaceMethod = ::prebindgen::lang::CachedIfaceMethod::new(); + const __CB_FQN: &str = "io/prebindgen/covertest/model/BlobValueBuilder"; + const __CB_DESCR: &str = "(JJ[BLjava/util/List;)Ljava/lang/Object;"; + let __out = perftest_flat::blob_value_new(secs, id, chunks); + let __obj0: jni::sys::jvalue = { + let __enc0 = match i64_to_jlong_fbf9a9bc(&mut env, __out.stamp.secs.clone()) { + ::core::result::Result::Ok(__w) => __w, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + jni::sys::jvalue { j: __enc0 } + }; + let __obj1: jni::sys::jvalue = { + let __enc1 = match i64_to_jlong_fbf9a9bc(&mut env, __out.stamp.nanos.clone()) { + ::core::result::Result::Ok(__w) => __w, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + jni::sys::jvalue { j: __enc1 } + }; + let __obj2: jni::objects::JObject = { + let __enc2 = match Vec_u8_to_JByteArray_7936d5de(&mut env, __out.id.clone()) { + ::core::result::Result::Ok(__w) => __w, ::core::result::Result::Err(__e) => { signal_binding_error( &mut env, @@ -9505,16 +11148,17 @@ pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_archiveStore<'a> __SINK_DESCR, &__e.to_string(), ); - return (); + return jni::objects::JObject::null().into(); } }; - ::core::option::Option::Some(__v) - } else { - ::core::option::Option::None + __enc2.into() }; - let __exp_s_0_1: Option = if s_0_1_present != 0u8 { - let __v = match jdouble_to_f64_9e4a8f70(&mut env, &s_0_1_value) { - ::core::result::Result::Ok(__v) => __v, + let __obj3: jni::objects::JObject = { + let __enc3 = match Vec_Vec_u8_to_JObject_43404875( + &mut env, + __out.chunks.clone(), + ) { + ::core::result::Result::Ok(__w) => __w, ::core::result::Result::Err(__e) => { signal_binding_error( &mut env, @@ -9524,94 +11168,45 @@ pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_archiveStore<'a> __SINK_DESCR, &__e.to_string(), ); - return (); + return jni::objects::JObject::null().into(); } }; - ::core::option::Option::Some(__v) - } else { - ::core::option::Option::None - }; - let __exp_s_1 = match jlong_to_Option_Summary_252ef2ba(&mut env, &s_1) { - ::core::result::Result::Ok(__v) => __v, - ::core::result::Result::Err(__e) => { - signal_binding_error( - &mut env, - &__error_sink, - &__SINK_MID, - __SINK_FQN, - __SINK_DESCR, - &__e.to_string(), - ); - return (); - } - }; - let __folded_s = match { - match __exp_s_sel { - 0i32 => { - match (__exp_s_0_0, __exp_s_0_1) { - ( - ::core::option::Option::Some(__p0), - ::core::option::Option::Some(__p1), - ) => { - ::core::result::Result::Ok( - perftest_flat::summary_new(__p0, __p1), - ) - } - _ => { - ::core::result::Result::Err( - ::std::string::String::from( - "constructor variant input missing", - ), - ) - } - } - } - 1i32 => { - match __exp_s_1 { - ::core::option::Option::Some(__v) => ::core::result::Result::Ok(__v), - ::core::option::Option::None => { - ::core::result::Result::Err( - ::std::string::String::from("identity variant value missing"), - ) - } - } - } - __sel => { - ::core::result::Result::Err( - ::std::format!("invalid constructor selector: {}", __sel), - ) - } - } - } { - ::core::result::Result::Ok(__v) => __v, - ::core::result::Result::Err(__e) => { - let __je = <__JniErr as ::core::convert::From< - ::std::string::String, - >>::from(__e); - signal_binding_error( - &mut env, - &__error_sink, - &__SINK_MID, - __SINK_FQN, - __SINK_DESCR, - &__je.to_string(), - ); - return (); - } + __enc3 }; - let __out = perftest_flat::archive_store(&mut a, __folded_s); - match unit_to_unit_9ecccf8e(&mut env, __out) { - ::core::result::Result::Ok(__w) => __w, + match __CB_MID + .call_object( + &mut env, + __CB_FQN, + "run", + __CB_DESCR, + &__builder, + &[ + __obj0, + __obj1, + jni::sys::jvalue { + l: __obj2.as_raw(), + }, + jni::sys::jvalue { + l: __obj3.as_raw(), + }, + ], + ) + { + ::core::result::Result::Ok(__o) => __o, ::core::result::Result::Err(__e) => { + let _ = env.exception_describe(); + let __e2 = <__JniErr as ::core::convert::From< + String, + >>::from(__e.to_string()); signal_binding_error( &mut env, &__error_sink, &__SINK_MID, __SINK_FQN, __SINK_DESCR, - &__e.to_string(), + &__e2.to_string(), ); - () + jni::objects::JObject::null().into() } } } @@ -12608,14 +14203,29 @@ pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_readingSeries<'a pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_stampNanos<'a>( mut env: jni::JNIEnv<'a>, _class: jni::objects::JClass<'a>, - s: jni::objects::JByteArray<'a>, + s_secs: jni::sys::jlong, + s_nanos: jni::sys::jlong, __error_sink: jni::objects::JObject<'a>, ) -> jni::sys::jlong { #[allow(non_upper_case_globals)] static __SINK_MID: ::prebindgen::lang::CachedIfaceMethod = ::prebindgen::lang::CachedIfaceMethod::new(); const __SINK_FQN: &str = "io/prebindgen/covertest/JniErrorHandler"; const __SINK_DESCR: &str = "(Ljava/lang/String;)Ljava/lang/Object;"; - let s = match JByteArray_to_Stamp_2fc9bd18(&mut env, &s) { + let __flat_s_secs = match jlong_to_i64_fbf9a9bc(&mut env, &s_secs) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return 0 as jni::sys::jlong; + } + }; + let __flat_s_nanos = match jlong_to_i64_fbf9a9bc(&mut env, &s_nanos) { ::core::result::Result::Ok(__v) => __v, ::core::result::Result::Err(__e) => { signal_binding_error( @@ -12629,6 +14239,11 @@ pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_stampNanos<'a>( return 0 as jni::sys::jlong; } }; + let __flat_s = perftest_flat::Stamp { + secs: __flat_s_secs, + nanos: __flat_s_nanos, + }; + let s = __flat_s; let __out = perftest_flat::stamp_nanos(&s); match i64_to_jlong_fbf9a9bc(&mut env, __out) { ::core::result::Result::Ok(__w) => __w, @@ -12652,8 +14267,9 @@ pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_stampNew<'a>( _class: jni::objects::JClass<'a>, secs: jni::sys::jlong, nanos: jni::sys::jlong, + __builder: jni::objects::JObject<'a>, __error_sink: jni::objects::JObject<'a>, -) -> jni::objects::JByteArray<'a> { +) -> jni::objects::JObject<'a> { #[allow(non_upper_case_globals)] static __SINK_MID: ::prebindgen::lang::CachedIfaceMethod = ::prebindgen::lang::CachedIfaceMethod::new(); const __SINK_FQN: &str = "io/prebindgen/covertest/JniErrorHandler"; @@ -12686,17 +14302,68 @@ pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_stampNew<'a>( return jni::objects::JObject::null().into(); } }; + #[allow(non_upper_case_globals)] + static __CB_MID: ::prebindgen::lang::CachedIfaceMethod = ::prebindgen::lang::CachedIfaceMethod::new(); + const __CB_FQN: &str = "io/prebindgen/covertest/model/StampBuilder"; + const __CB_DESCR: &str = "(JJ)Ljava/lang/Object;"; let __out = perftest_flat::stamp_new(secs, nanos); - match Stamp_to_JByteArray_2fc9bd18(&mut env, __out) { - ::core::result::Result::Ok(__w) => __w, + let __obj0: jni::sys::jvalue = { + let __enc0 = match i64_to_jlong_fbf9a9bc(&mut env, __out.secs.clone()) { + ::core::result::Result::Ok(__w) => __w, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + jni::sys::jvalue { j: __enc0 } + }; + let __obj1: jni::sys::jvalue = { + let __enc1 = match i64_to_jlong_fbf9a9bc(&mut env, __out.nanos.clone()) { + ::core::result::Result::Ok(__w) => __w, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + jni::sys::jvalue { j: __enc1 } + }; + match __CB_MID + .call_object( + &mut env, + __CB_FQN, + "run", + __CB_DESCR, + &__builder, + &[__obj0, __obj1], + ) + { + ::core::result::Result::Ok(__o) => __o, ::core::result::Result::Err(__e) => { + let _ = env.exception_describe(); + let __e2 = <__JniErr as ::core::convert::From< + String, + >>::from(__e.to_string()); signal_binding_error( &mut env, &__error_sink, &__SINK_MID, __SINK_FQN, __SINK_DESCR, - &__e.to_string(), + &__e2.to_string(), ); jni::objects::JObject::null().into() } @@ -12707,14 +14374,29 @@ pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_stampNew<'a>( pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_stampSecs<'a>( mut env: jni::JNIEnv<'a>, _class: jni::objects::JClass<'a>, - s: jni::objects::JByteArray<'a>, + s_secs: jni::sys::jlong, + s_nanos: jni::sys::jlong, __error_sink: jni::objects::JObject<'a>, ) -> jni::sys::jlong { #[allow(non_upper_case_globals)] static __SINK_MID: ::prebindgen::lang::CachedIfaceMethod = ::prebindgen::lang::CachedIfaceMethod::new(); const __SINK_FQN: &str = "io/prebindgen/covertest/JniErrorHandler"; const __SINK_DESCR: &str = "(Ljava/lang/String;)Ljava/lang/Object;"; - let s = match JByteArray_to_Stamp_2fc9bd18(&mut env, &s) { + let __flat_s_secs = match jlong_to_i64_fbf9a9bc(&mut env, &s_secs) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return 0 as jni::sys::jlong; + } + }; + let __flat_s_nanos = match jlong_to_i64_fbf9a9bc(&mut env, &s_nanos) { ::core::result::Result::Ok(__v) => __v, ::core::result::Result::Err(__e) => { signal_binding_error( @@ -12728,6 +14410,11 @@ pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_stampSecs<'a>( return 0 as jni::sys::jlong; } }; + let __flat_s = perftest_flat::Stamp { + secs: __flat_s_secs, + nanos: __flat_s_nanos, + }; + let s = __flat_s; let __out = perftest_flat::stamp_secs(&s); match i64_to_jlong_fbf9a9bc(&mut env, __out) { ::core::result::Result::Ok(__w) => __w, @@ -12775,12 +14462,12 @@ pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_stampSeries<'a>( #[allow(non_upper_case_globals)] static __CB_MID: ::prebindgen::lang::CachedIfaceMethod = ::prebindgen::lang::CachedIfaceMethod::new(); const __CB_FQN: &str = "io/prebindgen/covertest/model/StampFolderRaw"; - const __CB_DESCR: &str = "(Ljava/lang/Object;[B)Ljava/lang/Object;"; + const __CB_DESCR: &str = "(Ljava/lang/Object;JJ)Ljava/lang/Object;"; let __vec = perftest_flat::stamp_series(count); let mut __acc = __acc; for __elem in __vec.into_iter() { - let __enc = { - match Stamp_to_JByteArray_2fc9bd18(&mut env, __elem) { + let __obj0: jni::sys::jvalue = { + let __enc0 = match i64_to_jlong_fbf9a9bc(&mut env, __elem.secs.clone()) { ::core::result::Result::Ok(__w) => __w, ::core::result::Result::Err(__e) => { signal_binding_error( @@ -12793,9 +14480,26 @@ pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_stampSeries<'a>( ); return jni::objects::JObject::null().into(); } - } + }; + jni::sys::jvalue { j: __enc0 } + }; + let __obj1: jni::sys::jvalue = { + let __enc1 = match i64_to_jlong_fbf9a9bc(&mut env, __elem.nanos.clone()) { + ::core::result::Result::Ok(__w) => __w, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return jni::objects::JObject::null().into(); + } + }; + jni::sys::jvalue { j: __enc1 } }; - let __obj: jni::objects::JObject = __enc.into(); __acc = match __CB_MID .call_object( &mut env, @@ -12807,9 +14511,8 @@ pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_stampSeries<'a>( jni::sys::jvalue { l: __acc.as_raw(), }, - jni::sys::jvalue { - l: __obj.as_raw(), - }, + __obj0, + __obj1, ], ) { @@ -15098,7 +16801,9 @@ pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_storageTotalLen< pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_storageTryFromStamp<'a>( mut env: jni::JNIEnv<'a>, _class: jni::objects::JClass<'a>, - s: jni::objects::JByteArray<'a>, + s_secs: jni::sys::jlong, + s_nanos: jni::sys::jlong, + tag: jni::objects::JByteArray<'a>, __error_sink: jni::objects::JObject<'a>, __domain_sink: jni::objects::JObject<'a>, ) -> jni::sys::jlong { @@ -15110,7 +16815,40 @@ pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_storageTryFromSt static __DSINK_MID: ::prebindgen::lang::CachedIfaceMethod = ::prebindgen::lang::CachedIfaceMethod::new(); const __DSINK_FQN: &str = "io/prebindgen/covertest/errors/StorageErrorHandlerRaw"; const __DSINK_DESCR: &str = "(Ljava/lang/String;J)Ljava/lang/Object;"; - let s = match JByteArray_to_Stamp_2fc9bd18(&mut env, &s) { + let __flat_s_secs = match jlong_to_i64_fbf9a9bc(&mut env, &s_secs) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return 0 as jni::sys::jlong; + } + }; + let __flat_s_nanos = match jlong_to_i64_fbf9a9bc(&mut env, &s_nanos) { + ::core::result::Result::Ok(__v) => __v, + ::core::result::Result::Err(__e) => { + signal_binding_error( + &mut env, + &__error_sink, + &__SINK_MID, + __SINK_FQN, + __SINK_DESCR, + &__e.to_string(), + ); + return 0 as jni::sys::jlong; + } + }; + let __flat_s = perftest_flat::Stamp { + secs: __flat_s_secs, + nanos: __flat_s_nanos, + }; + let s = __flat_s; + let tag = match JByteArray_to_u8_2_9ca14e44(&mut env, &tag) { ::core::result::Result::Ok(__v) => __v, ::core::result::Result::Err(__e) => { signal_binding_error( @@ -15124,7 +16862,7 @@ pub unsafe extern "C" fn Java_io_prebindgen_covertest_CovNative_storageTryFromSt return 0 as jni::sys::jlong; } }; - let __out = match perftest_flat::storage_try_from_stamp(s) { + let __out = match perftest_flat::storage_try_from_stamp(s, tag) { ::core::result::Result::Ok(__v) => __v, ::core::result::Result::Err(__de) => { let __eze0: jni::objects::JObject = { diff --git a/examples/perftest-flat/src/ext.rs b/examples/perftest-flat/src/ext.rs index 55698748..f429ae8c 100644 --- a/examples/perftest-flat/src/ext.rs +++ b/examples/perftest-flat/src/ext.rs @@ -11,8 +11,8 @@ //! "introspection / analytics" helpers: //! //! * [`Priority`] — a `#[repr(i32)]` enum (→ Kotlin `enum class`). -//! * [`Stamp`] — a small `Copy` value (→ Kotlin `@JvmInline value class` over a -//! `ByteArray`); `Vec` surfaces as `List`. +//! * [`Stamp`] — a small `Copy` value crossing as its scalar fields (→ Kotlin +//! `data class`); `Vec` surfaces as `List`. //! * [`StorageError`] — the `E` of a fallible `Result` (→ the `onError` channel). //! * [`Summary`] — an opaque handle whose fields decompose at the boundary //! (→ flatten-input / flatten-output). @@ -302,12 +302,12 @@ pub fn observation_which(o: Observation) -> i32 { } // ───────────────────────────────────────────────────────────────────────────── -// Stamp — a small `Copy` value type (→ Kotlin value class over raw bytes). +// Stamp — a small `Copy` value type (→ Kotlin data class over its fields). // ───────────────────────────────────────────────────────────────────────────── -/// A plain `Copy` timestamp. Declared `value_class` in the binding, so it -/// crosses **by value as its raw bytes** in a `ByteArray` (no heap handle, no -/// `close()`), and `Vec` surfaces as `List`. +/// A plain `Copy` timestamp. Declared `data_class` in the binding, so it +/// crosses **by value as its two scalar fields** (no heap handle, no +/// `close()`), and `Vec` surfaces as `List`. #[prebindgen] #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -316,26 +316,103 @@ pub struct Stamp { pub nanos: i64, } -/// Build a [`Stamp`] (value-class **return**). +/// Build a [`Stamp`] (data-class **return**). #[prebindgen] pub fn stamp_new(secs: i64, nanos: i64) -> Stamp { Stamp { secs, nanos } } -/// Seconds component (value-class **accessor**, receiver = the value bytes). +/// Seconds component (data-class **accessor**, receiver = its field leaves). #[prebindgen] pub fn stamp_secs(s: &Stamp) -> i64 { s.secs } -/// Nanoseconds component (value-class **accessor**). +/// A value whose equality is **array-backed** on the JVM side: a byte-array +/// field beside a nested data class. +/// +/// Kotlin arrays compare by identity, so the `Vec` field would make two +/// equal-content values compare unequal unless the binding emits content-based +/// operators. This mirrors the shape that broke downstream (a `Vec` struct +/// field), which nothing else here exercised. Two fields are enough: `id` is +/// array-backed and the nested [`Stamp`] — which itself crosses as its scalar +/// fields — is not, so both comparison branches are covered, and a third of +/// either kind would only repeat an emitted form. +/// +/// Field ORDER is deliberate: the array-backed fields come after `stamp`, so +/// the generated `hashCode` folds them as `31 * result + id.contentHashCode()` +/// rather than seeding the accumulator with them. That is the shape a real +/// value takes (`Timestamp(ntp64, id)`), and it is a different emitted form +/// from the array-first one. +#[prebindgen] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct BlobValue { + pub stamp: Stamp, + pub id: Vec, + /// A CONTAINER of arrays. `List` inherits `ByteArray`'s + /// identity equality just as a bare array does, so the generated operators + /// have to dig through the container rather than stopping at the property. + pub chunks: Vec>, +} + +/// Build a [`BlobValue`] (its equality is asserted from Kotlin). +#[prebindgen] +pub fn blob_value_new(secs: i64, id: Vec, chunks: Vec>) -> BlobValue { + BlobValue { + stamp: Stamp { secs, nanos: 0 }, + id, + chunks, + } +} + +/// Fixed-size arrays of every JNI-primitive element type. +/// +/// Each crosses as the matching Kotlin primitive array — bulk-copied, nothing +/// boxed — rather than through the `Vec` -> `List` path. The wider +/// unsigned field (`raw`) pins the raw-bit-pattern rule: `[u64; N]` carries its +/// bits in a `LongArray`, exactly as a scalar `u64` crosses as a raw `jlong`. +/// +/// `flags` is the one element type that is NOT a cast: a `jboolean` is a `u8`, +/// and reinterpreting an out-of-range byte as a Rust `bool` would be undefined +/// behavior, so the decode normalizes instead. +#[prebindgen] +#[derive(Clone, Debug, PartialEq)] +pub struct Arrays { + pub bytes: [u8; 4], + pub shorts: [i16; 2], + pub ints: [i32; 3], + pub longs: [i64; 2], + pub doubles: [f64; 2], + pub flags: [bool; 3], + pub raw: [u64; 2], +} + +/// Round-trip every fixed-size array shape, both directions. +#[prebindgen] +pub fn arrays_echo(a: Arrays) -> Arrays { + a +} + +/// Round-trip a [`BlobValue`] through the WHOLE-OBJECT input decoder. +/// +/// The binding marks this class `.jobject_input()`, so the decoder reads each +/// field off the Kotlin object by JVM descriptor — including the nested +/// [`Stamp`], whose slot is its own class rather than the scalar leaves it +/// flattens to everywhere else. Getting that descriptor wrong is a +/// `NoSuchFieldError` on the first decode. +#[prebindgen] +pub fn blob_value_echo(value: BlobValue) -> BlobValue { + value +} + +/// Nanoseconds component (data-class **accessor**). #[prebindgen] pub fn stamp_nanos(s: &Stamp) -> i64 { s.nanos } -/// A monotonically increasing run of stamps (`Vec` → -/// `List`). +/// A monotonically increasing run of stamps (`Vec` → +/// `List`). #[prebindgen] pub fn stamp_series(count: i64) -> Vec { (0..count.max(0)) @@ -383,13 +460,16 @@ pub fn storage_try_with_label(label: &str) -> Result { } /// Build a storage stamped with `s`, **failing** on a non-positive `secs` (a -/// domain [`StorageError`]). This takes a `Stamp` **by value** (a value-blob -/// input), so a malformed `Stamp` blob fails the input decode FIRST — the -/// binding channel — while a well-formed but rejected value fails in the domain +/// domain [`StorageError`]). +/// +/// `tag` is a fixed-size array purely so the two error channels stay separately +/// provable: a wrong-length array fails the input DECODE first — the binding +/// channel — while a well-formed but rejected `secs` fails in the domain /// channel. It is the covertest exercise for issue #45's two-caller split: one /// wrapper, both `onBindingError` and `onError` provable independently. #[prebindgen] -pub fn storage_try_from_stamp(s: Stamp) -> Result { +pub fn storage_try_from_stamp(s: Stamp, tag: [u8; 2]) -> Result { + let _ = tag; if s.secs <= 0 { return Err(StorageError { message: "stamp secs must be positive".to_string(), diff --git a/prebindgen/src/api/core/prebindgen.rs b/prebindgen/src/api/core/prebindgen.rs index 7a701897..b7d123a3 100644 --- a/prebindgen/src/api/core/prebindgen.rs +++ b/prebindgen/src/api/core/prebindgen.rs @@ -242,7 +242,7 @@ pub trait Prebindgen { /// Element types the adapter nominates for a **whole-element leaf fold**: a /// `Vec` / `Option>` return (or `impl Fn(&[T])` callback arg) whose - /// element `T` is a single boundary leaf (e.g. a String, a value blob, an + /// element `T` is a single boundary leaf (e.g. a String, a scalar, an /// opaque handle) the foreign side can reassemble from one wire value. The /// single-leaf analog of [`Self::value_struct_decons`]: consulted right after /// it and wired by [`crate::api::core::unfold::apply_leaf_vec_folds`] so the diff --git a/prebindgen/src/api/core/registry.rs b/prebindgen/src/api/core/registry.rs index c0fa7376..7656ab66 100644 --- a/prebindgen/src/api/core/registry.rs +++ b/prebindgen/src/api/core/registry.rs @@ -685,6 +685,24 @@ impl Registry { /// the item's [`SourceLocation`] stamp, or `None` when unknown — /// callers then fall /// back to [`Self::default_module`]. + /// Every **named** item the registry indexes — functions, structs, enums, + /// consts — regardless of whether the stream carried an origin stamp. + /// + /// Lives here, beside the maps, so an adapter that needs "anything the + /// source crate defines" does not enumerate item kinds itself: a new kind + /// is added once, here, instead of drifting in each adapter. Deliberately + /// NOT keyed off [`Self::item_origins`], which holds only the items whose + /// [`SourceLocation::crate_name`] was set — an origin-less hand-built + /// stream indexes items that map never sees, and callers are expected to + /// pair this with `origin_module(..).unwrap_or_else(default_module)`. + pub fn named_item_idents(&self) -> impl Iterator { + self.functions + .keys() + .chain(self.structs.keys()) + .chain(self.enums.keys()) + .chain(self.consts.keys()) + } + pub fn origin_module(&self, ident: &syn::Ident) -> Option { let crate_name = self.item_origins.get(ident)?; let module = crate_name.replace('-', "_"); @@ -1394,7 +1412,7 @@ impl Registry { crate::api::core::unfold::apply_sum_returns(self, sum_decons, &declared.functions)?; } // Single-leaf `Vec`/`&[T]` whole-element folds — the dual of the - // `data_class` folds above, for String / value-blob / handle elements + // `data_class` folds above, for String / scalar / handle elements // (so the list is built on the foreign side, not via a Rust ArrayList). let leaf_elements = ext.leaf_vec_fold_elements(self); if !leaf_elements.is_empty() { diff --git a/prebindgen/src/api/core/unfold.rs b/prebindgen/src/api/core/unfold.rs index 443afca7..cb4f96d0 100644 --- a/prebindgen/src/api/core/unfold.rs +++ b/prebindgen/src/api/core/unfold.rs @@ -70,7 +70,7 @@ pub enum DeconRecord { /// (part of) the value itself, e.g. under a binding-defined condition. LocalAcc { path: syn::Path, name: String }, /// The value itself — the handle/identity leaf (cloned for a `&T` return, - /// moved for an owned `T`, copied for a `Copy` value_blob). At most one per + /// moved for an owned `T`). At most one per /// deconstructor. Identity, } @@ -700,7 +700,7 @@ fn wire_fixed_callbacks( /// Wire **whole-element** `Iterable` fold plans for bare `Vec` / /// `Option>` returns and `impl Fn(&[T])` callback args whose element `T` -/// is a single leaf (String, value blob, opaque handle) nominated by the adapter +/// is a single leaf (String, scalar, opaque handle) nominated by the adapter /// via [`crate::api::core::prebindgen::Prebindgen::leaf_vec_fold_elements`]. Each /// such position crosses as decoupled raw leaves folded into a **foreign-built** /// list — the single-leaf dual of [`apply_value_structs`] (which handles @@ -747,7 +747,7 @@ pub fn apply_leaf_vec_folds( registry.require_output(&vec_elem, &loc); // The fold delivers the return element-by-element, so the // whole `Vec` / `Option>` converter is not needed. - // De-require it: for String / value-blob elements it still + // De-require it: for String / scalar elements it still // resolves (and is emitted as harmless dead code); for an // opaque-handle element it cannot resolve (`jlong` wire isn't // JObject-shaped), and de-requiring keeps that `None` from diff --git a/prebindgen/src/api/core/unfold/tests.rs b/prebindgen/src/api/core/unfold/tests.rs index bedd3aef..7ea9765b 100644 --- a/prebindgen/src/api/core/unfold/tests.rs +++ b/prebindgen/src/api/core/unfold/tests.rs @@ -721,7 +721,7 @@ fn iterable_whole_element_plan() { }); // M5: `z_session_peers_zid -> Vec` with a ZZenohId combined // accessor → Iterable with per-element leaves: the string form + the - // value itself via `record_id` (a `value_blob` identity, owned at the + // value itself via `record_id` (an identity leaf, owned at the // root since `Vec` owns its elements). apply( @@ -759,7 +759,7 @@ fn iterable_whole_element_plan() { fn iterable_decomposed_plan() { // M5: `z_session_peers_zid -> Vec` with a ZZenohId combined // accessor → Iterable with per-element leaves: the string form + the - // value itself via `record_id` (a `value_blob` identity, owned at the + // value itself via `record_id` (an identity leaf, owned at the // root since `Vec` owns its elements). let mut reg = reg_with(&[ "fn z_session_peers_zid(s: &ZSession) -> Vec { todo!() }", diff --git a/prebindgen/src/api/lang/jnigen/jni/builder.rs b/prebindgen/src/api/lang/jnigen/jni/builder.rs index d7cd271c..7427f558 100644 --- a/prebindgen/src/api/lang/jnigen/jni/builder.rs +++ b/prebindgen/src/api/lang/jnigen/jni/builder.rs @@ -16,7 +16,6 @@ impl DeclaredKind { DeclaredKind::Enum(_) => "enum_class", DeclaredKind::Sealed(_) => "sealed_class", DeclaredKind::Data => "data_class", - DeclaredKind::Value => "value_class", } } @@ -55,7 +54,7 @@ impl DeclaredKind { have.variant_names.extend(add.variant_names); } // Kinds with no payload of their own: nothing to merge. - (DeclaredKind::Enum(_), _) | (DeclaredKind::Data, _) | (DeclaredKind::Value, _) => {} + (DeclaredKind::Enum(_), _) | (DeclaredKind::Data, _) => {} // The discriminants were just checked equal, so no mixed pair // reaches this arm — landing here means a kind carrying options // was added above without a merge rule. @@ -347,7 +346,6 @@ impl JniGen { ClassDecl::Enum(d) => self.accept_enum_class(subpackage, d), ClassDecl::Sealed(d) => self.accept_sealed_class(subpackage, d), ClassDecl::Data(d) => self.accept_data_class(subpackage, d), - ClassDecl::Value(d) => self.accept_value_class(subpackage, d), } } @@ -501,17 +499,8 @@ impl JniGen { self.accept_members(&key, decl.members); } - fn accept_value_class(&mut self, subpackage: &str, decl: ValueClassDecl) { - let short = rust_short_name(&decl.key); - let key = decl.key; - let spec = Self::data_value_name_spec(subpackage, short, decl.name_override); - self.register_class(&key, DeclaredKind::Value, spec); - self.store_iface_opts(&key, decl.iface); - self.accept_members(&key, decl.members); - } - - /// Shared tail of the member-bearing class kinds (`ptr` / `value` / - /// `data` — every kind whose instance can re-enter Rust): each member's + /// Shared tail of the member-bearing class kinds (`ptr` / `data` — + /// every kind whose instance can re-enter Rust): each member's /// per-fn expand overrides apply exactly as a free function's would; a /// constructor member's return is additionally never output-flattened /// (it's a factory); then the members join the class's registered set. diff --git a/prebindgen/src/api/lang/jnigen/jni/classify.rs b/prebindgen/src/api/lang/jnigen/jni/classify.rs index fdd57bd6..3a7b3c41 100644 --- a/prebindgen/src/api/lang/jnigen/jni/classify.rs +++ b/prebindgen/src/api/lang/jnigen/jni/classify.rs @@ -8,7 +8,7 @@ use super::*; /// Rust type: the declared [`DeclaredKind`] when the type is declared to this /// adapter, else a registered source struct, else everything else. /// -/// The four special kinds cannot overlap — a type stores exactly one +/// The three special kinds cannot overlap — a type stores exactly one /// [`DeclaredKind`], so this is a lookup, not a precedence chain. /// `DataStruct` is any struct captured from the source crate — `cfg` tells /// whether it was also declared to the builder (a `data_class` candidate) or @@ -22,8 +22,6 @@ pub(crate) enum TypeKind<'r, 'c> { /// one leaf group per variant, surfacing as a Kotlin `sealed interface`. /// It has no single wire of its own; it crosses flattened. Sum, - /// Declared via `value_class` — raw-memory `JByteArray` wire. - ValueBlob, /// A `#[prebindgen]` struct from the source crate that is none of the /// special kinds; flattens field-by-field when emitters support it. DataStruct { @@ -35,8 +33,8 @@ pub(crate) enum TypeKind<'r, 'c> { } impl TypeConfig { - /// Declared as one of the four non-data-class kinds (`ptr_class` / - /// `enum_class` / `sealed_class` / `value_class`) — types with their own + /// Declared as one of the three non-data-class kinds (`ptr_class` / + /// `enum_class` / `sealed_class`) — types with their own /// dedicated Kotlin emitters, never flattened as data classes. pub(crate) fn special_decl(&self) -> bool { !matches!(self.kind, DeclaredKind::Data) @@ -58,7 +56,6 @@ impl JniGen { DeclaredKind::Ptr(_) => return TypeKind::Handle, DeclaredKind::Enum(_) => return TypeKind::Enum, DeclaredKind::Sealed(_) => return TypeKind::Sum, - DeclaredKind::Value => return TypeKind::ValueBlob, // A data class is exactly a declared source struct — fall // through to the registry probe below, which supplies the // `syn::ItemStruct` its emitters flatten. diff --git a/prebindgen/src/api/lang/jnigen/jni/config.rs b/prebindgen/src/api/lang/jnigen/jni/config.rs index c69d8f1a..7b5be72a 100644 --- a/prebindgen/src/api/lang/jnigen/jni/config.rs +++ b/prebindgen/src/api/lang/jnigen/jni/config.rs @@ -25,7 +25,7 @@ //! | [`set_harness_name_mangle`](JniGen::set_harness_name_mangle) | the centralized externs object | `"JNINative"` | identity | //! | [`set_fun_name_mangle`](JniGen::set_fun_name_mangle) | top-level package functions | package, camelCased Rust fn name (`put_publisher` → `"putPublisher"`) | identity | //! | [`set_ptr_class_name_mangle`](JniGen::set_ptr_class_name_mangle) | `ptr_class` Kotlin classes | package, Rust type short name (`"KeyExpr"`) | identity | -//! | [`set_data_class_name_mangle`](JniGen::set_data_class_name_mangle) | `data_class` + `value_class` Kotlin classes | package, Rust type short name | identity | +//! | [`set_data_class_name_mangle`](JniGen::set_data_class_name_mangle) | `data_class` Kotlin classes | package, Rust type short name | identity | //! | [`set_enum_name_mangle`](JniGen::set_enum_name_mangle) | `enum_class` Kotlin classes | package, Rust type short name | identity | //! | [`set_method_name_mangle`](JniGen::set_method_name_mangle) | class methods/factories and JNI extern methods | package, final class name, full camelCase Rust fn name | identity | //! @@ -156,7 +156,7 @@ impl JniGen { } /// Set the closure that mangles Kotlin data-class names declared via a - /// `DataClassDecl` (and value classes, which reuse this hook). Receives + /// `DataClassDecl`. Receives /// the target package and Rust short name. Default = identity (see the /// module-level table). pub fn set_data_class_name_mangle(mut self, f: F) -> Self diff --git a/prebindgen/src/api/lang/jnigen/jni/decl.rs b/prebindgen/src/api/lang/jnigen/jni/decl.rs index e9528199..3af915f5 100644 --- a/prebindgen/src/api/lang/jnigen/jni/decl.rs +++ b/prebindgen/src/api/lang/jnigen/jni/decl.rs @@ -112,14 +112,6 @@ macro_rules! data_class { }; } -/// Build a [`ValueClassDecl`] directly from a bare Rust type. See [`ptr_class!`]. -#[macro_export] -macro_rules! value_class { - ($t:ty) => { - $crate::lang::ValueClassDecl::new($crate::__macro_support::parse_type(stringify!($t))) - }; -} - /// Build a [`FunctionDecl`] from a bare function ident or a path: /// /// * `fun!(foo)` — a `#[prebindgen]` fn; its signature is read from the @@ -309,8 +301,7 @@ macro_rules! expand_return { /// in Rust; the object crosses the boundary as that pointer, never copied. /// Use this for types with identity and a lifecycle — sessions, subscribers, /// configs, key expressions — that you pass around and eventually `close()`, -/// as opposed to plain data you copy across ([`data_class!`](crate::data_class)) -/// or small `Copy` values ([`value_class!`](crate::value_class)). +/// as opposed to plain data you copy across ([`data_class!`](crate::data_class)). /// /// A type that never materializes in Kotlin needs **no class declaration at /// all**: give it boundary decls only ([`expand_param!`](crate::expand_param) @@ -896,8 +887,7 @@ impl VariantDecl { /// boundary individually and Kotlin reassembles the object with a generated /// `fromParts(...)` — no Rust-side heap object, no handle to close. Use this /// for plain immutable data you copy across, as opposed to -/// [`ptr_class!`](crate::ptr_class) handles or -/// [`value_class!`](crate::value_class) blobs. +/// [`ptr_class!`](crate::ptr_class) handles. /// /// Members work like every class kind whose instance can re-enter Rust — /// here the receiver re-enters as its **field leaves** (the same call-site @@ -967,58 +957,6 @@ impl From for DataClassDecl { } } -/// Declares a small **`Copy`** Rust type that crosses **by value** — as its -/// raw bytes in a `ByteArray` — rather than as a heap handle. The -/// lightweight peer of [`PtrClassDecl`] for things like ids and timestamps -/// that have no lifecycle to manage. The type must be `Copy` (the generator -/// asserts it at compile time). Readers added with [`method`](Self::method) become -/// instance methods on the Kotlin value class. -pub struct ValueClassDecl { - pub(crate) key: TypeKey, - pub(crate) name_override: Option, - pub(crate) iface: IfaceOpts, - pub(crate) members: Vec<(FunctionDecl, MemberKind)>, -} - -impl ValueClassDecl { - pub fn new(rust_type: syn::Type) -> Self { - Self { - key: TypeKey::from_type(&rust_type), - name_override: None, - iface: IfaceOpts::default(), - members: Vec::new(), - } - } - - /// Override the Kotlin **class name** (relative, no dots). - pub fn name(mut self, name: impl Into) -> Self { - self.name_override = Some(name.into()); - self - } - - class_interface_methods!("value_class"); - - /// Expose a `#[prebindgen]` reader (`f(&Self) -> R`) as an instance - /// method on the Kotlin value class (see [`PtrClassDecl::method`]). - pub fn method(mut self, rust_fun: FunctionDecl) -> Self { - self.members.push((rust_fun, MemberKind::Method)); - self - } - - /// Expose a `#[prebindgen]` factory as a companion-object factory - /// (see [`PtrClassDecl::constructor`]). - pub fn constructor(mut self, rust_fun: FunctionDecl) -> Self { - self.members.push((rust_fun, MemberKind::Constructor)); - self - } -} - -impl From for ValueClassDecl { - fn from(rust_type: syn::Type) -> Self { - Self::new(rust_type) - } -} - /// Unifies the four class-kind decls into one type so [`PackageDecl::class`] /// can expose a single entry point. Deliberately **no** /// `impl From for ClassDecl` — a bare `syn::Type` alone doesn't @@ -1031,7 +969,6 @@ pub enum ClassDecl { Enum(EnumClassDecl), Sealed(SealedClassDecl), Data(DataClassDecl), - Value(ValueClassDecl), } impl From for ClassDecl { @@ -1054,11 +991,6 @@ impl From for ClassDecl { Self::Data(d) } } -impl From for ClassDecl { - fn from(d: ValueClassDecl) -> Self { - Self::Value(d) - } -} // ────────────────────────────────────────────────────────────────────── // Function decl @@ -1500,8 +1432,7 @@ impl PackageDecl { } /// Add a class to this package — any of [`ptr_class!`](crate::ptr_class) / - /// [`enum_class!`](crate::enum_class) / [`data_class!`](crate::data_class) / - /// [`value_class!`](crate::value_class). + /// [`enum_class!`](crate::enum_class) / [`data_class!`](crate::data_class). pub fn class(mut self, decl: impl Into) -> Self { self.classes.push(decl.into()); self diff --git a/prebindgen/src/api/lang/jnigen/jni/emit/callback.rs b/prebindgen/src/api/lang/jnigen/jni/emit/callback.rs index 8e09226c..67bc9f11 100644 --- a/prebindgen/src/api/lang/jnigen/jni/emit/callback.rs +++ b/prebindgen/src/api/lang/jnigen/jni/emit/callback.rs @@ -253,7 +253,7 @@ pub(crate) fn callback_input( } } - // Whole-value arg (scalar / String / data-class / value-blob …): + // Whole-value arg (scalar / String / data-class …): // encode with its output converter. A non-`Option` primitive-wire arg // passes its raw primitive; everything else casts to JObject. Output // converters take the value by move; `cb_arg` is the closure @@ -385,11 +385,8 @@ pub(crate) fn reject_vec_of_handle(inner_projection: &Option, elem: if p.kind == ProjectionKind::Handle { panic!( "JniGen: `Vec<{}>` is unsupported — its elements would be closeable native \ - handles (jlong) the JVM must free individually. If `{}` is `Copy`, declare \ - it as a value class via `.value_class(...)` so the Vec surfaces as \ - `List`; otherwise expose a per-element accessor instead of \ - returning a `Vec` of handles.", - elem.to_token_stream(), + handles (jlong) the JVM must free individually. Expose a per-element \ + accessor instead of returning a `Vec` of handles.", elem.to_token_stream(), ); } diff --git a/prebindgen/src/api/lang/jnigen/jni/emit/convert.rs b/prebindgen/src/api/lang/jnigen/jni/emit/convert.rs index 617bf1c4..f7b48c71 100644 --- a/prebindgen/src/api/lang/jnigen/jni/emit/convert.rs +++ b/prebindgen/src/api/lang/jnigen/jni/emit/convert.rs @@ -365,15 +365,7 @@ pub(crate) fn is_jobject_wire(wire: &syn::Type) -> bool { /// all impl `is_null()` and accept `JObject::null().into()` for /// construction. pub(crate) fn is_jobject_shaped_wire(wire: &syn::Type) -> bool { - if let syn::Type::Path(tp) = wire { - if let Some(last) = tp.path.segments.last() { - return matches!( - last.ident.to_string().as_str(), - "JObject" | "JString" | "JByteArray" | "JClass" - ); - } - } - false + crate::api::lang::jnigen::jni::wire_access::is_jni_reference_wire(wire) } /// Default niche set for a JNI wrapper wire: every `J*` handle has a diff --git a/prebindgen/src/api/lang/jnigen/jni/emit/delivery.rs b/prebindgen/src/api/lang/jnigen/jni/emit/delivery.rs index 9810cf7c..8c56da38 100644 --- a/prebindgen/src/api/lang/jnigen/jni/emit/delivery.rs +++ b/prebindgen/src/api/lang/jnigen/jni/emit/delivery.rs @@ -175,8 +175,8 @@ pub(crate) fn emit_unfold_delivery( }; let elem_wire = out_entry.destination.clone(); // Primitive-wire elements (including an opaque **handle**, whose wire - // is `jlong`) cross as a raw typed jvalue; object wires (String / - // value blob) cross as a `JObject`. Keyed purely on the wire shape — + // is `jlong`) cross as a raw typed jvalue; object wires (String, + // arrays) cross as a `JObject`. Keyed purely on the wire shape — // a handle's `Some(Handle)` projection still rides its `jlong`, and // the folder interface declares the matching `Long` (raw) param. let elem_is_prim = matches!(jni_field_access(&elem_wire), Some((_, _, false))); @@ -500,16 +500,13 @@ pub(crate) fn encode_plan_leaves( // typed class in bytecode — a native `new_object` would cost a // descriptor parse + FindClass + GetMethodID + NewObjectA per // delivery). A nullable handle (an `Option` nesting step on the - // path) boxes to `java.lang.Long` / null. A `value_blob` (`Copy`) - // is delivered by copy via its value-blob converter - // (→ `JByteArray`); the Kotlin adapter wraps it (Rust can't box a - // `@JvmInline value class`). The whole path is `Option`-unwrapped + // path) boxes to `java.lang.Long` / null. The whole path is `Option`-unwrapped // (`unwrap_last`): an optional nesting step makes the leaf null // when the value is absent. let proj = out_entry.metadata.projection.as_ref().unwrap_or_else(|| { panic!( "jnigen unfold: identity leaf `{}` has no projection — \ - `.accessor_record_id()` requires a ptr_class or value_blob type", + `.accessor_record_id()` requires a ptr_class type", TypeKey::from_type(&leaf.out_ty) ) }); @@ -576,42 +573,6 @@ pub(crate) fn encode_plan_leaves( stmts.extend(bind_obj(obj_ident, expr)); } } - ProjectionKind::ValueBlob => { - // The value_blob converter takes the value owned (`Copy`). - // Owned at the root; reached-by-`&` elsewhere ⇒ deref-copy. - let wire = out_entry.destination.clone(); - let enc_ident = format_ident!("__enc{}", idx); - let cast = cast_wire_to_jobject(&enc_ident, &wire, fail); - if leaf.path.is_empty() && !by_ref { - let __encoded = conv(quote!(#value)); - - stmts.extend(bind_obj( - obj_ident, - quote! {{ - let #enc_ident = #__encoded; - #cast - }}, - )); - } else { - let expr = reach_leaf( - &qualify, - &leaf.path, - &returns_option, - value.clone(), - by_ref, - true, - 0, - &|reached| { - let __encoded = conv(quote!(*#reached)); - quote! {{ - let #enc_ident = #__encoded; - #cast - }} - }, - ); - stmts.extend(bind_obj(obj_ident, expr)); - } - } ProjectionKind::Unsigned64 => { let enc_ident = format_ident!("__enc{}", idx); let encode = |reached: TokenStream| { @@ -737,7 +698,7 @@ pub(crate) fn encode_plan_leaves( } /// True when a plan leaf crosses the typed `run` as a **raw primitive** -/// `jvalue`: non-nullable, no projection (not a handle / value-blob), and a +/// `jvalue`: non-nullable, no projection (not a handle), and a /// primitive JNI wire. Must agree with the descriptor chunk /// [`crate::api::lang::jnigen::jni::iface`] derives for the same leaf — a /// nullable primitive boxes (object chunk), object wires pass as objects. diff --git a/prebindgen/src/api/lang/jnigen/jni/emit/flat_input.rs b/prebindgen/src/api/lang/jnigen/jni/emit/flat_input.rs index 807d3ec7..3a79130d 100644 --- a/prebindgen/src/api/lang/jnigen/jni/emit/flat_input.rs +++ b/prebindgen/src/api/lang/jnigen/jni/emit/flat_input.rs @@ -38,11 +38,6 @@ pub(crate) fn struct_input_body( // * Handle: read the JNINativeHandle object from the JVM slot, // `peek()` the raw jlong, then run the per-field input converter // (jlong-keyed; null handle ⇒ jlong 0 ⇒ `None` via the niche path). - // * ValueBlob: the class is JVM-erased to its `bytes: ByteArray`, so - // the slot is the `[B` descriptor; read it as a JObject, coerce to - // the inner wire, and run the per-field converter. (Without this - // branch a value-blob field would be mis-decoded as a handle — - // peeking a non-handle object.) if let Some(proj) = &field_entry.metadata.projection { match proj.kind { ProjectionKind::Handle => { @@ -95,17 +90,6 @@ pub(crate) fn struct_input_body( #decode }); } - ProjectionKind::ValueBlob => { - let descriptor = "[B"; - let tmp_ident = format_ident!("__{}_jobj", fname_ident); - field_preludes.push(quote! { - let #tmp_ident: jni::objects::JObject = env.get_field(v, #camel, #descriptor) - .and_then(|val| val.l()) - .map_err(|e| <__JniErr as ::core::convert::From>::from(format!(#err_prefix, e)))?; - let #raw_ident: #field_wire = #tmp_ident.into(); - let #fname_ident = #field_conv; - }); - } ProjectionKind::Unsigned64 => { if let Some(inner_ty) = option_inner_type(&field.ty) { let niche = matches!( @@ -799,8 +783,16 @@ pub(crate) fn kt_leaf_default(sig: &str, nullable: bool) -> Option { "F" => "0.0f", "D" => "0.0", "Ljava/lang/String;" => "\"\"", - "[B" => "ByteArray(0)", - _ => "null", + other => { + // An inert primitive-array slot is an EMPTY array of its own + // type, never null — the slot is non-nullable in the factory. + if let Some(n) = + crate::api::lang::jnigen::jni::wire_access::kotlin_array_of_descriptor(other) + { + return Some(format!("{n}(0)")); + } + "null" + } } .to_string(), ) @@ -916,7 +908,7 @@ fn build_flat_sum_field( let mut fields = Vec::new(); for (f, item_field) in v.fields.iter().zip(item_variant.fields.iter()) { let entry = registry.input_entry(&item_field.ty)?; - // A projection payload (handle / value blob) carries ownership + // A projection payload (handle) carries ownership // and locking rules the tag-gated group does not model yet. if entry.metadata.projection.is_some() { return None; @@ -1436,34 +1428,6 @@ fn build_flat_struct_node( }); continue; } - ProjectionKind::ValueBlob => { - let is_opt = option_inner_type(&field.ty).is_some(); - let mut access = if is_opt || nullable_context { - format!("{field_ref}?.bytes") - } else { - format!("{field_ref}.bytes") - }; - if nullable_context && !is_opt { - access.push_str(" ?: ByteArray(0)"); - } - let value_index = push_value_leaf( - leaves, - &child_native, - fident.clone(), - fentry, - access, - is_opt, - ); - fields.push(FlatFieldNode::Value { - field: fident, - value_leaf: value_index, - present_leaf: None, - direct_handle: false, - optional_handle: false, - rust_ty: Box::new(field.ty.clone()), - }); - continue; - } ProjectionKind::Unsigned64 => { let is_opt = option_inner_type(&field.ty).is_some(); let access = if is_opt || nullable_context { diff --git a/prebindgen/src/api/lang/jnigen/jni/emit/names.rs b/prebindgen/src/api/lang/jnigen/jni/emit/names.rs index 11ac26b6..911dfeda 100644 --- a/prebindgen/src/api/lang/jnigen/jni/emit/names.rs +++ b/prebindgen/src/api/lang/jnigen/jni/emit/names.rs @@ -45,6 +45,9 @@ pub(crate) struct QualifyEmittedTypes<'a> { /// Bare type name → the module it is reachable under (the item's origin /// crate, or the registry's default module). pub(crate) source_names: &'a std::collections::HashMap, + /// Every indexed source name (consts, structs, enums) → the same. Applied + /// ONLY inside an array length — see [`QualifyLengthPaths`]. + pub(crate) length_names: &'a std::collections::HashMap, } impl syn::visit_mut::VisitMut for QualifyEmittedTypes<'_> { @@ -59,6 +62,129 @@ impl syn::visit_mut::VisitMut for QualifyEmittedTypes<'_> { } syn::visit_mut::visit_type_path_mut(self, tp); } + + /// Qualify a `#[prebindgen]` const used as an ARRAY LENGTH (`[u8; MAX]`). + /// + /// `syn` models the length as an expression, so `visit_type_path_mut` never + /// sees it and the generated file would reference a const that is not in + /// scope. The rewrite is confined to `arr.len` on purpose: a generated + /// converter body is full of expression paths that are LOCALS (`v`, `env`), + /// and a source crate may legally declare `pub const env: usize` — so a + /// whole-item expression pass would rewrite those locals to + /// `mycrate::env` even when restricted to registered const idents. An array + /// length cannot contain a local, which is what makes this scope safe. + fn visit_type_array_mut(&mut self, arr: &mut syn::TypeArray) { + syn::visit_mut::visit_type_mut(self, &mut arr.elem); + reject_unsupported_array_length(arr); + let mut lengths = QualifyLengthPaths { + length_names: self.length_names, + }; + syn::visit_mut::visit_expr_mut(&mut lengths, &mut arr.len); + } +} + +/// Refuse an array length built from anything but a small, closed set of +/// expression forms. +/// +/// [`QualifyLengthPaths`] rewrites a bare path to its origin module, which is +/// sound only while every path in the length names a source ITEM. Anything that +/// can bind a name breaks that premise — a local shadowing a source item gets +/// rewritten into it: +/// +/// ```ignore +/// [u8; const { let array_len = 3; array_len }] // `array_len` is a LOCAL +/// [u8; match 3 { array_len => array_len }] // ...so is this one +/// ``` +/// +/// Qualifying either yields `myflat::array_len`, a function item where a +/// `usize` was meant; a same-typed collision would compile and silently change +/// the length. +/// +/// This is a WHITELIST on purpose. Listing the binding forms instead means +/// every omitted or newly added `syn::Expr` variant silently reopens the hole — +/// which is exactly how `match` and `if let` slipped past the first attempt. +/// Inverting it makes the failure mode "a legitimate length is refused", which +/// is loud and trivially worked around by hoisting the value into a named +/// `const`. +fn reject_unsupported_array_length(arr: &mut syn::TypeArray) { + // Rendered before the mutable walk below borrows the length. + let rendered = quote::ToTokens::to_token_stream(&*arr).to_string(); + struct Check(Option<&'static str>); + // `VisitMut` rather than `Visit`: syn's immutable visitor is behind a + // feature this crate does not enable, and the walk mutates nothing. + impl syn::visit_mut::VisitMut for Check { + fn visit_expr_mut(&mut self, e: &mut syn::Expr) { + let ok = matches!( + e, + // A literal length, `[u8; 4]`. + syn::Expr::Lit(_) + // The names this pass exists to qualify: `MAX`, + // `Holder::N`, and the callee of `array_len()`. + | syn::Expr::Path(_) + // Const arithmetic over those: `A + 1`, `-1`, `(A) * 2`, + // `A as usize`, `array_len()`. + | syn::Expr::Binary(_) + | syn::Expr::Unary(_) + | syn::Expr::Paren(_) + | syn::Expr::Group(_) + | syn::Expr::Cast(_) + | syn::Expr::Call(_) + ); + if !ok && self.0.is_none() { + self.0 = Some("an unsupported expression form"); + } + syn::visit_mut::visit_expr_mut(self, e); + } + } + let mut check = Check(None); + syn::visit_mut::VisitMut::visit_expr_mut(&mut check, &mut arr.len); + if let Some(what) = check.0 { + panic!( + "fixed-size array `{rendered}`: the length uses {what}. Only a literal, a path, a \ + call, and const arithmetic over those are supported — anything that can bind a name \ + (`const {{ … }}`, `match`, `if let`, a closure, a loop) would let a LOCAL be \ + mistaken for a source item, because this generator qualifies the length's paths \ + against their source module. Hoist the value into a named `const` and use that as \ + the length." + ); + } +} + +/// Qualifies the source-crate paths in an array's LENGTH expression, run only +/// by [`QualifyEmittedTypes::visit_type_array_mut`]. Separate from the type +/// visitor so it can never reach a converter body's locals. +/// +/// A length is an ordinary Rust const expression, so it reaches the source +/// crate two ways and both need the origin module prefixed: +/// +/// * a **free const**, `[u8; MAX]` — the whole path is the name; +/// * an **associated const**, `[u8; Holder::N]` — the LEADING segment is the +/// owning type. Only that segment is rewritten; the rest (`::N`, and any +/// further associated item) is relative to it and must be left alone. +/// +/// Both look up the same registry-wide map, so an owner that exists only as a +/// compile-time namespace does not have to be declared to the binding: forcing +/// that would emit a dead Kotlin class purely to make the generated Rust +/// compile. +struct QualifyLengthPaths<'a> { + length_names: &'a std::collections::HashMap, +} + +impl syn::visit_mut::VisitMut for QualifyLengthPaths<'_> { + fn visit_expr_path_mut(&mut self, ep: &mut syn::ExprPath) { + if ep.qself.is_none() && ep.path.leading_colon.is_none() { + // One segment names the const itself; more than one means the + // leading segment is the type that owns it. Either way it is the + // leading segment that carries the origin module. + let ident = ep.path.segments[0].ident.to_string(); + if let Some(module) = self.length_names.get(&ident) { + let mut qualified = module.clone(); + qualified.segments.extend(ep.path.segments.iter().cloned()); + ep.path = qualified; + } + } + syn::visit_mut::visit_expr_path_mut(self, ep); + } } /// If `ty` is a `&T` borrow with no explicit lifetime, splice in `'`. @@ -82,11 +208,8 @@ pub(crate) fn annotate_borrow_with_lifetime(ty: &syn::Type, life: &str) -> syn:: pub(crate) fn annotate_jobject_with_lifetime(ty: &syn::Type, life: &str) -> syn::Type { if let syn::Type::Path(tp) = ty { if let Some(last) = tp.path.segments.last() { - let name = last.ident.to_string(); - if matches!( - name.as_str(), - "JObject" | "JString" | "JByteArray" | "JClass" - ) && matches!(last.arguments, syn::PathArguments::None) + if crate::api::lang::jnigen::jni::wire_access::is_jni_reference_wire(ty) + && matches!(last.arguments, syn::PathArguments::None) { let mut new = tp.clone(); if let Some(last) = new.path.segments.last_mut() { @@ -150,19 +273,6 @@ pub(crate) fn option_inner_ref_mutability(ty: &syn::Type) -> Option { Some(r.mutability.is_some()) } -/// Inline-class field name for a value projection identified by its folded -/// [`Projection::leaf_key`] (e.g. `"ZZenohId"`) rather than by a raw param type. -/// Used for `Option` params where the written type isn't the bare -/// value class but the projection still resolves the leaf — so the wrapper -/// knows which inline field to unwrap (`.bytes`). -pub(crate) fn value_projection_field_for_leaf(ext: &JniGen, leaf_key: &TypeKey) -> Option { - let cfg = ext.types.get(leaf_key)?; - if cfg.is_value_blob() { - return Some("bytes".to_string()); - } - None -} - /// INPUT: wire → rust. Format `_to__` (including /// `impl Fn(...)` lambda converters — the legacy /// `process_kotlin__callback` naming is gone with the fun-interface diff --git a/prebindgen/src/api/lang/jnigen/jni/emit/struct_out.rs b/prebindgen/src/api/lang/jnigen/jni/emit/struct_out.rs index d22776d5..f8b274a7 100644 --- a/prebindgen/src/api/lang/jnigen/jni/emit/struct_out.rs +++ b/prebindgen/src/api/lang/jnigen/jni/emit/struct_out.rs @@ -72,11 +72,11 @@ pub(crate) fn primitive_default_for_descriptor(sig: &str) -> TokenStream { /// /// Returns `None` (⇒ the type keeps the whole-value `fromParts` path) when a /// field needs a transform this fixed builder can't yet forward verbatim — a -/// **projection** (opaque handle / value blob), an **enum**, or a nested +/// **projection** (opaque handle), an **enum**, or a nested /// data-class behind `Option` / `Vec`. (Those are handled by the slower /// [`struct_output_body`] until the synthesizer is widened to wrap them.) /// -/// Classification reads only `ext.types` (`opaque`/`enum_cfg`/`value_blob`) and +/// Classification reads only `ext.types` (`opaque`/`enum_cfg`) and /// `registry.structs` — both populated before `resolve` — never the output /// converter table (not yet built at this stage). pub(crate) fn synth_value_struct_leaves( @@ -107,7 +107,7 @@ pub(crate) fn synth_value_struct_leaves( let mut path = path_prefix.to_vec(); path.push(fname); - // A projection field (opaque handle / `value_blob`) or an enum field + // A projection field (opaque handle) or an enum field // is delivered with a transform the fixed builder can't forward yet. // A nested data-class field (a *declared* plain struct) inlines when // non-optional (recurse); `Option`/`Vec`-wrapped nesting is deferred @@ -122,7 +122,7 @@ pub(crate) fn synth_value_struct_leaves( // leaf whose `out_ty` is the sum and then REQUIRE an output // converter for it, failing the resolve with the sum named rather // than the unsupported position. - TypeKind::Handle | TypeKind::Enum | TypeKind::ValueBlob | TypeKind::Sum => return None, + TypeKind::Handle | TypeKind::Enum | TypeKind::Sum => return None, TypeKind::DataStruct { st, cfg: Some(_) } => Some(st.clone()), _ => None, }; @@ -160,8 +160,8 @@ pub(crate) fn synth_value_struct_leaves( /// `call_static_method`). Nested non-optional data-class fields are inlined; /// nested `Option` fields emit a `present` `jboolean` slot followed /// by the child's leaves (encoded in the `Some` arm, defaulted in the `None` -/// arm). Leaves (primitives, handles→`jlong`, value classes/blobs→`ByteArray`, -/// enums→`jint`, strings, `Vec`) terminate the recursion. +/// arm). Leaves (primitives, handles→`jlong`, enums→`jint`, strings, arrays, +/// `Vec`) terminate the recursion. /// /// The field classification is the shared [`build_struct_plan`] — the same /// plan `flatten_struct_factory` walks for the Kotlin side, so the slot @@ -226,7 +226,7 @@ fn encode_field( // rust-side stages first (`Duration → u64 → jlong`). let conv_value = |conv: &ConvChain| -> TokenStream { conv.call(env_expr, value, base) }; match kind { - // Projection leaf (opaque handle → jlong, value class / blob → ByteArray). + // Projection leaf (opaque handle → jlong, `ULong` → jlong). PlanFieldKind::Projection { conv, proj, .. } => { let value_expr = conv_value(conv); match proj.kind { @@ -240,18 +240,6 @@ fn encode_field( default: quote!(0i64), }); } - ProjectionKind::ValueBlob => { - preludes.extend( - quote! { let #id: jni::objects::JObject = { #value_expr }.into(); }, - ); - slots.push(EncSlot { - ident: id, - wire_ty: quote!(jni::objects::JObject), - descriptor: "[B".to_string(), - is_object: true, - default: quote!(jni::objects::JObject::null()), - }); - } ProjectionKind::Unsigned64 => match proj.strategy { FoldStrategy::Base => { preludes.extend(quote! { let #id: jni::sys::jlong = #value_expr; }); diff --git a/prebindgen/src/api/lang/jnigen/jni/emit/vec_build.rs b/prebindgen/src/api/lang/jnigen/jni/emit/vec_build.rs index ffd1ff04..e2e4a9b9 100644 --- a/prebindgen/src/api/lang/jnigen/jni/emit/vec_build.rs +++ b/prebindgen/src/api/lang/jnigen/jni/emit/vec_build.rs @@ -24,7 +24,7 @@ pub(crate) fn slice_or_vec_elem(arg_ty: &syn::Type) -> Option<(syn::Type, bool)> /// conservative leaf set [`build_flat_input_plan`] accepts, so each element can /// cross as decoupled raw params and be rebuilt on the Rust side with no /// `env.get_field(...)`. `None` for any other shape (opaque handles, enums, -/// value blobs, nested-`Option` structs), which keep the `input_vec` path. +/// nested-`Option` structs), which keep the `input_vec` path. /// /// This is the single detection seam shared by `emit_input_param`, the param /// classifier, `render_extern_decl`, and the synthetic-extern emitter so all diff --git a/prebindgen/src/api/lang/jnigen/jni/emit/wrapper.rs b/prebindgen/src/api/lang/jnigen/jni/emit/wrapper.rs index 5eff8357..0106e385 100644 --- a/prebindgen/src/api/lang/jnigen/jni/emit/wrapper.rs +++ b/prebindgen/src/api/lang/jnigen/jni/emit/wrapper.rs @@ -540,7 +540,6 @@ fn emit_input_param( // ordinary converter chain. InputKind::Callback { .. } | InputKind::Handle { .. } - | InputKind::ValueUnwrap { .. } | InputKind::Unsigned64 { .. } | InputKind::Plain => { let entry = registry.input_entry(arg_ty).unwrap_or_else(|| { diff --git a/prebindgen/src/api/lang/jnigen/jni/equality.rs b/prebindgen/src/api/lang/jnigen/jni/equality.rs new file mode 100644 index 00000000..8ea08a30 --- /dev/null +++ b/prebindgen/src/api/lang/jnigen/jni/equality.rs @@ -0,0 +1,203 @@ +//! Content-based `equals` / `hashCode` / `toString` for generated Kotlin +//! classes with an **array-backed** property. +//! +//! A generated class mirrors a Rust type that derives `PartialEq`/`Eq`, so two +//! values with equal contents must compare equal. Kotlin arrays compare by +//! IDENTITY, which breaks that for every `Vec` field (`ByteArray`) and for +//! the `ByteArray` a value blob carries: +//! +//! ```text +//! Timestamp(1uL, byteArrayOf(1,2,3)) == Timestamp(1uL, byteArrayOf(1,2,3)) // false +//! ``` +//! +//! Kotlin is inconsistent here rather than uniformly identity-based: a `data +//! class`'s generated `hashCode`/`toString` DO special-case arrays +//! (`contentHashCode` / `contentToString`), while its `equals` does not. So a +//! broken value has an equal hash and an unequal `equals` — it lands in the +//! right `HashMap` bucket and is then rejected. Both members are emitted here +//! anyway, so the behavior is stated by the generated source rather than +//! inherited from a compiler special case that only covers two of the three. +//! +//! ## Why value blobs are not `@JvmInline` +//! +//! Kotlin 1.9 (the version this generator targets downstream) rejects +//! `equals`/`hashCode` members on a value class outright — *"Member with the +//! name 'equals' is reserved for future releases"* — and its typed-equals +//! replacement (`operator fun equals(other: T)`) is experimental, needing an +//! opt-in flag every consumer would have to set. A `@JvmInline value class` +//! therefore CANNOT be given value equality at this language level, so +//! [`super::kotlin_emit`] emits value blobs as a plain `data class`. That costs +//! one small allocation per crossing at the wrapper tier (the JNI ABI is +//! unaffected — externs declare `ByteArray` directly and the wrapper passes +//! `.bytes`), which is the price of the type behaving like the value it is. + +use super::*; + +/// Whether a Kotlin type carries an array anywhere inside it, and therefore +/// compares by identity unless the generated operators dig in. +/// +/// Recursive, because a container of arrays is just as identity-compared as a +/// bare one: `List` (from `Vec>`) inherits `ByteArray`'s +/// `equals`, so two lists of equal chunks are unequal and `toString` renders +/// `[[B@3830f1c0]`. A container of *classes* is fine — those already compare +/// by value — so only an array at the bottom makes a property array-bearing. +fn array_bearing(ty: &kt::KtType) -> bool { + match ty { + kt::KtType::Named { fqn, args, .. } => { + is_kotlin_array(fqn.rsplit('.').next().unwrap_or(fqn)) || args.iter().any(array_bearing) + } + kt::KtType::Function { .. } => false, + } +} + +/// Every Kotlin primitive array. All of them compare by identity, so a +/// fixed-size Rust array field needs the content operators whatever its element +/// type is — not only `[u8; N]`. +fn is_kotlin_array(name: &str) -> bool { + crate::api::lang::jnigen::jni::wire_access::kotlin_array_descriptor(name).is_some() +} + +/// The element type of a single-argument container (`List` -> `T`). +fn element_of(ty: &kt::KtType) -> Option<&kt::KtType> { + match ty { + kt::KtType::Named { args, .. } if args.len() == 1 => Some(&args[0]), + _ => None, + } +} + +/// `a == b` for one value of type `ty`, digging through containers. +/// +/// `a`/`b` are Kotlin expressions. Nullability is handled per level: a +/// `ByteArray?` rides `contentEquals`'s nullable-receiver overload, while a +/// nullable container needs an explicit both-null / both-present test. +fn eq_expr(a: &str, b: &str, ty: &kt::KtType) -> String { + if !array_bearing(ty) { + return format!("{a} == {b}"); + } + if element_of(ty).is_none() { + // The array itself. + return format!("{a}.contentEquals({b})"); + } + let elem = element_of(ty).expect("checked above"); + let inner = eq_expr("__x", "__y", elem); + let cmp = format!( + "{a}.size == {b}.size && {a}.indices.all {{ __i -> \ + val __x = {a}[__i]; val __y = {b}[__i]; {inner} }}" + ); + if ty.is_nullable() { + format!("(({a} == null && {b} == null) || ({a} != null && {b} != null && {cmp}))") + } else { + format!("({cmp})") + } +} + +/// `hashCode` for one value of type `ty`, digging through containers. The +/// container fold mirrors `Arrays.hashCode`'s 31-multiplier so a `List` and the +/// array it came from agree. +fn hash_expr(x: &str, ty: &kt::KtType) -> String { + if !array_bearing(ty) { + return if ty.is_nullable() { + format!("({x}?.hashCode() ?: 0)") + } else { + format!("{x}.hashCode()") + }; + } + match element_of(ty) { + None if ty.is_nullable() => format!("({x}?.contentHashCode() ?: 0)"), + None => format!("{x}.contentHashCode()"), + Some(elem) => { + let inner = hash_expr("__e", elem); + let fold = format!("{x}.fold(1) {{ __acc, __e -> 31 * __acc + {inner} }}"); + if ty.is_nullable() { + format!("({x}?.let {{ __l -> {} }} ?: 0)", fold.replace(x, "__l")) + } else { + format!("({fold})") + } + } + } +} + +/// `toString` rendering for one value of type `ty`, digging through containers +/// so a nested array never prints as `[B@1a2b3c`. +fn str_expr(x: &str, ty: &kt::KtType) -> String { + if !array_bearing(ty) { + return format!("${{{x}}}"); + } + match element_of(ty) { + None if ty.is_nullable() => format!("${{{x}?.contentToString()}}"), + None => format!("${{{x}.contentToString()}}"), + Some(elem) => { + let inner = str_expr("__e", elem); + let join = format!("{x}.joinToString(\", \", \"[\", \"]\") {{ __e -> \"{inner}\" }}"); + if ty.is_nullable() { + format!("${{{x}?.let {{ __l -> {} }}}}", join.replace(x, "__l")) + } else { + format!("${{{join}}}") + } + } + } +} + +/// The `equals` / `hashCode` / `toString` trio a class needs when any +/// constructor property is array-backed. +/// +/// `None` when none is — the compiler's own generation is already correct +/// there, and emitting these would be pure churn on every existing class. +/// +/// `props` is the class's constructor properties in declaration order, as +/// `(kotlin_name, kotlin_type)`. +pub(crate) fn content_equality_members( + class_name: &str, + props: &[(String, kt::KtType)], +) -> Option> { + if !props.iter().any(|(_, ty)| array_bearing(ty)) { + return None; + } + + // `equals`: identity short-circuit, type check, then per-property + // comparison that digs through any container down to the arrays. + let comparisons: Vec = props + .iter() + .map(|(name, ty)| eq_expr(name, &format!("other.{name}"), ty)) + .collect(); + let equals_body = kt::Code::new() + .line("if (this === other) return true") + .line(format!("if (other !is {class_name}) return false")) + .line(format!("return {}", comparisons.join(" && "))); + let equals = kt::KtFun::new("equals") + .modifier("override") + .param(kt::KtParam::new("other", kt::KtType::any().nullable())) + .returns(kt::KtType::boolean()) + .body(equals_body); + + // `hashCode`: the standard 31-multiplier fold over the same per-property + // expressions, so equal values always agree. + let first = hash_expr(&props[0].0, &props[0].1); + let hash_body = if props.len() == 1 { + // A single property needs no accumulator — `var result` would draw a + // "never reassigned" warning in the generated source. + kt::Code::new().line(format!("return {first}")) + } else { + let mut b = kt::Code::new().line(format!("var result = {first}")); + for (name, ty) in &props[1..] { + b = b.line(format!("result = 31 * result + {}", hash_expr(name, ty))); + } + b.line("return result") + }; + let hash_code = kt::KtFun::new("hashCode") + .modifier("override") + .returns(kt::KtType::int()) + .body(hash_body); + + // `toString`: an array at any depth would otherwise render as `[B@1a2b3c`. + let rendered: Vec = props + .iter() + .map(|(name, ty)| format!("{name}={}", str_expr(name, ty))) + .collect(); + let to_string = kt::KtFun::new("toString") + .modifier("override") + .returns(kt::KtType::string()) + .expr_body(kt::Code::new().line(format!("\"{class_name}({})\"", rendered.join(", ")))); + + Some(vec![equals, hash_code, to_string]) +} diff --git a/prebindgen/src/api/lang/jnigen/jni/fn_plan.rs b/prebindgen/src/api/lang/jnigen/jni/fn_plan.rs index ddf6e10d..c7eb965b 100644 --- a/prebindgen/src/api/lang/jnigen/jni/fn_plan.rs +++ b/prebindgen/src/api/lang/jnigen/jni/fn_plan.rs @@ -110,9 +110,6 @@ pub(crate) enum InputKind { /// [`KotlinMeta::is_direct_handle`] — `true` only for the bare /// `T`/`&T` shape, the by-value consume fast-path trigger. Handle { direct: bool }, - /// Non-lockable value projection (`value_blob`): the call site passes the - /// unwrapped inline-class `field`; the extern keeps the erased wire. - ValueUnwrap { field: String }, /// Rust `u64`: typed Kotlin `ULong`, raw JNI `Long`. The wrapper passes /// the bit-preserving `toLong()` representation and takes no lock. Unsigned64 { niche: Option }, @@ -197,7 +194,7 @@ pub(crate) enum ReturnSurface { Skip, /// Unit return, including the canonical peel (`ZResult<()>`). Unit, - /// Projection return (opaque handle / value class). `leaf_fqn` is the + /// Projection return (opaque handle / `ULong`). `leaf_fqn` is the /// resolved Kotlin FQN; `None` = unregistered (the adapter panics). Projected { projection: Projection, @@ -479,15 +476,13 @@ impl JniFunctionPlan { InputKind::OptionScalar(plan) => 1 + kotlin_jvm_slots(&plan.value_kt_type), InputKind::Handle { .. } | InputKind::VecBuild { .. } => 2, InputKind::Callback { .. } => 1, - InputKind::ValueUnwrap { .. } | InputKind::Unsigned64 { .. } | InputKind::Plain => { - registry - .input_entry(&leaf.ty) - .and_then(|entry| JniPrim::from_wire(&entry.destination)) - .map_or(1, |prim| match prim { - JniPrim::Long | JniPrim::Double => 2, - _ => 1, - }) - } + InputKind::Unsigned64 { .. } | InputKind::Plain => registry + .input_entry(&leaf.ty) + .and_then(|entry| JniPrim::from_wire(&entry.destination)) + .map_or(1, |prim| match prim { + JniPrim::Long | JniPrim::Double => 2, + _ => 1, + }), }; } slots += match &self.output { @@ -573,23 +568,6 @@ fn classify_leaf( Some(ProjectionKind::Handle) => InputKind::Handle { direct: entry.metadata.is_direct_handle(), }, - Some(ProjectionKind::ValueBlob) => { - let proj = entry.metadata.projection.as_ref().expect("checked above"); - if matches!(proj.strategy, FoldStrategy::Iterable(_)) { - panic!( - "render_wrapper_fn: value-blob `Vec<_>` params aren't \ - supported yet (param `{kt_name}`); add array codegen to lift this guard." - ); - } - let field = - value_projection_field_for_leaf(ext, &proj.leaf_key).unwrap_or_else(|| { - panic!( - "render_wrapper_fn: cannot determine inline-class field for value \ - projection param `{kt_name}`" - ) - }); - InputKind::ValueUnwrap { field } - } Some(ProjectionKind::Unsigned64) => InputKind::Unsigned64 { niche: entry.metadata.projection.as_ref().and_then(|p| { is_option_type(ty) @@ -749,7 +727,7 @@ impl ReturnSurface { if crate::api::lang::jnigen::util::is_unit(&canonical) { return (Self::Unit, canonical); } - // Projection return (opaque handle or value class): read the folded + // Projection return (opaque handle or `ULong`): read the folded // `Projection` the type-unfolding mechanism propagated onto this // return type's converter metadata — one source of truth, no // shape-specific peeling. diff --git a/prebindgen/src/api/lang/jnigen/jni/fold.rs b/prebindgen/src/api/lang/jnigen/jni/fold.rs index 840b380c..8514ed7a 100644 --- a/prebindgen/src/api/lang/jnigen/jni/fold.rs +++ b/prebindgen/src/api/lang/jnigen/jni/fold.rs @@ -52,14 +52,12 @@ pub(crate) fn handle_kt_type(strategy: &FoldStrategy, leaf: &kt::KtType) -> kt:: ) } -/// Typed Kotlin leaf of a projection. Declared handle/value-blob projections +/// Typed Kotlin leaf of a projection. Declared handle projections /// take their configured class FQN; the built-in `u64` projection is Kotlin's /// stable unsigned scalar type. pub(crate) fn projection_leaf_kt(ext: &JniGen, proj: &Projection) -> Option { match proj.kind { - ProjectionKind::Handle | ProjectionKind::ValueBlob => { - ext.kotlin_fqn(&proj.leaf_key).map(kt::KtType::cls) - } + ProjectionKind::Handle => ext.kotlin_fqn(&proj.leaf_key).map(kt::KtType::cls), ProjectionKind::Unsigned64 => Some(kt::KtType::cls("ULong")), } } @@ -67,16 +65,16 @@ pub(crate) fn projection_leaf_kt(ext: &JniGen, proj: &Projection) -> Option String { match kind { - ProjectionKind::Handle | ProjectionKind::ValueBlob => format!("{short}({raw})"), + ProjectionKind::Handle => format!("{short}({raw})"), ProjectionKind::Unsigned64 => format!("{raw}.toULong()"), } } -/// For a projection (handle / value-class / value-blob) **struct field**, +/// For a projection (handle / unsigned) **struct field**, /// compute the `(wire_param_type, wrap_expr)` the data class's `fromParts` /// factory uses: the wire param type matches the leaf wire -/// `struct_output_body` passes (handle → `Long` jlong sentinel, value class / -/// blob → `ByteArray`), and the wrap reconstructs the typed value in JVM +/// `struct_output_body` passes (handle → `Long` jlong sentinel), and the wrap +/// reconstructs the typed value in JVM /// bytecode (`Short(arg)`, with null mapped from the `0L` sentinel for handles /// or the declared invalid `Long` for a bounded unsigned representation; JVM /// null remains the fallback for non-niche value projections). Only the @@ -95,7 +93,6 @@ pub(crate) fn factory_projection_wire_wrap( }; let direct = |kind: &crate::api::lang::jnigen::jni::ProjectionKind| match kind { Handle => (kt::KtType::long(), format!("{short}({name})")), - ValueBlob => (kt::KtType::byte_array(), format!("{short}({name})")), Unsigned64 => (kt::KtType::long(), format!("{name}.toULong()")), }; match &proj.strategy { @@ -113,11 +110,6 @@ pub(crate) fn factory_projection_wire_wrap( kt::KtType::long(), format!("if ({name} == 0L) null else {short}({name})"), ), - // Value-blob null rides JVM-null of the `ByteArray` slot. - ValueBlob => ( - kt::KtType::byte_array().nullable(), - format!("{name}?.let {{ {short}(it) }}"), - ), Unsigned64 => match nullable { // A bounded unsigned representation reserves an invalid // raw value for `None`, so the factory receives primitive @@ -232,7 +224,7 @@ fn factory_field( { let f_kind = kind; match f_kind { - // Projection leaf (handle / value class / blob). + // Projection leaf (handle / `ULong`). PlanFieldKind::Projection { proj, fqn, .. } => { let short = register_fqn(fqn, imports); let (wire_ty, wrap) = factory_projection_wire_wrap(proj, &short, &base); @@ -552,9 +544,9 @@ pub(crate) fn fold_projection_wrap( } /// JNI extern's declared Kotlin wire-return for a projection. The leaf wire -/// is the inner converter's destination Kotlin name: `Long` for handles -/// (boxed jlong), the inner field's converter result for value classes (e.g. -/// `ByteArray` for `ZenohId`/`ZBytes`). The fold honours +/// is the inner converter's destination Kotlin name — `Long` for both +/// projection kinds (a handle's pointer, a `ULong`'s raw bit pattern). The +/// fold honours /// [`NullableKind`] so the declared wire matches the runtime ABI: /// `Niche+primitive` keeps the layer non-nullable on the wire (the sentinel /// represents null); `Niche+object` and `Boxed` add `?`. @@ -564,8 +556,6 @@ pub(crate) fn projection_wire_return( use crate::api::lang::jnigen::jni::{FoldStrategy, NullableKind, ProjectionKind}; let (inner_wire, inner_is_primitive) = match proj.kind { ProjectionKind::Handle => (kt::KtType::long(), true), - // Value-blob's inner wire is always `ByteArray` (object-shaped). - ProjectionKind::ValueBlob => (kt::KtType::byte_array(), false), ProjectionKind::Unsigned64 => (kt::KtType::long(), true), }; fold_shape( @@ -586,8 +576,8 @@ pub(crate) fn projection_wire_return( /// Kotlin null-sentinel literal for the *leaf wire* of a projection. Read /// at the wrapper-body call site and forwarded to [`fold_projection_wrap`]; -/// `None` for object-wired leaves (e.g. value classes over `ByteArray`), -/// where `?.let { }` covers the JVM-null case directly. +/// `None` when the leaf wire has no primitive null sentinel, where +/// `?.let { }` covers the JVM-null case directly. pub(crate) fn projection_leaf_sentinel( proj: &crate::api::lang::jnigen::jni::Projection, ) -> Option { @@ -597,10 +587,6 @@ pub(crate) fn projection_leaf_sentinel( use crate::api::lang::jnigen::jni::ProjectionKind; let leaf_wire: syn::Type = match proj.kind { ProjectionKind::Handle => syn::parse_quote!(jni::sys::jlong), - // Value-blob leaf wire is always `JByteArray` (object-shaped) — no - // primitive sentinel; JVM `null` represents the absent value, so - // `?.let` covers nullability. - ProjectionKind::ValueBlob => syn::parse_quote!(jni::objects::JByteArray), // No niche exists for `u64`; `Option` uses the boxed path, so a // primitive sentinel must never be synthesized. ProjectionKind::Unsigned64 => return None, diff --git a/prebindgen/src/api/lang/jnigen/jni/iface.rs b/prebindgen/src/api/lang/jnigen/jni/iface.rs index 627796d1..accf5e6b 100644 --- a/prebindgen/src/api/lang/jnigen/jni/iface.rs +++ b/prebindgen/src/api/lang/jnigen/jni/iface.rs @@ -10,9 +10,7 @@ //! Every callback position (impl-`Fn` delivery, output-expansion `build`, //! `fold`, `onError`) gets a generated interface whose single method is //! `public fun run(...)` with **JVM-stable parameter types** — typed handle -//! classes, `ByteArray` for `value_blob` (never the `@JvmInline` class — -//! Kotlin would mangle the method name and `GetMethodID` would fail), -//! primitives unboxed, nullable primitives boxed. The native side calls +//! classes, primitives unboxed, nullable primitives boxed. The native side calls //! `run` with raw typed `jvalue`s: no per-leaf boxing upcalls, no erased //! `FunctionN`. //! @@ -48,8 +46,6 @@ pub(crate) enum WrapKind { /// zeroes its `ptr`). Replaces the former Rust-side `new_object` + post-invoke /// `close()` for a plan-less `impl Fn(Handle)` arg (Phase 3). HandleOwned(String), - /// `Copy` value blob: raw `ByteArray` → `@JvmInline` value class (FQN). - Blob(String), /// Rust `u64`: raw `Long` bit pattern → typed Kotlin `ULong`. A bounded /// optional representation carries its `None` niche as a primitive value. Unsigned64 { niche_sentinel: Option }, @@ -60,7 +56,7 @@ impl WrapKind { pub fn class_fqn(&self) -> Option<&str> { match self { WrapKind::None | WrapKind::Unsigned64 { .. } => None, - WrapKind::Handle(f) | WrapKind::HandleOwned(f) | WrapKind::Blob(f) => Some(f), + WrapKind::Handle(f) | WrapKind::HandleOwned(f) => Some(f), } } @@ -103,7 +99,7 @@ impl WrapKind { #[derive(Clone, Debug)] pub(crate) struct IfaceParam { pub name: String, - /// User-facing type (typed handle class, value class, …). + /// User-facing type (typed handle class, …). pub typed: kt::KtType, /// JNI-called raw-twin type (`Long`, `ByteArray`, …) — what the /// descriptor and the native jvalues match. @@ -592,10 +588,13 @@ fn kt_jvm_descriptor(ty: &kt::KtType, type_params: &[String]) -> String { p.descriptor().to_string() }; } + if let Some(d) = crate::api::lang::jnigen::jni::wire_access::kotlin_array_descriptor(simple) + { + return d.to_string(); + } return match simple { "Unit" => "V".to_string(), "String" => "Ljava/lang/String;".to_string(), - "ByteArray" => "[B".to_string(), "List" | "MutableList" => "Ljava/util/List;".to_string(), "Any" => "Ljava/lang/Object;".to_string(), // A dot-free non-builtin: a generated class with no package @@ -728,13 +727,11 @@ fn plan_leaf_param( /// Both interface views of one delivered leaf. /// /// * **typed** (user-facing, Kotlin-called): handles as their typed handle -/// classes, value blobs as their `@JvmInline` value classes — legal here -/// because the JNI border never touches this method. +/// classes — legal here because the JNI border never touches this method. /// * **raw** (JNI-called twin): a PLAN leaf (`raw_handle`) crosses handles /// as the raw `jlong` (`Long`/boxed `Long?` — the proxy constructs the /// class in bytecode; a native `new_object` would cost descriptor parse + -/// FindClass + GetMethodID + NewObjectA per message) and blobs as -/// `ByteArray` (the `@JvmInline` class would mangle `run`). A whole +/// FindClass + GetMethodID + NewObjectA per message). A whole /// (plan-less callback) arg keeps the typed handle class in BOTH views — /// the close-unless-taken contract needs the native side to `close()` the /// wrapped object after the invoke. @@ -773,15 +770,6 @@ fn leaf_iface_param( }; if is_value_projection { match proj?.kind { - ProjectionKind::ValueBlob => { - let fqn = ext.kotlin_fqn(&proj?.leaf_key)?.to_string(); - return Some(IfaceParam { - name, - typed: nullable_kt(kt::KtType::cls(fqn.clone())), - raw: nullable_kt(kt::KtType::byte_array()), - wrap: WrapKind::Blob(fqn), - }); - } ProjectionKind::Unsigned64 => { let mut raw = projection_wire_return(proj?); if nullable && !raw.is_nullable() { diff --git a/prebindgen/src/api/lang/jnigen/jni/iface/tests.rs b/prebindgen/src/api/lang/jnigen/jni/iface/tests.rs index 6078cbdd..33fe301b 100644 --- a/prebindgen/src/api/lang/jnigen/jni/iface/tests.rs +++ b/prebindgen/src/api/lang/jnigen/jni/iface/tests.rs @@ -49,8 +49,8 @@ fn as_raw_adapter_breaks_wide_lambda_params_and_run_args() { IfaceParam { name: "replierZid".to_string(), typed: kt::KtType::cls("io.test.ZenohId").nullable(), - raw: kt::KtType::byte_array().nullable(), - wrap: WrapKind::Blob("io.test.ZenohId".to_string()), + raw: kt::KtType::long().nullable(), + wrap: WrapKind::Handle("io.test.ZenohId".to_string()), }, IfaceParam::same("replierEid".to_string(), kt::KtType::int()), IfaceParam::same("isOk".to_string(), kt::KtType::boolean()), @@ -68,7 +68,7 @@ fn as_raw_adapter_breaks_wide_lambda_params_and_run_args() { }, ], ret: kt::KtType::unit(), - descr: "([BIZLjava/lang/Long;Ljava/lang/Long;)V".to_string(), + descr: "(Ljava/lang/Long;IZLjava/lang/Long;Ljava/lang/Long;)V".to_string(), typed_groups: Vec::new(), kdoc: None, }; diff --git a/prebindgen/src/api/lang/jnigen/jni/kotlin_emit.rs b/prebindgen/src/api/lang/jnigen/jni/kotlin_emit.rs index 38a291bd..af6d80e4 100644 --- a/prebindgen/src/api/lang/jnigen/jni/kotlin_emit.rs +++ b/prebindgen/src/api/lang/jnigen/jni/kotlin_emit.rs @@ -7,7 +7,7 @@ //! * the shared `NativeHandle` base + lock helpers (root package, e.g. //! `io.zenoh.jni`). //! * one typed-handle class per `ptr_class` entry. -//! * one enum / data / `@JvmInline value` class per declaration. +//! * one enum / data class per declaration. //! * one top-level free-function bucket per `package()` context. //! * the centralized `external fun` holder (`JNINative`). (`impl Fn(...)` //! params surface as typed Kotlin lambdas on the wrapper tier and erased @@ -18,7 +18,7 @@ //! at the FLATTENED path `/.kt` (`io.zenoh.jni.config` //! → `io/zenoh/jni/config.kt`) — i.e. the file is named after the package's //! last segment and lives in the directory of its parent package, holding all -//! of that package's classes, enums, value-classes and free functions. +//! of that package's classes, enums and free functions. //! //! Every `#[prebindgen]` function must be assigned a Kotlin home — as a //! class member (`.method`/`.constructor` on a class decl) or a free function @@ -84,7 +84,6 @@ impl JniGen { fragments.extend(self.write_enum_classes(registry)?); fragments.extend(self.write_sealed_classes(registry)?); fragments.extend(self.write_data_classes(registry)); - fragments.extend(self.write_value_blobs(registry)?); // Build the borrowed `TypedHandle<'_>` view from internal config. let owned = self.collect_typed_handles(); @@ -386,122 +385,6 @@ impl JniGen { file } - /// Emit one `@JvmInline value class (val bytes: ByteArray)` per - /// declared `value_blob` type. The class is the typed wrapper level; it is - /// erased to its `ByteArray` field at the JVM/ABI level, so the `JNINative` - /// extern (and the wire) stays `ByteArray` while wrappers speak the typed - /// class. The single field name `bytes` matches `value_projection_field`. - pub(crate) fn write_value_blobs( - &self, - registry: &Registry, - ) -> Result, WriteKotlinError> { - let mut written = Vec::new(); - // Deterministic order by canonical Rust type-key (the `types` map is a - // HashMap, so iterate sorted keys rather than raw map order). - let mut keys: Vec<&TypeKey> = self.types.keys().collect(); - keys.sort_by(|a, b| a.as_str().cmp(b.as_str())); - for key in keys { - let cfg = &self.types[key]; - if !cfg.is_value_blob() { - continue; - } - let fqn = cfg - .name_spec - .as_ref() - .map(|s| self.fqn_of(s)) - .ok_or_else(|| { - WriteKotlinError::Other(format!( - "value_blob `{}` has no Kotlin FQN", - key.as_str() - )) - })?; - let (package, class_name) = match fqn.rsplit_once('.') { - Some((p, c)) => (p.to_string(), c.to_string()), - None => (String::new(), fqn.clone()), - }; - let framework_line = format!( - "Typed by-value wrapper for the native Rust `{}` (a `Copy` blob carried\n\ - as its raw bytes; `@JvmInline`-erased to `ByteArray` at the JNI boundary).", - key.as_str() - ); - let class_kdoc = crate::api::lang::jnigen::jni::source_item_doc(registry, key) - .map(|d| format!("{d}\n\n{framework_line}")) - .unwrap_or(framework_line); - let mut class = KtClass::new(ClassKind::ValueInline, &class_name) - .vis(Vis::Public) - .kdoc(class_kdoc) - .ctor_param( - KtCtorParam::new("bytes", KtType::byte_array()) - .val() - .vis(Vis::Public), - ); - let mut imports: BTreeSet = BTreeSet::new(); - let members = self - .class_members - .get(key) - .map(Vec::as_slice) - .unwrap_or(&[]); - if !members.is_empty() && !self.package.is_empty() { - imports.insert(format!("{}.{}", self.package, self.jni_native_class_name())); - } - // Promoted instance methods (`.method`): receiver bound to `this`, - // passing `this.bytes` to the extern. - for m in members.iter().filter(|m| m.kind == MemberKind::Method) { - if let Some((item_fn, _)) = registry.functions.get(&m.rust_ident) { - if let Some(f) = crate::api::lang::jnigen::jni::render_wrapper_fn( - self, - item_fn, - registry, - Some(self.effective_method_name(key, m).as_str()), - Some(key), - ) { - for ov in crate::api::lang::jnigen::jni::render_param_overloads( - self, item_fn, registry, &f, - ) { - class = class.member(ov); - } - class = class.member(f); - } - } - } - // Companion-object factory members (`.constructor`). - let ctors: Vec<_> = members - .iter() - .filter(|m| m.kind == MemberKind::Constructor) - .collect(); - if !ctors.is_empty() { - let mut companion = KtClass::companion_object().vis(Vis::Public); - for m in ctors { - if let Some((item_fn, _)) = registry.functions.get(&m.rust_ident) { - if let Some(f) = crate::api::lang::jnigen::jni::render_wrapper_fn( - self, - item_fn, - registry, - Some(self.effective_method_name(key, m).as_str()), - None, - ) { - for ov in crate::api::lang::jnigen::jni::render_param_overloads( - self, item_fn, registry, &f, - ) { - companion = companion.member(ov); - } - companion = companion.member(f); - } - } - } - class = class.companion(companion); - } - let mut file = kt::KtFile::new(package); - if let Some(iface) = - self.apply_class_interface(key, &mut class, &class_name, &[], Vec::new(), true) - { - file = file.decl(iface); - } - written.push(file.decl(class).imports(imports)); - } - Ok(written) - } - /// Build the `TypedHandle` slice from internal `types` config. /// Iterates entries where `opaque.is_some()` and emits one /// `TypedHandle` per opaque-handle registration. Stable order by @@ -744,6 +627,7 @@ impl JniGen { if let Some(doc) = crate::api::lang::jnigen::util::doc_string(&item_variant.attrs) { vclass = vclass.kdoc(doc); } + let mut vprops: Vec<(String, KtType)> = Vec::new(); for (field, item_field) in variant.fields.iter().zip(item_variant.fields.iter()) { let prop = sum_field_property_name(field); let ty = self.sum_payload_kt_type( @@ -753,8 +637,18 @@ impl JniGen { &prop, item_field, ); + vprops.push((prop.clone(), ty.clone())); vclass = vclass.ctor_param(KtCtorParam::new(&prop, ty).val().vis(Vis::Public)); } + // An array-backed payload (a `Vec` variant field) compares by + // identity otherwise — same rule as a data-class property. + for m in + crate::api::lang::jnigen::jni::equality::content_equality_members(&vname, &vprops) + .into_iter() + .flatten() + { + vclass = vclass.member(m); + } class = class.member(vclass); } @@ -906,7 +800,7 @@ impl JniGen { if let Some(h) = out.metadata.projection.clone() { let leaf = projection_leaf_kt(self, &h).unwrap_or_else(|| { panic!( - "{}: leaf `{}` has no Kotlin FQN registered (ptr_class / value_class)", + "{}: leaf `{}` has no Kotlin FQN registered (ptr_class)", where_(), h.leaf_key ) @@ -982,9 +876,8 @@ impl JniGen { for key in keys { let cfg = &self.types[key]; - // Opaque handles, enums and `value_blob` (`@JvmInline value`) - // types each have their own emitter; only plain structs become - // data classes here. + // Opaque handles, enums and sealed classes each have their own + // emitter; only plain structs become data classes here. if cfg.special_decl() { continue; } @@ -1016,7 +909,7 @@ impl JniGen { // factory-body imports ride the AST/`Code`); this file-level set // is only for the `JNINative` harness the promoted members call. let mut imports: BTreeSet = BTreeSet::new(); - // Members: same shape as the value-blob path — the instance + // Members: the instance // method's receiver re-enters Rust as `this`'s field leaves // (the data-class param destructuring, rebased to `this`). let members = self @@ -1630,13 +1523,13 @@ impl JniGen { } /// The hoisted **folder-appender** singleton for a **whole single-leaf - /// element** fold (`Vec` / `Vec` return, or the matching + /// element** fold (`Vec` / `Vec` return, or the matching /// slice callback): an instance of the folder's raw twin (`__FolderRaw`) /// that, per element, wraps the raw leaf into its typed Kotlin value and /// appends it to the accumulator `ArrayList`, returning the same list. The /// single-leaf analog of [`Self::value_struct_folder_singleton`] — there is no /// `fromParts`; reassembly is just `acc.add((element))`, where `` - /// is the value-class ctor for a value blob, the handle ctor for a handle, or + /// is the handle ctor for a handle, `toULong()` for a `u64`, or /// identity for a String. So the list is composed on the Kotlin side and no /// Java object is built on the Rust side. The folder's `run` params are /// `[acc, element]`. diff --git a/prebindgen/src/api/lang/jnigen/jni/metadata.rs b/prebindgen/src/api/lang/jnigen/jni/metadata.rs index 72321296..5f249079 100644 --- a/prebindgen/src/api/lang/jnigen/jni/metadata.rs +++ b/prebindgen/src/api/lang/jnigen/jni/metadata.rs @@ -28,8 +28,8 @@ pub enum NullableKind { Boxed, } -/// The JNI adapter's nullability / collection layer stack over a handle or -/// value-class leaf, on the unified [`Shape`](crate::api::core::shape::Shape) +/// The JNI adapter's nullability / collection layer stack over a projection +/// leaf, on the unified [`Shape`](crate::api::core::shape::Shape) /// with [`NullableKind`] as the per-`Optional`-layer payload: /// * `Base` — the receiver *is* the handle; /// * `Optional(kind, inner)` — `T?`; `kind` records how null is represented @@ -48,19 +48,14 @@ pub enum ProjectionKind { /// Opaque native handle (`ptr_class`). Wire is `jlong`; a struct field /// stores the **boxed** handle object (`L;`); closeable when owned. Handle, - /// Kotlin `@JvmInline value class` wrapping a **`Copy` value-blob** - /// (`value_blob`). Its inner is always a raw `ByteArray` (`[B`) — there is - /// no Rust struct field to resolve. The typed class has a single - /// `bytes: ByteArray` field; the wire is `JByteArray`. Never closeable. - ValueBlob, /// Rust `u64`: raw JNI `jlong` bit pattern with a typed Kotlin `ULong` /// surface. It owns no resource; wrapping/unwrapping is /// `Long.toULong()` / `ULong.toLong()`. Unsigned64, } -/// Folded description of a Kotlin newtype projection (opaque handle or value -/// class) reached through zero or more wrapper layers. Set at the leaf, +/// Folded description of a Kotlin newtype projection (opaque handle or +/// `ULong`) reached through zero or more wrapper layers. Set at the leaf, /// transformed by each wrapper as the type folds (see [`FoldStrategy`]), and /// read by every typed-surface emitter (data-class fields, struct /// encode/decode, `classify_return`, param classification) so "what Kotlin @@ -74,11 +69,11 @@ pub struct Projection { pub leaf_key: crate::api::core::registry::TypeKey, /// `false` for `&T` borrows of a handle — still a projection (param /// classification needs this), but not the holder's to close, so - /// `close()` emission skips it. Always `false` for [`ProjectionKind::ValueBlob`]. + /// `close()` emission skips it. pub owned: bool, /// Nullability / collection layers. pub strategy: FoldStrategy, - /// Handle vs value class — see [`ProjectionKind`]. + /// Handle vs `ULong` — see [`ProjectionKind`]. pub kind: ProjectionKind, /// Kotlin literals for representation-domain niches, in carve order. /// Empty for ordinary projections; bounded u64 conversions populate it. diff --git a/prebindgen/src/api/lang/jnigen/jni/mod.rs b/prebindgen/src/api/lang/jnigen/jni/mod.rs index 3c9b2f2e..7d927f87 100644 --- a/prebindgen/src/api/lang/jnigen/jni/mod.rs +++ b/prebindgen/src/api/lang/jnigen/jni/mod.rs @@ -171,11 +171,6 @@ pub(crate) enum DeclaredKind { /// class`, flattened field-by-field at the boundary. The kind with no /// options of its own. Data, - /// `value_class!` — a `Copy` Rust type passed **by value as its raw - /// memory blob** in a `JByteArray` (wire), the value-level peer of an - /// opaque handle's `jlong`. Surfaces as a `@JvmInline value class` - /// erased to `ByteArray`. - Value, } /// All configuration the structured builder accumulates for one @@ -258,11 +253,6 @@ impl TypeConfig { _ => None, } } - - /// `true` if this type is a `value_class`-declared `Copy` value blob. - pub(crate) fn is_value_blob(&self) -> bool { - matches!(self.kind, DeclaredKind::Value) - } } /// Free-standing functions emitted into a synthetic package-level wrapper @@ -309,7 +299,7 @@ pub(crate) enum MemberKind { } /// One `#[prebindgen]` function attached to a declared class (`ptr_class` / -/// `value_class` / `data_class`) via a declaration's `.method(...)` / +/// `data_class`) via a declaration's `.method(...)` / /// `.constructor(...)`. Methods become **instance methods** (receiver /// dropped→`this`); constructors become **companion factory** methods. Each /// is also a real `#[prebindgen]` wrapper (Rust extern + `JNINative` extern + @@ -582,8 +572,10 @@ mod classify; mod config; mod decl; mod emit; +mod equality; mod iface; mod prim; +mod prim_array; mod selector; #[cfg(test)] mod tests; diff --git a/prebindgen/src/api/lang/jnigen/jni/overloads.rs b/prebindgen/src/api/lang/jnigen/jni/overloads.rs index db55559d..cddb36a1 100644 --- a/prebindgen/src/api/lang/jnigen/jni/overloads.rs +++ b/prebindgen/src/api/lang/jnigen/jni/overloads.rs @@ -33,21 +33,6 @@ use super::*; use crate::api::core::expand::{FoldArg, FoldPlan}; impl JniGen { - /// `true` if `simple` is the Kotlin simple name of a `value_blob` - /// (`@JvmInline value class`) type — which erases to `ByteArray` on the - /// JVM, so two distinct such classes share one method descriptor. - pub(crate) fn is_value_blob_kotlin(&self, simple: &str) -> bool { - self.types.values().any(|c| { - c.is_value_blob() - && c.name_spec - .as_ref() - .map(|s| self.fqn_of(s)) - .and_then(|fqn| fqn.rsplit('.').next().map(str::to_string)) - .as_deref() - == Some(simple) - }) - } - /// Proactively verify every multi-variant `expand_param!` declaration is /// splittable (its arms have pairwise-distinct JVM-erased signatures), so /// [`FunctionDecl::split_on_param`](crate::fun) can emit unambiguous @@ -144,8 +129,8 @@ fn arm_erased_sig( /// The [`ErasedJvmType`] a Rust arm type surfaces as: map it to its Kotlin /// surface type (a declared class's FQN, else the resolved converter's Kotlin -/// name) and run the shared [`erase_kt_type`]; a value class folds to `byte[]` -/// and a plain class to its FQN there. Falls back to the token string for a +/// name) and run the shared [`erase_kt_type`]; a plain class folds to its FQN +/// there. Falls back to the token string for a /// type with no resolved surface. References are peeled first (`&T` erases /// like `T`). fn rust_type_erased( @@ -160,14 +145,14 @@ fn rust_type_erased( let key = TypeKey::from_type(peeled); if ext.types.get(&key).is_some_and(|c| c.name_spec.is_some()) { if let Some(fqn) = ext.kotlin_fqn(&key) { - return erase_kt_type(ext, &[], &kt::KtType::cls(fqn)); + return erase_kt_type(&[], &kt::KtType::cls(fqn)); } } if let Some(kt) = registry .input_entry(peeled) .and_then(|e| e.metadata.kotlin_name.clone()) { - return erase_kt_type(ext, &[], &kt); + return erase_kt_type(&[], &kt); } ErasedJvmType::raw(peeled.to_token_stream().to_string()) } diff --git a/prebindgen/src/api/lang/jnigen/jni/prim_array.rs b/prebindgen/src/api/lang/jnigen/jni/prim_array.rs new file mode 100644 index 00000000..b1c7ebf1 --- /dev/null +++ b/prebindgen/src/api/lang/jnigen/jni/prim_array.rs @@ -0,0 +1,181 @@ +//! Fixed-size arrays (`[T; N]`) of JNI-primitive elements, crossing as the +//! matching Kotlin primitive array. +//! +//! `[u8; 16]` ⇄ `ByteArray`, `[i64; 4]` ⇄ `LongArray`, and so on for every +//! [`JniPrim`](super::prim::JniPrim) scalar. Primitive arrays bulk-copy through +//! `set_*_array_region` / `get_*_array_region` and box nothing, which is why a +//! fixed-size array does NOT go through the `Vec` → `List` path. +//! +//! Wider unsigned elements (`[u16; N]`, `[u32; N]`, `[u64; N]`) carry the **raw +//! bit pattern** in the signed array. That matches the existing scalar rule — a +//! `u64` already crosses as a raw `jlong` — and `Vec` → `ByteArray`, where +//! Kotlin's `Byte` is signed. Kotlin's own `UByteArray`/`ULongArray` are +//! `@ExperimentalUnsignedTypes`, so using them would push an opt-in onto every +//! consumer of a shared binding tier. +//! +//! **`N` is never needed at generation time.** It is often a const *path* rather +//! than a literal (`ZenohId` is `[u8; ZENOH_ID_MAX_SIZE]`), so the decode leans +//! on `TryFrom<&[T]> for [T; N]` and lets `rustc` infer the length; a JVM array +//! of the wrong length becomes a binding error rather than a panic. +//! +//! Element conversion is by `as`-cast (or `!= 0` for `bool`), never a transmute: +//! `jboolean` is a `u8`, and reinterpreting a byte of `2` as a Rust `bool` would +//! be UB — the very hazard that retired the raw-memory value blob this module +//! replaces. + +use super::*; + +/// The JNI/Kotlin array pair for one primitive element type. +pub(crate) struct PrimArray { + /// Wire type: `jni::objects::JLongArray`. + pub wire: syn::Type, + /// JNI element type: `jni::sys::jlong`. + pub elem_wire: syn::Type, + /// Kotlin surface: `LongArray`. + pub kotlin: kt::KtType, + /// `JNIEnv::new_long_array`. + pub new_fn: syn::Ident, + /// `JNIEnv::set_long_array_region`. + pub set_region: syn::Ident, + /// `JNIEnv::get_long_array_region`. + pub get_region: syn::Ident, + /// True for `[bool; N]` — its element needs normalizing rather than casting. + pub is_bool: bool, + /// True for `[u8; N]` — the one case with a dedicated bulk helper. + pub is_u8: bool, +} + +/// Classify `ty` as a fixed-size array of JNI-primitive elements. +/// +/// `None` for everything else, including `[T; N]` of a declared class or enum — +/// those keep resolving as unsupported, so an unhandled shape is a clear +/// resolve error rather than silently wrong code. +pub(crate) fn prim_array_of(ty: &syn::Type) -> Option { + let syn::Type::Array(arr) = ty else { + return None; + }; + let syn::Type::Path(tp) = &*arr.elem else { + return None; + }; + let elem = tp.path.segments.last()?.ident.to_string(); + // `usize`/`isize` are deliberately absent: their width is platform + // dependent, so there is no stable JNI element type to pick. + let (letter, jni_elem) = match elem.as_str() { + "u8" | "i8" => ("byte", "jbyte"), + "u16" | "i16" => ("short", "jshort"), + "u32" | "i32" => ("int", "jint"), + "u64" | "i64" => ("long", "jlong"), + "f32" => ("float", "jfloat"), + "f64" => ("double", "jdouble"), + "bool" => ("boolean", "jboolean"), + _ => return None, + }; + let cap = format!("{}{}", letter[..1].to_uppercase(), &letter[1..]); + let wire_ident = format_ident!("J{}Array", cap); + let elem_wire_ident = format_ident!("{}", jni_elem); + Some(PrimArray { + wire: syn::parse_quote!(jni::objects::#wire_ident), + elem_wire: syn::parse_quote!(jni::sys::#elem_wire_ident), + kotlin: kt::KtType::cls(format!("{cap}Array")), + new_fn: format_ident!("new_{}_array", letter), + set_region: format_ident!("set_{}_array_region", letter), + get_region: format_ident!("get_{}_array_region", letter), + is_bool: elem == "bool", + is_u8: elem == "u8", + }) +} + +/// `[T; N]` → the Kotlin primitive array (Rust → wire). +pub(crate) fn output_body(spec: &PrimArray) -> syn::Expr { + if spec.is_u8 { + // `&[u8; N]` derefs to `&[u8]`, so the dedicated helper applies with no + // intermediate buffer — the common case (`ZenohId`). + return syn::parse_quote!({ + env.byte_array_from_slice(&v).map_err(|e| { + <__JniErr as ::core::convert::From>::from(format!( + "fixed-size array encode: {}", + e + )) + })? + }); + } + let elem_wire = &spec.elem_wire; + let new_fn = &spec.new_fn; + let set_region = &spec.set_region; + // One form for every element: `bool as u8` yields 0/1, and the rest are + // same-width numeric casts. Only the DECODE needs a special case, because + // `u8 as bool` is not a cast at all. + let to_wire: syn::Expr = syn::parse_quote!(*__x as #elem_wire); + syn::parse_quote!({ + let __buf: ::std::vec::Vec<#elem_wire> = v.iter().map(|__x| #to_wire).collect(); + let __arr = env.#new_fn(__buf.len() as jni::sys::jsize).map_err(|e| { + <__JniErr as ::core::convert::From>::from(format!( + "fixed-size array encode: {}", + e + )) + })?; + env.#set_region(&__arr, 0, &__buf).map_err(|e| { + <__JniErr as ::core::convert::From>::from(format!( + "fixed-size array encode: {}", + e + )) + })?; + __arr + }) +} + +/// The Kotlin primitive array → `[T; N]` (wire → Rust). +/// +/// The length check is the `try_into`: a JVM array of the wrong size becomes a +/// binding error naming the type, never a panic or a partially-filled array. +pub(crate) fn input_body(ty: &syn::Type, spec: &PrimArray) -> syn::Expr { + let key = TypeKey::from_type(ty); + let len_err = format!("fixed-size array decode: `{key}` expects a different length"); + if spec.is_u8 { + return syn::parse_quote!({ + let __buf = env.convert_byte_array(v).map_err(|e| { + <__JniErr as ::core::convert::From>::from(format!( + "fixed-size array decode: {}", + e + )) + })?; + let __arr: #ty = __buf.as_slice().try_into().map_err(|_| { + <__JniErr as ::core::convert::From>::from(#len_err.to_string()) + })?; + __arr + }); + } + let elem_wire = &spec.elem_wire; + let get_region = &spec.get_region; + let elem_ty = match ty { + syn::Type::Array(a) => (*a.elem).clone(), + _ => unreachable!("prim_array_of matched a non-array"), + }; + // A `jboolean` is a `u8`: normalize it, never reinterpret it — an out-of- + // range byte read back as a Rust `bool` would be undefined behavior. + let from_wire: syn::Expr = if spec.is_bool { + syn::parse_quote!(*__x != 0) + } else { + syn::parse_quote!(*__x as #elem_ty) + }; + syn::parse_quote!({ + let __len = env.get_array_length(v).map_err(|e| { + <__JniErr as ::core::convert::From>::from(format!( + "fixed-size array decode: {}", + e + )) + })? as usize; + let mut __buf: ::std::vec::Vec<#elem_wire> = ::std::vec![0 as #elem_wire; __len]; + env.#get_region(v, 0, &mut __buf).map_err(|e| { + <__JniErr as ::core::convert::From>::from(format!( + "fixed-size array decode: {}", + e + )) + })?; + let __vals: ::std::vec::Vec<#elem_ty> = __buf.iter().map(|__x| #from_wire).collect(); + let __arr: #ty = __vals.as_slice().try_into().map_err(|_| { + <__JniErr as ::core::convert::From>::from(#len_err.to_string()) + })?; + __arr + }) +} diff --git a/prebindgen/src/api/lang/jnigen/jni/render.rs b/prebindgen/src/api/lang/jnigen/jni/render.rs index c79d1fbd..2514400b 100644 --- a/prebindgen/src/api/lang/jnigen/jni/render.rs +++ b/prebindgen/src/api/lang/jnigen/jni/render.rs @@ -94,6 +94,9 @@ pub(crate) fn build_data_class( }); let mut ctor_params: Vec = Vec::new(); + // Property (name, type) pairs, for the content-equality members an + // array-backed property needs — see [`equality::content_equality_members`]. + let mut equality_props: Vec<(String, kt::KtType)> = Vec::new(); // Track per-field destructible (name, folded close strategy) so the // bottom emitter can produce a matching `close()` body for each. let mut destructible_fields: Vec<(String, crate::api::lang::jnigen::jni::FoldStrategy)> = @@ -137,8 +140,9 @@ pub(crate) fn build_data_class( } } - ctor_params - .push(kt::KtCtorParam::new(&kotlin_field_name, pf.kind.property_type(&owner)).val()); + let property_type = pf.kind.property_type(&owner); + equality_props.push((kotlin_field_name.clone(), property_type.clone())); + ctor_params.push(kt::KtCtorParam::new(&kotlin_field_name, property_type).val()); if let Some(strategy) = pf.kind.destructible() { destructible_fields.push((kotlin_field_name, strategy)); } @@ -171,6 +175,17 @@ pub(crate) fn build_data_class( for p in ctor_params { class = class.ctor_param(p); } + // Array-backed properties compare by identity in Kotlin, so a class with + // one gets explicit content-based operators (the Rust type derives `Eq`). + for m in crate::api::lang::jnigen::jni::equality::content_equality_members( + class_name, + &equality_props, + ) + .into_iter() + .flatten() + { + class = class.member(m); + } // Supertype clause: a data class with a destructible native-handle field // implements `AutoCloseable`; otherwise no supertype. if !destructible_fields.is_empty() { @@ -481,15 +496,11 @@ pub(crate) fn render_extern_decl( // An opaque-**handle** projection (direct `&T`/`T`, `Option<&T>`, // or by-value `Option`) crosses the JNI wire as a primitive // `jlong` with `0` encoding `None` — a non-null `Long`; the `?` - // lives only on the typed-wrapper surface. (`value_blob` - // projections are NOT handles; they keep their erased wire.) + // lives only on the typed-wrapper surface. InputKind::Handle { .. } => { params.push(kt::KtParam::new(name, kt::KtType::long())); } - InputKind::Callback { .. } - | InputKind::ValueUnwrap { .. } - | InputKind::Unsigned64 { .. } - | InputKind::Plain => { + InputKind::Callback { .. } | InputKind::Unsigned64 { .. } | InputKind::Plain => { let ty = if leaf.as_enum_value { // Enum (incl. `Option`) crosses as jint → Kotlin // `Int`; the wrapper passes `.value` / `?.value`. The Rust @@ -538,9 +549,8 @@ pub(crate) fn render_extern_decl( // The plan classified the declared surface once — `convert_out_ty` // for a `convert_output` (Return), else the function's own return. let (kt_return, projection) = render_return_surface(&v.surface)?; - // JNI extern's wire return: handle projections wire as `Long`; - // value-class projections wire as their inner converter's type - // folded through the projection strategy; enums wire as `Int` + // JNI extern's wire return: projections wire as `Long` folded + // through the projection strategy; enums wire as `Int` // (`Int?` under `Option`); everything else is the declared return. match &projection { Some(p) => Some(projection_wire_return(p)), @@ -587,14 +597,6 @@ enum ParamMode { /// present. The Rust converter consumes the `Box` to `Option`. ConsumeNullable, PassThrough, - /// Value-projection param (`value_blob`): a Kotlin `@JvmInline value class` - /// that is **not** a lockable handle. The Kotlin param type is the - /// value-class FQN; the call site passes the unwrapped inline-class field - /// (`.`) so the `JNINative` extern receives the erased inner - /// wire (e.g. `ByteArray`). No lock. - ValueUnwrap { - field: String, - }, /// Kotlin `ULong` projected to its raw JNI `Long` bit pattern. Unsigned64 { niche: Option, @@ -626,8 +628,7 @@ enum ParamMode { /// `impl Fn(args)` callback param: typed Kotlin lambda over the flattened /// leaves of each arg's callback plan (whole arg when plan-less), erased to /// `Any` at the extern tier — the same shape as the unfold `build`/`onError` - /// lambdas. `call_arg` is the call-site expression: the param itself, or a - /// value-blob rebuilding adapter. + /// lambdas. `call_arg` is the call-site expression. Callback { call_arg: String, }, @@ -703,8 +704,8 @@ pub(crate) fn peel_receiver_key(ty: &syn::Type) -> TypeKey { /// When `receiver_key` is `Some(class_key)` the function is emitted as an /// **instance method** of that class: the first parameter whose (peeled) Rust /// type equals `class_key` is dropped from the signature and bound to `this` -/// (the inherited `NativeHandle` scope for a `ptr_class` — `this.ptr` + lock — -/// or `this.bytes` for a `value_class` blob). The JNINative extern/call is +/// (the inherited `NativeHandle` scope for a `ptr_class` — `this.ptr` + lock). +/// The JNINative extern/call is /// unchanged (keyed on the Rust ident), so only the Kotlin wrapper relocates. /// The Kotlin surface of a wrapper: the assembled `KtFun` with every /// parameter/return type in place but **no body**, plus the emission @@ -1001,7 +1002,7 @@ fn render_val_over_helper( /// agree on. struct OutputPlan { kt_return: Option, - /// Kotlin-newtype return (opaque handle / value class) — the wrap the + /// Kotlin-newtype return (opaque handle / `ULong`) — the wrap the /// call expression folds around the extern result. projection: Option, /// Trailing **lambda** param (`build` / `fold`) of an output expansion. @@ -1089,8 +1090,7 @@ fn classify_params( // (`Callback`) whose `run` parameters are the flattened // leaves of each arg's callback plan (the arg whole when plan-less). // The extern receives it erased (`Any`) and the native trampoline - // calls the typed `run` — value-blob leaves surface as their raw - // `ByteArray` wire (the SDK wraps), so no call-site adapter exists. + // calls the typed `run`, so no call-site adapter exists. // Lambda-literal call sites SAM-convert unchanged. if let InputKind::Callback { iface, .. } = &leaf.kind { let spec = iface.as_deref()?; @@ -1210,12 +1210,6 @@ fn classify_params( ParamMode::Consume } } - // `@JvmInline value class` (value_blob) param: pass the erased - // inner field (`.bytes`, or `?.bytes` when Option) to - // the extern, no lock. - InputKind::ValueUnwrap { field } => ParamMode::ValueUnwrap { - field: field.clone(), - }, InputKind::Unsigned64 { niche } => ParamMode::Unsigned64 { niche: niche.clone(), }, @@ -1251,11 +1245,11 @@ fn classify_params( /// * `Iterable` (M4 whole / M5 decomposed): per element, fold /// `(acc, leaves…) -> acc`; ``, returns `A`, threads the accumulator. /// -/// Each leaf is delivered with its final Kotlin type; a **value_blob** leaf -/// (`@JvmInline value class`) can't be constructed Rust-side, so the wrapper -/// installs an **adapter** that applies the Kotlin-side projection wrap -/// (`ZZenohId(raw)`) before the user callback. Leaves with no value_blob ⇒ -/// the callback is passed directly (M1–M4 unchanged). +/// Each leaf is delivered with its final Kotlin type; a leaf whose typed form +/// can't be constructed Rust-side makes the wrapper install an **adapter** that +/// applies the Kotlin-side projection wrap before the user callback. Leaves +/// with no such projection ⇒ the callback is passed directly (M1–M4 +/// unchanged). fn classify_output( ext: &JniGen, f: &syn::ItemFn, @@ -1282,7 +1276,7 @@ fn classify_output( // (`convert_out_ty` for a convert, the signature's own output // otherwise). No callback param, no generic, no extra call args; the // extern returns the real wire and `build_call` applies the - // projection wrap (value_blob/handle) below. + // projection wrap (handle) below. render_return_surface(&v.surface)? } else if let ( FnOutputPlan::Unfold( @@ -1303,8 +1297,8 @@ fn classify_output( // `Vec` fold, a `List` composed on the Kotlin side. // The concrete element/return Kotlin type. For a decomposed `data_class` // builder/fold it is the data class (`plan.source`'s registered FQN); for - // a **whole-element leaf** fold (`plan.element` set — String / value blob - // / handle) `plan.source` (e.g. `String`) has no class FQN, so take the + // a **whole-element leaf** fold (`plan.element` set — String / handle) + // `plan.source` (e.g. `String`) has no class FQN, so take the // element's typed view from the folder interface's element param instead. // Full-FQN class type: the render-time `ImportSet` shortens it (and // handles simple-name collisions) when it renders the return type. The @@ -1357,9 +1351,8 @@ fn classify_output( } else if let (FnOutputPlan::Unfold(u), Some(_)) = (&fplan.output, unfold) { // The builder / fold params are generated typed `fun interface`s // (`Builder` / `Folder`); the native side - // calls their typed `run` with raw jvalues (value-blob leaves surface - // as `ByteArray` — no call-site adapter). Lambda-literal call sites - // SAM-convert unchanged. + // calls their typed `run` with raw jvalues (no call-site adapter). + // Lambda-literal call sites SAM-convert unchanged. generic = u.generic.map(str::to_string); // An `Iterable` fold — bare or `Optional`-wrapped — folds with `` // (`acc` lead + `fold` lambda). The wrapped form returns `A?`: `None` @@ -1466,16 +1459,6 @@ fn build_native_call( | ParamMode::Consume | ParamMode::BorrowNullable | ParamMode::ConsumeNullable => format!("{}_ptr", p.kt_name), - ParamMode::ValueUnwrap { field } => { - // Inline value class → pass its erased inner field to the - // extern (e.g. `z.bytes`: a `ByteArray`). A nullable value - // class (`ZBytes?`) safe-navigates so it stays `ByteArray?`. - if p.kt_type.is_nullable() { - format!("{}?.{}", p.kt_name, field) - } else { - format!("{}.{}", p.kt_name, field) - } - } ParamMode::Unsigned64 { niche } => { if p.kt_type.is_nullable() { match niche { @@ -1500,7 +1483,7 @@ fn build_native_call( } } // Callback lambda → the param itself (the extern takes the - // erased `Any`), or its value-blob rebuilding adapter. + // erased `Any`). ParamMode::Callback { call_arg } => call_arg.clone(), ParamMode::FlattenStruct { .. } => { unreachable!("FlattenStruct expanded before the single-arg match") @@ -1539,8 +1522,8 @@ fn build_native_call( fn build_success_return(ext: &JniGen, out: &OutputPlan, raw: &str) -> String { if let Some(p) = &out.projection { // Fold the wrap through the projection strategy. The wrap class is - // the projection leaf's typed short name (Handle's typed-handle - // class or value-class wrapper). The sentinel is the Kotlin + // the projection leaf's typed short name (a Handle's typed-handle + // class, or `ULong`). The sentinel is the Kotlin // null-representation literal for the leaf wire — used only by // the `Niche+primitive` arm of `fold_projection_wrap`. let leaf_fqn = ext @@ -2009,8 +1992,8 @@ fn render_body( /// The Kotlin typing of one delivered lambda leaf: `(builder_kt, wire_kt, /// wrap, is_value_projection)` — the type the *user's* lambda sees, the type the /// extern delivers, and the expression rebuilding the former from the latter -/// (`pk` is the adapter's parameter name; passthrough unless the leaf is a -/// `value_blob`, whose `@JvmInline value class` can't be built Rust-side). +/// (`pk` is the adapter's parameter name; passthrough unless the leaf carries a +/// value projection that can't be built Rust-side). /// Shared by the unfold builder/fold lambda and the callback lambda params. pub(crate) fn unfold_leaf_kt( ext: &JniGen, @@ -2027,13 +2010,12 @@ pub(crate) fn unfold_leaf_kt( .map(|p| { matches!( p.kind, - crate::api::lang::jnigen::jni::ProjectionKind::ValueBlob - | crate::api::lang::jnigen::jni::ProjectionKind::Unsigned64 + crate::api::lang::jnigen::jni::ProjectionKind::Unsigned64 ) }) .unwrap_or(false); // builder_kt: enum → Int; otherwise the normal classified type - // (handle class / value class / String / ByteArray / Long …). + // (handle class / String / ByteArray / Long …). let builder_kt = if ext.is_kotlin_enum(&enum_probe_type(out_ty)) { kt::KtType::int() } else { @@ -2146,12 +2128,11 @@ pub(crate) fn kotlin_for_wire(wire: &syn::Type) -> Option { /// * `kt_return` is the declared Kotlin return type written in the /// wrapper's signature (empty for `Unit`). /// * `projection` is `Some(Projection)` when the return is a Kotlin newtype -/// (opaque handle or value class) reached through 0+ wrappers. The +/// (opaque handle or unsigned scalar) reached through 0+ wrappers. The /// wrapper body uses it to fold the wrap call (`W(x)` for `Direct`, /// `?.let { W(it) }` for `Nullable`, `.map { W(it) }` for `Iterable`) -/// and pick the JNI extern's wire return (`Long` for `Handle`, -/// the inner wire's Kotlin name for `ValueClass`). `None` for plain -/// non-projection returns. +/// and pick the JNI extern's wire return (`Long` for `Handle`). `None` for +/// plain non-projection returns. pub(crate) fn classify_return( ext: &JniGen, output: &syn::ReturnType, @@ -2185,9 +2166,8 @@ pub(crate) fn render_return_surface( } => { let fqn = leaf_fqn.clone().unwrap_or_else(|| { panic!( - "classify_return: projection return type `{}` has no Kotlin FQN registered \ - — every opaque/value class must be declared via `JniGen::ptr_class(...)` \ - / `JniGen::value_class(...)`.", + "classify_return: projection return type `{}` has no Kotlin FQN \ + registered — every opaque class must be declared via `ptr_class!(...)`.", projection.leaf_key ) }); diff --git a/prebindgen/src/api/lang/jnigen/jni/struct_plan.rs b/prebindgen/src/api/lang/jnigen/jni/struct_plan.rs index 5393fc3f..ff9dcf4a 100644 --- a/prebindgen/src/api/lang/jnigen/jni/struct_plan.rs +++ b/prebindgen/src/api/lang/jnigen/jni/struct_plan.rs @@ -94,9 +94,8 @@ impl ConvChain { } pub(crate) enum PlanFieldKind { - /// Opaque-handle / value-blob leaf. Wire slot: `jlong` (`"J"`) for a - /// handle, `ByteArray` (`"[B"`) for a blob; the factory rebuilds the - /// typed value from `fqn`. + /// Projection leaf (opaque handle / `ULong`). Wire slot: `jlong` (`"J"`); + /// the factory rebuilds the typed value from `fqn`. Projection { conv: ConvChain, proj: Projection, @@ -260,7 +259,7 @@ pub(crate) fn classify_field( let conv = ConvChain::of(field_entry); { - // Projection leaf (opaque handle / value blob). + // Projection leaf (opaque handle / `ULong`). if let Some(proj) = field_entry.metadata.projection.clone() { if matches!(proj.strategy, FoldStrategy::Iterable(_)) { panic!( @@ -343,16 +342,10 @@ pub(crate) fn classify_field( .or_else(|| { if pat_match_top(&slot_ty, "Vec") { Some("Ljava/util/List;".to_string()) - } else if let syn::Type::Path(tp) = &wire { - tp.path.segments.last().and_then(|seg| { - match seg.ident.to_string().as_str() { - "JString" => Some("Ljava/lang/String;".to_string()), - "JByteArray" => Some("[B".to_string()), - _ => None, - } - }) } else { - None + // The wire table already names every reference wire's + // descriptor — String and the eight primitive arrays. + jni_field_access(&wire).map(|(sig, _, _)| sig.to_string()) } }) .unwrap_or_else(|| "Ljava/lang/Object;".to_string()); @@ -441,8 +434,8 @@ impl PlanFieldKind { /// The close strategy when this field owns a native handle, so the class /// implements `AutoCloseable` and `close()` walks it. Only an **owned** - /// `Handle` projection qualifies: a value blob is erased to its inner wire - /// and owns nothing, and a borrowed handle is not this object's to release. + /// `Handle` projection qualifies: a `ULong` owns nothing, and a borrowed + /// handle is not this object's to release. pub(crate) fn destructible(&self) -> Option { match self { PlanFieldKind::Projection { proj, .. } diff --git a/prebindgen/src/api/lang/jnigen/jni/symbols.rs b/prebindgen/src/api/lang/jnigen/jni/symbols.rs index f6cc54bd..ef4e8f4e 100644 --- a/prebindgen/src/api/lang/jnigen/jni/symbols.rs +++ b/prebindgen/src/api/lang/jnigen/jni/symbols.rs @@ -59,7 +59,7 @@ pub(crate) fn validate_symbols(ext: &JniGen, registry: &Registry) -> // clash"); distinct signatures are legitimate overloads and pass. let mut overloads: BTreeMap<(String, String, JvmSignature), String> = BTreeMap::new(); let mut add_overload = |scope: &str, f: &kt::KtFun, origin: &str, errors: &mut Vec| { - let sig = jvm_signature(ext, f); + let sig = jvm_signature(f); let key = (scope.to_string(), f.name.clone(), sig.clone()); if let Some(prev) = overloads.insert(key, origin.to_string()) { errors.push(format!( @@ -470,12 +470,10 @@ fn boxed_primitive(simple: &str) -> Option<&'static str> { /// the boxed `kotlin.ULong` class; /// * `String` / `ByteArray` / `Any` → their JVM types (object nullability is /// irrelevant to the descriptor); -/// * a `@JvmInline value class` → its underlying wire (`byte[]`), so two -/// distinct value classes clash; /// * a generic type → its raw class (`List` → `List`), arguments erased; /// * any other class → its FQN (distinct classes stay distinct); /// * a function type → `kotlin.Function`. -pub(crate) fn erase_kt_type(ext: &JniGen, generics: &[String], ty: &kt::KtType) -> ErasedJvmType { +pub(crate) fn erase_kt_type(generics: &[String], ty: &kt::KtType) -> ErasedJvmType { use kt::KtType; let token = match ty { KtType::Function { params, .. } => format!("kotlin.Function{}", params.len()), @@ -483,8 +481,6 @@ pub(crate) fn erase_kt_type(ext: &JniGen, generics: &[String], ty: &kt::KtType) let simple = ty.simple_name().unwrap_or(fqn); if generics.iter().any(|g| g == fqn) { "java.lang.Object".to_string() - } else if ext.is_value_blob_kotlin(simple) { - "byte[]".to_string() } else if simple == "ULong" { if *nullable { "kotlin.ULong".to_string() @@ -517,11 +513,11 @@ pub(crate) fn erase_kt_type(ext: &JniGen, generics: &[String], ty: &kt::KtType) /// The [`JvmSignature`] of a generated wrapper (`render_wrapper_fn` / /// `render_param_overloads` output): each parameter erased through /// [`erase_kt_type`] under the function's own generic type variables. -pub(crate) fn jvm_signature(ext: &JniGen, f: &kt::KtFun) -> JvmSignature { +pub(crate) fn jvm_signature(f: &kt::KtFun) -> JvmSignature { JvmSignature( f.params .iter() - .map(|p| erase_kt_type(ext, &f.generics, &p.ty)) + .map(|p| erase_kt_type(&f.generics, &p.ty)) .collect(), ) } @@ -543,15 +539,12 @@ impl NativeSymbol { #[cfg(test)] mod tests { - use super::{ - erase_kt_type, is_valid_kotlin_ident, mangle_kotlin_ident, mangle_package, JniGen, - }; + use super::{erase_kt_type, is_valid_kotlin_ident, mangle_kotlin_ident, mangle_package}; use crate::api::gen::kotlin as kt; fn erase(generics: &[&str], ty: kt::KtType) -> String { - let ext = JniGen::new(); let gs: Vec = generics.iter().map(|s| s.to_string()).collect(); - erase_kt_type(&ext, &gs, &ty).to_string() + erase_kt_type(&gs, &ty).to_string() } #[test] diff --git a/prebindgen/src/api/lang/jnigen/jni/tests/callbacks.rs b/prebindgen/src/api/lang/jnigen/jni/tests/callbacks.rs index 0db9ef0e..dbba8910 100644 --- a/prebindgen/src/api/lang/jnigen/jni/tests/callbacks.rs +++ b/prebindgen/src/api/lang/jnigen/jni/tests/callbacks.rs @@ -268,7 +268,7 @@ fn callback_root_identity_moved_after_nested_borrow() { /// `z_sample_timestamp`), a nested handle identity reached *through* an /// `Option` step (`z_reply_sample` → `z_sample_key_expr`), and an Acc leaf /// whose own return keeps its full `Option<…>` as the converter input -/// (`z_reply_zid -> Option`, a value class with no canonical child). +/// (`z_reply_zid -> Option`, a data class with no canonical child). /// Every `Option` nesting step must become its own `match` (`None` ⇒ null /// leaf) — never a blind accessor compose through an `Option`. #[test] @@ -293,6 +293,17 @@ fn callback_double_option_unwrap_pipeline() { (syn::Item::Fn(f), loc.clone()) }) .collect(); + // `ZId` is the value leaf of the outer `Option`; it needs a real struct so + // it can be a `data_class!`. + items.push(( + syn::Item::Struct(syn::parse_quote!( + pub struct ZId { + pub hi: i64, + pub lo: i64, + } + )), + loc.clone(), + )); items.push(( syn::Item::Fn(syn::parse_quote!( pub fn z_get(cb: impl Fn(ZReply) + Send + Sync + 'static) { @@ -307,7 +318,7 @@ fn callback_double_option_unwrap_pipeline() { .set_package_prefix("io.test.jni") .package( crate::package!("query") - .class(crate::value_class!(ZId)) + .class(crate::data_class!(ZId)) .class( crate::ptr_class!(ZKeyExpr).method(crate::fun!(z_keyexpr_as_str).name("asStr")), ) @@ -377,11 +388,13 @@ fn callback_double_option_unwrap_pipeline() { // converter — no unwrap of the leaf's own `Option`. assert!(rc.contains("myflat::z_reply_zid(&__cb_arg0)"), "{rust}"); assert!(!rc.contains("matchmyflat::z_reply_zid("), "{rust}"); - // 6 leaves ⇒ typed `run` descriptor: nullable value-blob `[B`, raw `Z` + // 6 leaves ⇒ typed `run` descriptor: nullable `ZId` data class, raw `Z` // for the non-null bool discriminator, typed handle class (full FQN), // nullable String, BOXED Long for the nullable timestamp, nullable `[B`. assert!( - rc.contains("\"([BZLjava/lang/Long;Ljava/lang/String;Ljava/lang/Long;[B)V\""), + rc.contains( + "\"(Lio/test/jni/query/ZId;ZLjava/lang/Long;Ljava/lang/String;Ljava/lang/Long;[B)V\"" + ), "{rust}" ); // The non-null bool crosses as a raw typed jvalue — never boxed. @@ -389,8 +402,8 @@ fn callback_double_option_unwrap_pipeline() { // Kotlin tier: the generated callback `fun interface` carries the typed // params — ok-arm and err-arm leaves nullable (the value may be absent), - // the discriminator non-null; the value-blob leaf surfaces as its raw - // (nullable) ByteArray wire, NOT the value class — the SDK wraps. + // the discriminator non-null; the nested `ZId` data class surfaces as its + // typed (nullable) Kotlin class. let kdir = dir.join("kotlin"); let paths = gen.write_kotlin(&kdir).expect("write_kotlin"); let iface_file = paths @@ -399,7 +412,7 @@ fn callback_double_option_unwrap_pipeline() { .find(|v| v.contains("fun interface ZReplyCallback")) .unwrap_or_default(); // Scope to the interface block — the merged package file also holds the - // ZId value class and other decls. + // ZId data class and other decls. let iface = iface_file .split("fun interface ZReplyCallback") .nth(1) @@ -410,8 +423,7 @@ fn callback_double_option_unwrap_pipeline() { assert!(ic.contains("sample__keyExpr:ZKeyExpr?"), "{iface}"); assert!(ic.contains(":Long?"), "{iface}"); assert!(ic.contains(":ZId?"), "{iface}"); - // The wrapper takes the typed interface and forwards it bare (no - // value-blob rebuilding adapter exists anymore). + // The wrapper takes the typed interface and forwards it bare. let pkg = paths .iter() .filter_map(|p| std::fs::read_to_string(p).ok()) diff --git a/prebindgen/src/api/lang/jnigen/jni/tests/config.rs b/prebindgen/src/api/lang/jnigen/jni/tests/config.rs index 133ccf9a..665138ed 100644 --- a/prebindgen/src/api/lang/jnigen/jni/tests/config.rs +++ b/prebindgen/src/api/lang/jnigen/jni/tests/config.rs @@ -214,12 +214,12 @@ fn interface_name_override_and_hook() { ); } -/// #54: `.interface()` on a `value_class!` — the generated `Api` -/// interface carries the `bytes` property and the accessors, and the -/// `@JvmInline value class` implements it with `override val bytes` -/// (the ctor-prop path, shared with data/enum classes). +/// #54: `.interface()` on a `data_class!` — the generated `Api` +/// interface carries the field properties and the accessors, and the +/// `data class` implements it with `override val` on each field +/// (the ctor-prop path, shared with ptr/enum classes). #[test] -fn value_class_interface_emits_generated_api() { +fn data_class_interface_emits_generated_api() { let loc = myflat_loc(); let items: Vec<(syn::Item, SourceLocation)> = vec![ ( @@ -227,6 +227,7 @@ fn value_class_interface_emits_generated_api() { #[derive(Clone, Copy)] pub struct ZStamp { pub secs: i64, + pub nanos: i64, } )), loc.clone(), @@ -243,12 +244,12 @@ fn value_class_interface_emits_generated_api() { let registry = Registry::::from_items(items).expect("index"); let jni = JniGen::new().set_package_prefix("io.test.jni").package( crate::package!("t").class( - crate::value_class!(ZStamp) + crate::data_class!(ZStamp) .interface() .method(crate::fun!(z_stamp_secs).name("secs")), ), ); - let dir = unique_test_dir("jnigen_value_iface"); + let dir = unique_test_dir("jnigen_data_iface"); let _ = std::fs::remove_dir_all(&dir); std::fs::create_dir_all(&dir).unwrap(); let gen = registry.resolve(jni).expect("resolve"); @@ -261,14 +262,20 @@ fn value_class_interface_emits_generated_api() { .join("\n"); let ac: String = all.split_whitespace().collect(); assert!(ac.contains("interfaceZStampApi{"), "{all}"); - assert!(ac.contains("valbytes:ByteArray"), "{all}"); + // The interface carries one property per struct FIELD (the data class + // crosses as its fields), plus the declared accessor. + assert!(ac.contains("valsecs:Long"), "{all}"); + assert!(ac.contains("valnanos:Long"), "{all}"); assert!(ac.contains("funsecs("), "{all}"); assert!( - ac.contains("valueclassZStamp(overridepublicvalbytes:ByteArray):ZStampApi{") - || ac.contains("valueclassZStamp(publicoverridevalbytes:ByteArray):ZStampApi{"), + ac.contains("dataclassZStamp(overridevalsecs:Long,overridevalnanos:Long):ZStampApi{"), "{all}" ); assert!(ac.contains("publicoverridefunsecs("), "{all}"); + // Equality comes from the `data class` itself (all-scalar ctor props), so + // no hand-written `equals`/`hashCode` members are emitted. + assert!(!ac.contains("funequals("), "{all}"); + assert!(!ac.contains("funhashCode("), "{all}"); } /// Per-declaration class rename (`.name()`, the type-level dual of the per-fn diff --git a/prebindgen/src/api/lang/jnigen/jni/tests/sealed.rs b/prebindgen/src/api/lang/jnigen/jni/tests/sealed.rs index 015100fb..5e92634d 100644 --- a/prebindgen/src/api/lang/jnigen/jni/tests/sealed.rs +++ b/prebindgen/src/api/lang/jnigen/jni/tests/sealed.rs @@ -346,39 +346,59 @@ fn a_type_gets_one_class_declarator() { drop(registry); }; - // Every conflicting pair among the five declarators is rejected, in both + // Every conflicting pair among the four declarators is rejected, in both // orders — the second declaration is matched against the kind the first // one stored, so it does not depend on which came first. type MakeDecl = fn() -> crate::lang::ClassDecl; let pairs: Vec<(MakeDecl, MakeDecl)> = vec![ ( || crate::sealed_class!(Reading).into(), - || crate::value_class!(Reading).into(), + || crate::data_class!(Reading).into(), ), ( - || crate::value_class!(Reading).into(), + || crate::data_class!(Reading).into(), || crate::sealed_class!(Reading).into(), ), ( || crate::sealed_class!(Reading).into(), || crate::enum_class!(Reading).into(), ), + ( + || crate::enum_class!(Reading).into(), + || crate::sealed_class!(Reading).into(), + ), ( || crate::sealed_class!(Reading).into(), || crate::ptr_class!(Reading).into(), ), + ( + || crate::ptr_class!(Reading).into(), + || crate::sealed_class!(Reading).into(), + ), ( || crate::data_class!(Sample).into(), - || crate::value_class!(Sample).into(), + || crate::enum_class!(Sample).into(), ), ( - || crate::value_class!(Sample).into(), + || crate::enum_class!(Sample).into(), || crate::data_class!(Sample).into(), ), ( || crate::ptr_class!(Sample).into(), || crate::data_class!(Sample).into(), ), + ( + || crate::data_class!(Sample).into(), + || crate::ptr_class!(Sample).into(), + ), + ( + || crate::ptr_class!(Sample).into(), + || crate::enum_class!(Sample).into(), + ), + ( + || crate::enum_class!(Sample).into(), + || crate::ptr_class!(Sample).into(), + ), ]; for (a, b) in pairs { assert!( diff --git a/prebindgen/src/api/lang/jnigen/jni/tests/values.rs b/prebindgen/src/api/lang/jnigen/jni/tests/values.rs index a1073d35..7fb8f4e8 100644 --- a/prebindgen/src/api/lang/jnigen/jni/tests/values.rs +++ b/prebindgen/src/api/lang/jnigen/jni/tests/values.rs @@ -1435,3 +1435,235 @@ fn data_class_properties_match_their_from_parts_params() { assert!(kc.contains("Child.fromParts(child_n)"), "{kotlin}"); assert!(kc.contains("Level.fromInt(level)"), "{kotlin}"); } + +/// Every shape an array length can take — a FREE const, an ASSOCIATED const, +/// and a `const fn` CALL — is qualified against its origin module, and that +/// rewrite reaches ONLY the length, never a converter body's locals. +/// +/// None of the three owners is declared to JniGen: each is a compile-time +/// namespace, not a boundary type, so qualification must not depend on a +/// Kotlin class existing for it. +/// +/// The const here is deliberately named `env`, which is also the name of the +/// `JNIEnv` local every generated converter uses. A source crate may legally +/// declare it (`#[allow(non_upper_case_globals)] pub const env`), so a +/// whole-item expression pass would rewrite `env.get_java_vm()` to +/// `myflat::env.get_java_vm()` even when restricted to registered const idents +/// — thousands of `no method named get_java_vm found for type usize`. Scoping +/// the pass to `TypeArray::len` is what makes the two cases distinguishable. +#[test] +fn array_length_const_is_qualified_without_touching_locals() { + // Stamped stream: names qualify with the origin crate's module. + check_array_length_qualification(myflat_loc(), "myflat"); +} + +/// The same contract for an ORIGIN-LESS stream. Core supports hand-built item +/// streams with no `SourceLocation::crate_name` and documents `crate` as their +/// module, so the name set must not be derived from the origin map — those +/// items are absent from it entirely, and deriving from it silently emitted +/// every length bare. +#[test] +fn array_length_qualification_falls_back_to_crate_without_an_origin() { + check_array_length_qualification(SourceLocation::default(), "crate"); +} + +fn check_array_length_qualification(loc: SourceLocation, module: &str) { + let mut items: Vec<(syn::Item, SourceLocation)> = Vec::new(); + items.push(( + syn::Item::Const(syn::parse_quote!( + #[allow(non_upper_case_globals)] + pub const env: usize = 4; + )), + loc.clone(), + )); + // A type owning an ASSOCIATED const, used as the other length below. It is + // deliberately NEVER declared to JniGen: it is only the Rust namespace for + // a compile-time length, not a boundary type, so qualification must not + // require a Kotlin class to exist for it. + items.push(( + syn::Item::Struct(syn::parse_quote!( + pub struct Holder { + pub marker: u8, + } + )), + loc.clone(), + )); + // A `const fn` whose CALL is a length. Also never declared: its result + // determines an array size, which is no reason to put it in the Kotlin + // surface. + items.push(( + syn::Item::Fn(syn::parse_quote!( + pub const fn array_len() -> usize { + 4 + } + )), + loc.clone(), + )); + items.push(( + syn::Item::Struct(syn::parse_quote!( + pub struct Blob { + pub bytes: [u8; env], + pub assoc: [u8; Holder::N], + pub called: [u8; array_len()], + } + )), + loc.clone(), + )); + items.push(( + syn::Item::Fn(syn::parse_quote!( + pub fn blob_echo(b: Blob) -> Blob { + unimplemented!() + } + )), + loc.clone(), + )); + let registry = Registry::::from_items(items).unwrap(); + let jni = JniGen::new().set_package_prefix("io.test.jni").package( + crate::package!("blob") + .class(crate::data_class!(Blob)) + .fun(crate::fun!(blob_echo)), + ); + let dir = unique_test_dir(&format!("jnigen_array_len_const_{module}")); + let _ = std::fs::remove_dir_all(&dir); + std::fs::create_dir_all(&dir).unwrap(); + let generation = registry.resolve(jni).unwrap(); + let rust_path = generation.write_rust(dir.join("gen.rs")).unwrap(); + let rust = std::fs::read_to_string(rust_path).unwrap(); + let rc: String = rust.split_whitespace().collect(); + + // The length IS qualified — otherwise the generated file names a const + // that is not in scope. + assert!(rc.contains(&format!("[u8;{module}::env]")), "{rust}"); + // ...and the identically-named LOCAL is untouched. These two assertions + // fail in opposite directions, so neither alone pins the behavior: the + // `env` here is the `&mut JNIEnv` every converter body threads through. + assert!(rc.contains("env.byte_array_from_slice"), "{rust}"); + assert!( + !rc.contains(&format!("{module}::env.byte_array_from_slice")), + "{rust}" + ); + assert!(!rc.contains(&format!("{module}::env,")), "{rust}"); + assert!(!rc.contains(&format!("&mut{module}::env")), "{rust}"); + + // An ASSOCIATED const qualifies its leading TYPE segment and leaves the + // rest of the path relative to it — `myflat::Holder::N`, never + // `myflat::Holder::myflat::N`. `Holder` is UNDECLARED, so this also pins + // that qualification reads the registry rather than the declared surface. + // Asserted at the two CODE positions (return type and param type); the bare + // spelling legitimately survives inside the decode's diagnostic string, + // which names the type as the source wrote it. + assert!( + rc.contains(&format!("Result<[u8;{module}::Holder::N]")), + "{rust}" + ); + assert!( + rc.contains(&format!("v:[u8;{module}::Holder::N]")), + "{rust}" + ); + assert!(!rc.contains("Result<[u8;Holder::N]"), "{rust}"); + assert!(!rc.contains("v:[u8;Holder::N]"), "{rust}"); + // The leading segment is rewritten ONCE — the associated item stays + // relative to the type it belongs to. + assert!( + !rc.contains(&format!("{module}::Holder::{module}")), + "{rust}" + ); + + // A `const fn` CALL is a third shape a length can take, and its callee is + // an indexed item like the other two. Also undeclared. + assert!( + rc.contains(&format!("Result<[u8;{module}::array_len()]")), + "{rust}" + ); + assert!( + rc.contains(&format!("v:[u8;{module}::array_len()]")), + "{rust}" + ); + assert!(!rc.contains("Result<[u8;array_len()]"), "{rust}"); + assert!(!rc.contains("v:[u8;array_len()]"), "{rust}"); +} + +/// An array length whose expression form is not on the supported whitelist is +/// REJECTED, not qualified. +/// +/// An inline `const { … }` block may bind locals, and this generator qualifies +/// a length's bare paths against their source module — so a local shadowing a +/// source item would be rewritten into it (`array_len` the local becoming +/// `myflat::array_len` the fn). Scope tracking is the general answer; the shape +/// has no place in an FFI boundary type, so the whole family is refused with a +/// message naming the type and the fix. Silently mis-qualifying is the +/// alternative this exists to prevent. +#[test] +#[should_panic(expected = "an unsupported expression form")] +fn array_length_inline_const_block_is_rejected() { + // A local bound by an inline const block, shadowing the indexed fn. + check_array_length_rejected(syn::parse_quote!( + [u8; const { + let array_len = 3; + array_len + }] + )); +} + +/// `match` arms bind their patterns directly, with no `Expr::Block` node in +/// between — which is how this form slipped past the first, blacklist-shaped +/// attempt. The whitelist refuses it because `match` is simply not on the list. +#[test] +#[should_panic(expected = "an unsupported expression form")] +fn array_length_match_arm_binding_is_rejected() { + check_array_length_rejected(syn::parse_quote!( + [u8; match 3 { + array_len => array_len, + }] + )); +} + +/// `if let` likewise binds without an intervening block node. +#[test] +#[should_panic(expected = "an unsupported expression form")] +fn array_length_if_let_binding_is_rejected() { + check_array_length_rejected(syn::parse_quote!( + [u8; if let array_len = 3 { array_len } else { 0 }] + )); +} + +fn check_array_length_rejected(field_ty: syn::Type) { + let loc = myflat_loc(); + let mut items: Vec<(syn::Item, SourceLocation)> = Vec::new(); + items.push(( + syn::Item::Fn(syn::parse_quote!( + pub const fn array_len() -> usize { + 4 + } + )), + loc.clone(), + )); + // The offending length; in each case its binding shadows `array_len`. + items.push(( + syn::Item::Struct(syn::parse_quote!( + pub struct Blob { + pub local: #field_ty, + } + )), + loc.clone(), + )); + items.push(( + syn::Item::Fn(syn::parse_quote!( + pub fn blob_echo(b: Blob) -> Blob { + unimplemented!() + } + )), + loc.clone(), + )); + let registry = Registry::::from_items(items).unwrap(); + let jni = JniGen::new().set_package_prefix("io.test.jni").package( + crate::package!("blob") + .class(crate::data_class!(Blob)) + .fun(crate::fun!(blob_echo)), + ); + let dir = unique_test_dir("jnigen_array_len_scope"); + let _ = std::fs::remove_dir_all(&dir); + std::fs::create_dir_all(&dir).unwrap(); + let generation = registry.resolve(jni).unwrap(); + generation.write_rust(dir.join("gen.rs")).unwrap(); +} diff --git a/prebindgen/src/api/lang/jnigen/jni/trait_impl.rs b/prebindgen/src/api/lang/jnigen/jni/trait_impl.rs index 5fcf1b7b..930a2c30 100644 --- a/prebindgen/src/api/lang/jnigen/jni/trait_impl.rs +++ b/prebindgen/src/api/lang/jnigen/jni/trait_impl.rs @@ -372,11 +372,36 @@ impl JniGen { /// site having to remember to qualify. fn qualify_item(&self, item: &mut syn::Item, registry: &Registry) { let source_names = self.emitted_source_type_names(registry); - if source_names.is_empty() { - return; - } + // Names reachable from an array LENGTH (`[u8; MAX]`, `[u8; Holder::N]`). + // + // Registry-wide, NOT the declared-surface `source_names`: a length's + // owner is a compile-time namespace, not a boundary type. Requiring it + // to be declared would force an otherwise-unused Kotlin class into + // existence just to make the generated Rust compile, and would be + // asymmetric with consts, which qualify whether or not JniGen declared + // them. + // EVERY named item the registry indexes. A length is an arbitrary const + // expression, so it can name a const, the type owning an associated + // const, or a `const fn` — and enumerating item KINDS here missed one + // of those three twice, so the enumeration lives in core + // (`named_item_idents`) where a new kind is added once. + // + // The NAME SET is independent of origin stamps and the VALUE falls back + // to the default module: an origin-less hand-built stream indexes items + // that `item_origins` never sees, and those still need qualifying (core + // documents `crate` as their module). + let length_names: std::collections::HashMap = registry + .named_item_idents() + .map(|ident| { + let module = registry + .origin_module(ident) + .unwrap_or_else(|| self.default_module(registry)); + (ident.to_string(), module) + }) + .collect(); let mut visitor = QualifyEmittedTypes { source_names: &source_names, + length_names: &length_names, }; syn::visit_mut::VisitMut::visit_item_mut(&mut visitor, item); } @@ -868,22 +893,23 @@ impl JniGen { } /// True when `elem` crosses the boundary as a **single leaf** the foreign - /// side can reassemble from one wire value — a value blob (→ `ByteArray`) or - /// the `String` builtin (→ `JString`). Multi-field `data_class` elements - /// (whose output is a `fromParts` object), enums, and opaque handles are - /// excluded. Drives [`Self::leaf_vec_fold_elements`]. + /// side can reassemble from one wire value — a declared opaque handle + /// (→ a `jlong` pointer), the `String` builtin (→ `JString`), or the `u64` + /// scalar projection (→ a raw `jlong` the folder wraps into `ULong`). + /// Multi-field `data_class` elements (whose output is a `fromParts` + /// object) and enums are excluded. Drives + /// [`Self::leaf_vec_fold_elements`]. /// /// Classified from the adapter's declared [`TypeConfig`] table (and the /// `String` builtin), not the resolver's output converters — this runs /// **before** type resolution, exactly like [`Self::value_struct_decons`]. fn is_leaf_vec_element(&self, elem: &syn::Type) -> bool { match self.types.get(&TypeKey::from_type(elem)) { - // A declared value blob crosses as a single `ByteArray` leaf; a - // declared opaque handle crosses as a single `jlong` (pointer) leaf - // that the Kotlin folder wraps into its typed handle class. Enums - // and multi-field data classes are not leaf-folded — data classes go - // through `value_struct_decons`. - Some(cfg) => cfg.is_value_blob() || cfg.is_opaque(), + // A declared opaque handle crosses as a single `jlong` (pointer) + // leaf that the Kotlin folder wraps into its typed handle class. + // Enums and multi-field data classes are not leaf-folded — data + // classes go through `value_struct_decons`. + Some(cfg) => cfg.is_opaque(), // Undeclared: `String` is JObject-shaped; `u64` is the built-in // scalar projection whose raw jlong leaf the Kotlin folder wraps // into `ULong`. Other primitive collections retain their existing @@ -963,7 +989,7 @@ impl Prebindgen for JniGen { let source: syn::Type = syn::parse_quote!(#ident); let key = TypeKey::from_type(&source); // A `data_class` is a registered type that is neither an opaque - // handle, an enum, nor a value blob. + // handle nor an enum. let is_data_class = matches!( self.type_kind(registry, &source), TypeKind::DataStruct { cfg: Some(c), .. } if c.name_spec.is_some() @@ -1031,8 +1057,8 @@ impl Prebindgen for JniGen { /// `Option>` return or an `impl Fn(&[T])` callback arg, so /// [`crate::api::core::unfold::apply_leaf_vec_folds`] routes the collection /// through a foreign-built fold (no Rust `ArrayList`). A single-leaf element - /// is a value blob (→ `ByteArray`), an opaque handle (→ a `jlong` pointer - /// the Kotlin folder wraps into its typed handle class), or a non-`data_class` + /// is an opaque handle (→ a `jlong` pointer the Kotlin folder wraps into its + /// typed handle class) or a non-`data_class` /// builtin with a JObject-shaped output wire (e.g. String). Multi-field /// `data_class` elements are excluded — they go through /// [`Self::value_struct_decons`]. @@ -1135,8 +1161,8 @@ impl Prebindgen for JniGen { } /// Every type registered via one of the **class declarators** - /// (`ptr_class!` / `enum_class!` / `sealed_class!` / `data_class!` / - /// `value_class!`) — i.e. every entry in the type table, whose only + /// (`ptr_class!` / `enum_class!` / `sealed_class!` / `data_class!`) + /// — i.e. every entry in the type table, whose only /// writer is `JniGen::register_class`. These are the only structs/enums /// the per-item emitter walks, and the scan requires them in BOTH /// directions (their converters always resolve both ways). Wrapper @@ -1499,23 +1525,6 @@ impl Prebindgen for JniGen { // the handle (see `ParamMode::VecBuild`), avoiding per-element // `env.get_field(...)` upcalls on the Rust side. items.extend(build_vec_build_helper_items(self, registry)); - // Compile-time `Copy` assertion per `value_blob` type — the blob - // converters reinterpret raw bytes by value, which is only sound for - // `Copy` types. A mis-declared non-`Copy` type fails to compile here - // (at the include site) with a clear bound error rather than at a - // converter use. The bare type name is qualified against - // its origin module by `post_process_item` like every other body. - for (key, cfg) in &self.types { - if cfg.is_value_blob() { - let ty = key.to_type(); - items.push(syn::parse_quote!( - const _: () = { - const fn __assert_copy() {} - __assert_copy::<#ty>(); - }; - )); - } - } // Expression constants — one nullary JNI getter extern per // `PackageDecl::constant_expr`, its value the binding-defined // expression evaluated with a glob import of every source module (so @@ -1623,8 +1632,8 @@ impl Prebindgen for JniGen { impl JniGen { // ── Input converters ───────────────────────────────────────────── - /// Whole-type **input** terminal categories (opaque handle, value-blob, - /// enum, the rank-0 user table, `str`, primitive, struct) — depends on + /// Whole-type **input** terminal categories (opaque handle, enum, the + /// rank-0 user table, `str`, primitive, struct) — depends on /// nothing, `subs` empty. pub(crate) fn input_terminal( &self, @@ -1639,31 +1648,13 @@ impl JniGen { return Some(self.opaque_handle_input(ty)); } } - // `value_blob`-declared `Copy` types: decode the raw memory blob out - // of a `JByteArray` (length-checked, `read_unaligned` since the byte - // array isn't aligned to the type). Returns owned `T`, so `&T` / - // by-value / `Vec` / `Option` all compose through the existing - // handlers. `T: Copy` ⇒ reading the value out is sound (no double - // drop); the `Copy` bound itself is enforced by the assertion in - // `prerequisites`. - if self.types.get(&key).is_some_and(|c| c.is_value_blob()) { - let wire: syn::Type = syn::parse_quote!(jni::objects::JByteArray); - let body: syn::Expr = syn::parse_quote!({ - let __bytes = env.convert_byte_array(v).map_err(|e| { - <__JniErr as ::core::convert::From>::from(format!( - "value-blob decode: {}", - e - )) - })?; - if __bytes.len() != ::core::mem::size_of::<#ty>() { - return ::core::result::Result::Err( - <__JniErr as ::core::convert::From>::from( - "value-blob decode: wrong byte length".to_string(), - ), - ); - } - unsafe { ::core::ptr::read_unaligned(__bytes.as_ptr() as *const #ty) } - }); + // Fixed-size array of JNI primitives — dual of the output branch. + // The `try_into` IS the length check: a JVM array of the wrong size + // becomes a binding error naming the type, never a panic. + if let Some(spec) = crate::api::lang::jnigen::jni::prim_array::prim_array_of(ty) { + let body = crate::api::lang::jnigen::jni::prim_array::input_body(ty, &spec); + let wire = spec.wire.clone(); + let kotlin_name = self.override_kotlin_name(ty, Some(spec.kotlin.clone())); let niches = default_niches_for_wire(&wire); return Some(ConverterImpl { subs: vec![], @@ -1671,16 +1662,7 @@ impl JniGen { function: self.build_input_fn(ty, &wire, &body, None), destination: wire, niches, - metadata: KotlinMeta { - projection: Some(Projection { - leaf_key: key.clone(), - owned: false, - strategy: FoldStrategy::Base, - kind: ProjectionKind::ValueBlob, - niche_sentinels: Vec::new(), - }), - ..self.framework_meta(Some(kt::KtType::cls("ByteArray"))) - }, + metadata: self.framework_meta(kotlin_name), }); } // `enum_class`-declared enums: jint wire, `TryFrom` decode. @@ -1866,7 +1848,7 @@ impl JniGen { // ── Output converters ──────────────────────────────────────────── /// Whole-type **output** terminal categories (the dual of - /// [`Self::input_terminal`]: opaque handle, value-blob, enum, user table, + /// [`Self::input_terminal`]: opaque handle, enum, user table, /// `str`, `Cow<[u8]>`, unit, primitive, struct) — `subs` empty. pub(crate) fn output_terminal( &self, @@ -1881,28 +1863,13 @@ impl JniGen { return Some(self.opaque_handle_output(ty)); } } - // `value_blob`-declared `Copy` types: encode the value's raw memory - // bytes into a fresh `JByteArray` (the value-level peer of an opaque - // handle's `jlong`). `v: #ty` is owned and `Copy`, so reading its - // bytes and letting it drop normally is sound. Wire is `JByteArray` - // (jobject-shaped), so `Vec` / `Option` compose through the - // existing handlers — `Vec` surfaces as `List`. - if self.types.get(&key).is_some_and(|c| c.is_value_blob()) { - let wire: syn::Type = syn::parse_quote!(jni::objects::JByteArray); - let body: syn::Expr = syn::parse_quote!({ - let __bytes: &[u8] = unsafe { - ::core::slice::from_raw_parts( - (&v as *const #ty) as *const u8, - ::core::mem::size_of::<#ty>(), - ) - }; - env.byte_array_from_slice(__bytes).map_err(|e| { - <__JniErr as ::core::convert::From>::from(format!( - "value-blob encode: {}", - e - )) - })? - }); + // Fixed-size array of JNI primitives: `[u8; N]` -> `ByteArray`, + // `[i64; N]` -> `LongArray`, ... Bulk-copied, nothing boxed. See + // [`prim_array`]; this replaced the raw-memory value blob. + if let Some(spec) = crate::api::lang::jnigen::jni::prim_array::prim_array_of(ty) { + let body = crate::api::lang::jnigen::jni::prim_array::output_body(&spec); + let wire = spec.wire.clone(); + let kotlin_name = self.override_kotlin_name(ty, Some(spec.kotlin.clone())); let niches = default_niches_for_wire(&wire); return Some(ConverterImpl { subs: vec![], @@ -1910,16 +1877,7 @@ impl JniGen { function: self.build_output_fn(ty, &wire, &body, None), destination: wire, niches, - metadata: KotlinMeta { - projection: Some(Projection { - leaf_key: key.clone(), - owned: false, - strategy: FoldStrategy::Base, - kind: ProjectionKind::ValueBlob, - niche_sentinels: Vec::new(), - }), - ..self.framework_meta(Some(kt::KtType::cls("ByteArray"))) - }, + metadata: self.framework_meta(kotlin_name), }); } // `enum_class`-declared enums: jint wire, `as jni::sys::jint` @@ -2195,7 +2153,7 @@ impl JniGen { Some(kt::KtType::generic("List", [inner_kotlin])), ); // Fold an Iterable layer over the inner projection (if any), so - // `Vec` / `Vec` carry the full strategy. + // `Vec` carries the full strategy. let projection = inner.metadata.projection.clone().map(|h| Projection { strategy: FoldStrategy::Iterable(Box::new(h.strategy)), ..h diff --git a/prebindgen/src/api/lang/jnigen/jni/wire_access.rs b/prebindgen/src/api/lang/jnigen/jni/wire_access.rs index dd761560..1c14143b 100644 --- a/prebindgen/src/api/lang/jnigen/jni/wire_access.rs +++ b/prebindgen/src/api/lang/jnigen/jni/wire_access.rs @@ -15,6 +15,69 @@ use super::JniPrim; /// Object types (`JString`, `JByteArray`, …) set `is_object = true`; the /// caller uses `.l()` to get a `JObject` and then `.into()` to cast to the /// wire type. +/// The Kotlin primitive arrays and their JVM descriptors. +/// +/// ONE table, read forwards (Kotlin type → descriptor, for interface signatures +/// and field slots) and backwards (descriptor → empty literal, for inert +/// flattened slots). Four separate copies of this knowledge existed before +/// fixed-size arrays needed all of them, and only some got updated. +pub(crate) const KOTLIN_PRIM_ARRAYS: [(&str, &str); 8] = [ + ("BooleanArray", "[Z"), + ("ByteArray", "[B"), + ("CharArray", "[C"), + ("ShortArray", "[S"), + ("IntArray", "[I"), + ("LongArray", "[J"), + ("FloatArray", "[F"), + ("DoubleArray", "[D"), +]; + +/// JVM descriptor of a Kotlin primitive array (`ByteArray` → `[B`). +pub(crate) fn kotlin_array_descriptor(name: &str) -> Option<&'static str> { + KOTLIN_PRIM_ARRAYS + .iter() + .find(|(n, _)| *n == name) + .map(|(_, d)| *d) +} + +/// The Kotlin primitive array a JVM array descriptor names (`[B` → `ByteArray`). +pub(crate) fn kotlin_array_of_descriptor(descr: &str) -> Option<&'static str> { + KOTLIN_PRIM_ARRAYS + .iter() + .find(|(_, d)| *d == descr) + .map(|(n, _)| *n) +} + +/// The JNI **reference** (object-shaped) wire types: the `J*` handles plus the +/// eight primitive arrays. +/// +/// ONE list. It drives the JVM field descriptor, the wire→`JObject` cast, and +/// the lifetime annotation on a generated converter's signature — three sites +/// that each kept their own copy until fixed-size arrays added a wire to all of +/// them and only two got updated. +pub(crate) fn is_jni_reference_wire(ty: &syn::Type) -> bool { + let syn::Type::Path(tp) = ty else { + return false; + }; + let Some(last) = tp.path.segments.last() else { + return false; + }; + matches!( + last.ident.to_string().as_str(), + "JObject" + | "JString" + | "JClass" + | "JBooleanArray" + | "JByteArray" + | "JCharArray" + | "JShortArray" + | "JIntArray" + | "JLongArray" + | "JFloatArray" + | "JDoubleArray" + ) +} + pub(crate) fn jni_field_access(jni_type: &syn::Type) -> Option<(&'static str, syn::Ident, bool)> { if let Some(p) = JniPrim::from_wire(jni_type) { return Some((p.descriptor(), format_ident!("{}", p.unbox_getter()), false)); @@ -24,7 +87,17 @@ pub(crate) fn jni_field_access(jni_type: &syn::Type) -> Option<(&'static str, sy }; let sig = match tp.path.segments.last()?.ident.to_string().as_str() { "JString" => "Ljava/lang/String;", + // The eight primitive-array wires, one per `JniPrim` scalar — a + // fixed-size Rust array crosses as the matching Kotlin primitive array + // (see `prim_array`). + "JBooleanArray" => "[Z", "JByteArray" => "[B", + "JCharArray" => "[C", + "JShortArray" => "[S", + "JIntArray" => "[I", + "JLongArray" => "[J", + "JFloatArray" => "[F", + "JDoubleArray" => "[D", _ => return None, }; Some((sig, format_ident!("l"), true)) diff --git a/prebindgen/src/api/lang/jnigen/mod.rs b/prebindgen/src/api/lang/jnigen/mod.rs index 0d881070..60e3d6ea 100644 --- a/prebindgen/src/api/lang/jnigen/mod.rs +++ b/prebindgen/src/api/lang/jnigen/mod.rs @@ -43,8 +43,7 @@ pub use jni::{ decode_byte_array, decode_string, encode_byte_array, encode_string, matching, null_byte_array, null_string, CachedIfaceMethod, ClassDecl, ConstDecl, ConvertDecl, ConvertSourceDecl, DataClassDecl, EnumClassDecl, ExpandDecl, ExpandParamDecl, ExpandReturnDecl, FunctionDecl, - IgnoreDecl, JniBindingError, JniGen, PackageDecl, PtrClassDecl, SealedClassDecl, - ValueClassDecl, VariantDecl, + IgnoreDecl, JniBindingError, JniGen, PackageDecl, PtrClassDecl, SealedClassDecl, VariantDecl, }; // Kotlin emission types now live in the standalone generator module diff --git a/prebindgen/src/lib.rs b/prebindgen/src/lib.rs index 1974be81..74c139f2 100644 --- a/prebindgen/src/lib.rs +++ b/prebindgen/src/lib.rs @@ -136,8 +136,7 @@ //! syntax — the domain vocabulary you compose and hand to [`lang::JniGen`]: //! //! - Kotlin surface: [`package!`](crate::package), [`ptr_class!`](crate::ptr_class), -//! [`data_class!`](crate::data_class), [`value_class!`](crate::value_class), -//! [`enum_class!`](crate::enum_class) +//! [`data_class!`](crate::data_class), [`enum_class!`](crate::enum_class) //! - Members & constants: [`fun!`](crate::fun), [`constant!`](crate::constant) //! - Conversions: [`convert!`](crate::convert), [`from!`](crate::from), //! [`try_from!`](crate::try_from), [`into!`](crate::into), @@ -217,7 +216,7 @@ pub mod __macro_support { /// generic `impl Into` bound doesn't give it anything to unify against. /// /// This is what powers the `lang::jnigen` [`fun!`](crate::fun) decl macro — -/// see that macro (and `ptr_class!`/`enum_class!`/`data_class!`/`value_class!`, +/// see that macro (and `ptr_class!`/`enum_class!`/`data_class!`, /// which apply the same trick to `syn::Type`) for the primary way this /// crate's builders are fed bare Rust names today. /// @@ -324,7 +323,7 @@ pub mod lang { null_byte_array, null_string, CachedIfaceMethod, ClassDecl, ConstDecl, ConvertDecl, ConvertSourceDecl, DataClassDecl, EnumClassDecl, ExpandDecl, ExpandParamDecl, ExpandReturnDecl, FunctionDecl, IgnoreDecl, JniBindingError, JniGen, KotlinFile, - PackageDecl, PtrClassDecl, SealedClassDecl, ValueClassDecl, VariantDecl, WriteKotlinError, + PackageDecl, PtrClassDecl, SealedClassDecl, VariantDecl, WriteKotlinError, }; }