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 skiptools#63.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Merged
vincentborko added a commit to vincentborko/skipstone that referenced this pull request
Jul 24, 2026Follow-up to skiptools#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 skiptools#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>
marcprux pushed a commit that referenced this pull request
Jul 24, 2026…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>