Commits on Jul 24, 2026
-
Don't JNI-bridge generic @inline(__always) functions (#260)
* Don't JNI-bridge generic @inline(__always) functions A generic `@inline(__always)` function is emitted as a Kotlin `inline fun <reified T>`, which has no JVM-callable method. The bridge generator nonetheless emitted a `getMethodID(...)!` JNI lookup for it, so the force-unwrap trapped at class load ("Unexpectedly found nil while unwrapping an Optional value") for any native-Swift-on-Android caller. This reproduces whenever the reified method lives on an `open` class that has a subclass (the method is then lifted to a top-level `inline fun <reified T>` extension and bridged via `getStaticMethodID`) — the root cause behind skiptools/skip-firebase#81 and the still-open #91. Return nil from `KotlinFunctionDeclaration.checkBridgable` for a generic `@inline(__always)` function so it is not JNI-bridged. Native-Swift callers use the `@inline(__always)` Swift body directly (inlined); Kotlin callers use the `inline fun <reified T>`. An inline reified function was never JNI-callable to begin with, so nothing is lost. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Also skip bridging SKIP DECLARE'd inline-reified functions A `// SKIP DECLARE: ... inline fun <reified T> ...` override produces the same JVM-uncallable Kotlin as @inline(__always) but carries no structured attribute, so the attribute guard alone left the crash reachable via skip-firebase's FirestoreDecoder.decode(from:) (called from the inlined body of DocumentSnapshot.decoded()) — i.e. #91 was not fully closed. Also bail out of bridging when the declaration override is an inline reified function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
-
Apply spread operator when delegating variadic static funcs to compan…
…ion (#261) A public/internal static function is emitted as an `override` on the type's `companion object` plus a delegating `open fun` on `CompanionClass` that calls back into the concrete implementation. For a variadic parameter the delegating member is declared `vararg`, but `appendCompanionClassDelegatingMember` forwarded the argument by bare name, so inside the delegate the parameter has its Array type and Kotlin rejects the call: Argument type mismatch: actual type is 'kotlin.Array<...>', but 'kotlin.String' was expected. Prefix the forwarded argument with the spread ("*") operator when the parameter is variadic, for both the labeled and unlabeled argument forms. Fixes #64. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
-
Escape invalid characters in file-derived bridge function names (#262)
When a bridged Swift file has no owning type, the generated `@_cdecl` bridging code derives its symbol from the file name (`<FileName>Kt`). The JNI symbol string is escaped via `cdeclEscaped`, but the Swift function name reused the raw file name. A file such as `Model+Bridging.swift` therefore produced an uncompilable Swift declaration: func Model+BridgingKt_Swift_i(...) // '+' is not valid in an identifier Decouple the raw type name (used to compute the JNI symbol) from an escaped one used for the Swift identifier, applying the same JNI-style codepoint escaping (`+` -> `_0002b`) that the symbol side already uses. The generated function becomes `Model_0002bBridgingKt_Swift_i`, matching the escaping of its `@_cdecl` symbol. Fixes #63. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> -
Use Swift-identifier escaping for file-derived bridge function names (#…
…263) Follow-up to #262. That change escaped the Swift `@_cdecl` function name derived from a source file name via `cdeclEscaped`, but `cdeclEscaped` is a JNI-symbol escaper, not a Swift-identifier escaper — the two have different rules, so reusing it leaves two cases wrong: - `.` is preserved by `cdeclEscaped` (it is a separator callers handle), but a `.` is invalid in a Swift identifier. A file such as `Model.generated.swift` still produces an uncompilable declaration: func Model.generatedKt_Swift_i(...) // '.' is not valid - `_` is valid in a Swift identifier but `cdeclEscaped` mangles it to `_1`, so `Model_Extensions.swift` needlessly yields `Model_1ExtensionsKt_Swift_i` on the Swift side. Add a dedicated `swiftIdentifierEscaped` that leaves ASCII alphanumerics and `_` untouched and hex-escapes everything else, and apply it to the Swift function name only; the JNI symbol keeps `cdeclEscaped`. The `+` case from #262 is unchanged (both escapers map `+` to `_0002b`). Also corrects the `cdeclEscaped` doc comment, which claimed `.` -> `_` while the code preserves `.`. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>