From d06c1724b630d3ec205a0178c10e8caeba11084e Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Fri, 18 Jul 2025 17:30:44 +0200 Subject: [PATCH 1/2] fix boolean naming --- .../Convenience/String+Extensions.swift | 3 +- Sources/JExtractSwiftLib/ImportedDecls.swift | 8 +-- .../JNI/JNIVariablesTests.swift | 56 ++++++++++++++++++- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/Sources/JExtractSwiftLib/Convenience/String+Extensions.swift b/Sources/JExtractSwiftLib/Convenience/String+Extensions.swift index 1878ba2f..1851e154 100644 --- a/Sources/JExtractSwiftLib/Convenience/String+Extensions.swift +++ b/Sources/JExtractSwiftLib/Convenience/String+Extensions.swift @@ -14,8 +14,7 @@ extension String { - // TODO: naive implementation good enough for our simple case `methodMethodSomething` -> `MethodSomething` - var toCamelCase: String { + var firstCharacterUppercased: String { guard let f = first else { return self } diff --git a/Sources/JExtractSwiftLib/ImportedDecls.swift b/Sources/JExtractSwiftLib/ImportedDecls.swift index 4b8a478a..06f17af8 100644 --- a/Sources/JExtractSwiftLib/ImportedDecls.swift +++ b/Sources/JExtractSwiftLib/ImportedDecls.swift @@ -160,15 +160,15 @@ extension ImportedFunc { let returnsBoolean = self.functionSignature.result.type.asNominalTypeDeclaration?.knownTypeKind == .bool if !returnsBoolean { - return "get\(self.name.toCamelCase)" + return "get\(self.name.firstCharacterUppercased)" } else if !self.name.hasJavaBooleanNamingConvention { - return "is\(self.name.toCamelCase)" + return "is\(self.name.firstCharacterUppercased)" } else { - return self.name.toCamelCase + return self.name } } var javaSetterName: String { - "set\(self.name.toCamelCase)" + "set\(self.name.firstCharacterUppercased)" } } diff --git a/Tests/JExtractSwiftTests/JNI/JNIVariablesTests.swift b/Tests/JExtractSwiftTests/JNI/JNIVariablesTests.swift index 3f65bd97..25a87571 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIVariablesTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIVariablesTests.swift @@ -411,7 +411,7 @@ struct JNIVariablesTests { } @Test - func boolean_swiftThunks() throws { + func someBoolean_swiftThunks() throws { try assertOutput( input: membersSource, .jni, @@ -450,4 +450,58 @@ struct JNIVariablesTests { ] ) } + + @Test + func isBoolean_javaBindings() throws { + try assertOutput( + input: membersSource, + .jni, + .java, + detectChunkByInitialLines: 8, + expectedChunks: [ + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public let isBoolean: Bool + * } + */ + public boolean isBoolean() { + long self$ = this.$memoryAddress(); + return MyClass.$isBoolean(self$); + } + """, + """ + private static native boolean $isBoolean(long selfPointer); + """, + ] + ) + } + + @Test + func isBoolean_swiftThunks() throws { + try assertOutput( + input: membersSource, + .jni, + .swift, + detectChunkByInitialLines: 1, + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_MyClass__00024isBoolean__J") + func Java_com_example_swift_MyClass__00024isBoolean__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jboolean { + guard let env$ = environment else { + fatalError("Missing JNIEnv in downcall to \\(#function)") + } + assert(selfPointer != 0, "selfPointer memory address was null") + let selfBits$ = Int(Int64(fromJNI: selfPointer, in: env$)) + guard let self$ = UnsafeMutablePointer(bitPattern: selfBits$) else { + fatalError("self memory address was null in call to \\(#function)!") + } + let result = self$.pointee.isBoolean + return result.getJNIValue(in: environment) + } + """, + ] + ) + } } From 5628cf14106991960ebdf621837a9a59031694bb Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Tue, 22 Jul 2025 09:53:59 +0200 Subject: [PATCH 2/2] update setter name to match spec --- Sources/JExtractSwiftLib/ImportedDecls.swift | 12 ++++++- .../Asserts/TextAssertions.swift | 1 - .../JNI/JNIVariablesTests.swift | 33 +++++++++++++++++-- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/Sources/JExtractSwiftLib/ImportedDecls.swift b/Sources/JExtractSwiftLib/ImportedDecls.swift index 06f17af8..dd7e9c10 100644 --- a/Sources/JExtractSwiftLib/ImportedDecls.swift +++ b/Sources/JExtractSwiftLib/ImportedDecls.swift @@ -169,6 +169,16 @@ extension ImportedFunc { } var javaSetterName: String { - "set\(self.name.firstCharacterUppercased)" + let isBooleanSetter = self.functionSignature.parameters.first?.type.asNominalTypeDeclaration?.knownTypeKind == .bool + + // If the variable is already named "isX", then we make + // the setter "setX" to match beans spec. + if isBooleanSetter && self.name.hasJavaBooleanNamingConvention { + // Safe to force unwrap due to `hasJavaBooleanNamingConvention` check. + let propertyName = self.name.split(separator: "is", maxSplits: 1).last! + return "set\(propertyName)" + } else { + return "set\(self.name.firstCharacterUppercased)" + } } } diff --git a/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift b/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift index 5b70f68a..47397c63 100644 --- a/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift +++ b/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift @@ -115,7 +115,6 @@ func assertOutput( print("==== ---------------------------------------------------------------") #expect(output.contains(expectedChunk), sourceLocation: sourceLocation) - fatalError("Failed: \(filePath):\(line)") continue } diff --git a/Tests/JExtractSwiftTests/JNI/JNIVariablesTests.swift b/Tests/JExtractSwiftTests/JNI/JNIVariablesTests.swift index 25a87571..272d27b5 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIVariablesTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIVariablesTests.swift @@ -33,7 +33,7 @@ struct JNIVariablesTests { set { } } public var someBoolean: Bool - public let isBoolean: Bool + public var isBoolean: Bool } """ @@ -463,7 +463,7 @@ struct JNIVariablesTests { /** * Downcall to Swift: * {@snippet lang=swift : - * public let isBoolean: Bool + * public var isBoolean: Bool * } */ public boolean isBoolean() { @@ -472,8 +472,23 @@ struct JNIVariablesTests { } """, """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public var isBoolean: Bool + * } + */ + public void setBoolean(boolean newValue) { + long self$ = this.$memoryAddress(); + MyClass.$setBoolean(newValue, self$); + } + """, + """ private static native boolean $isBoolean(long selfPointer); """, + """ + private static native void $setBoolean(boolean newValue, long selfPointer); + """ ] ) } @@ -501,6 +516,20 @@ struct JNIVariablesTests { return result.getJNIValue(in: environment) } """, + """ + @_cdecl("Java_com_example_swift_MyClass__00024setBoolean__ZJ") + func Java_com_example_swift_MyClass__00024setBoolean__ZJ(environment: UnsafeMutablePointer!, thisClass: jclass, newValue: jboolean, selfPointer: jlong) { + guard let env$ = environment else { + fatalError("Missing JNIEnv in downcall to \\(#function)") + } + assert(selfPointer != 0, "selfPointer memory address was null") + let selfBits$ = Int(Int64(fromJNI: selfPointer, in: env$)) + guard let self$ = UnsafeMutablePointer(bitPattern: selfBits$) else { + fatalError("self memory address was null in call to \\(#function)!") + } + self$.pointee.isBoolean = Bool(fromJNI: newValue, in: environment!) + } + """ ] ) }