Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions Sources/JExtractSwiftLib/ExtractedDecls+JavaNaming.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,15 @@ extension ExtractedFunc {
}

let returnsBoolean = self.functionSignature.result.type.asNominalTypeDeclaration?.knownTypeKind == .bool
// A property named with backticks, such as `default`, is `getDefault` in Java.
let name = self.name.unescapedSwiftName

if !returnsBoolean {
return "get\(self.name.firstCharacterUppercased)"
} else if !self.name.hasJavaBooleanNamingConvention {
return "is\(self.name.firstCharacterUppercased)"
return "get\(name.firstCharacterUppercased)"
} else if !name.hasJavaBooleanNamingConvention {
return "is\(name.firstCharacterUppercased)"
} else {
return self.name
return name
}
}

Expand All @@ -85,13 +87,14 @@ extension ExtractedFunc {
}

let isBooleanSetter = self.functionSignature.parameters.first?.type.asNominalTypeDeclaration?.knownTypeKind == .bool
let name = self.name.unescapedSwiftName

if isBooleanSetter && self.name.hasJavaBooleanNamingConvention {
if isBooleanSetter && name.hasJavaBooleanNamingConvention {
// Safe to force unwrap due to `hasJavaBooleanNamingConvention` check.
let propertyName = self.name.split(separator: "is", maxSplits: 1).last!
let propertyName = name.split(separator: "is", maxSplits: 1).last!
return "set\(propertyName)"
} else {
return "set\(self.name.firstCharacterUppercased)"
return "set\(name.firstCharacterUppercased)"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ extension JNISwift2JavaGenerator {

private func renderEscapingClosureApplySignature(_ type: SwiftFunctionType) -> String {
let params = type.parameters.enumerated().map { idx, param -> String in
let name = param.parameterName ?? "_\(idx)"
let name = (param.parameterName ?? "_\(idx)").javaSafeIdentifier
let typeName = param.type.description
return "_ \(name): \(typeName)"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ extension JNISwift2JavaGenerator {
) throws -> SyntheticEscapingClosureFunctionType {
let parameterConversions = try functionType.parameters.enumerated().map { idx, param in
try self.translateParameter(
parameterName: param.parameterName ?? "_\(idx)",
parameterName: (param.parameterName ?? "_\(idx)").javaSafeIdentifier,
type: param.type
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ extension JNISwift2JavaGenerator {
let methodName = "" // TODO: Used for closures, replace with better name?

let parameterResults = try enumCase.parameters.enumerated().map { idx, parameter in
let resultName = parameter.name ?? "arg\(idx)"
let resultName = (parameter.name ?? "arg\(idx)").javaSafeIdentifier
let translatedResult = try self.translateResult(
swiftType: parameter.type,
methodName: methodName,
Expand Down Expand Up @@ -226,7 +226,7 @@ extension JNISwift2JavaGenerator {
// Closures.
var funcTypes: [TranslatedFunctionType] = []
for (idx, param) in decl.functionSignature.parameters.enumerated() {
let parameterName = param.parameterName ?? "_\(idx)"
let parameterName = (param.parameterName ?? "_\(idx)").javaSafeIdentifier

switch param.type {
case .function(let funcTy):
Expand Down Expand Up @@ -303,7 +303,7 @@ extension JNISwift2JavaGenerator {
var translatedParams: [TranslatedParameter] = []

for (i, param) in swiftType.parameters.enumerated() {
let paramName = param.parameterName ?? "_\(i)"
let paramName = (param.parameterName ?? "_\(i)").javaSafeIdentifier
translatedParams.append(
try translateParameter(
swiftType: param.type,
Expand Down Expand Up @@ -387,7 +387,7 @@ extension JNISwift2JavaGenerator {
genericRequirements: [SwiftGenericRequirement],
) throws -> [TranslatedParameter] {
try parameters.enumerated().map { idx, param in
let parameterName = param.name ?? "arg\(idx)"
let parameterName = (param.name ?? "arg\(idx)").javaSafeIdentifier
return try translateParameter(
swiftType: param.type,
parameterName: parameterName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ extension JNISwift2JavaGenerator {
// Non-escaping closures use the legacy translation
var parameters = [NativeParameter]()
for (i, parameter) in functionType.parameters.enumerated() {
let closureParamName = parameter.parameterName ?? "_\(i)"
let closureParamName = (parameter.parameterName ?? "_\(i)").javaSafeIdentifier
let closureParameter = try translateClosureParameter(
parameter.type,
parameterName: closureParamName
Expand Down Expand Up @@ -1726,7 +1726,7 @@ extension JNISwift2JavaGenerator {

let fn = syntheticFunction.functionType
let parameterNames = fn.parameters.enumerated().map { idx, param in
param.parameterName ?? "_\(idx)"
(param.parameterName ?? "_\(idx)").javaSafeIdentifier
}
let closureParameters = parameterNames.joined(separator: .comma)
let isVoid = fn.resultType.isVoid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ extension JNISwift2JavaGenerator {
}.joined(separator: .comma)
printer.printBraceBlock("fileprivate func getAs\(enumCase.name)() -> (\(associatedValueTypes))?") { printer in
let params = enumCase.original.parameters.enumerated().map { i, param in
param.name ?? "_\(i)"
(param.name ?? "_\(i)").javaSafeIdentifier
}.joined(separator: .comma)
printer.printIfBlock("case let .\(enumCase.original.name)(\(params)) = self") { printer in
printer.print("return (\(params))")
Expand Down
23 changes: 22 additions & 1 deletion Sources/JExtractSwiftLib/JavaIdentifierFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ package struct JavaIdentifierFactory {
Self.javaOperatorName(decl.name)
}
var methodName = baseName + paramsSuffix(decl, baseName: baseName)
if Self.javaKeywords.contains(methodName) {
if Self.javaKeywords.contains(methodName) || Self.finalObjectMethods.contains(methodName) {
methodName += "_"
}
return methodName
Expand Down Expand Up @@ -167,4 +167,25 @@ package struct JavaIdentifierFactory {
/// literals
"true", "false", "null",
]

/// Final methods of `java.lang.Object`, which a generated method can't declare again.
static let finalObjectMethods: Set<String> = ["getClass", "notify", "notifyAll", "wait"]

/// Swift keywords that can't name a parameter or a local variable without backticks.
static let reservedSwiftKeywords: Set<String> = [
"associatedtype", "class", "deinit", "enum", "extension", "fileprivate", "func", "import", "init", "inout",
"internal", "let", "operator", "precedencegroup", "private", "protocol", "public", "rethrows", "static",
"struct", "subscript", "typealias", "var", "break", "case", "catch", "continue", "default", "defer", "do",
"else", "fallthrough", "for", "guard", "if", "in", "repeat", "return", "switch", "throw", "where", "while",
"Any", "as", "false", "is", "nil", "self", "Self", "super", "throws", "true", "try",
]
}

extension String {
/// This name, made usable as a parameter or local variable in both the generated Java and Swift: a Java or
/// Swift keyword such as `long` or `default` gets a `_` suffix.
package var javaSafeIdentifier: String {
JavaIdentifierFactory.javaKeywords.contains(self) || JavaIdentifierFactory.reservedSwiftKeywords.contains(self)
? self + "_" : self
}
}
10 changes: 5 additions & 5 deletions Tests/JExtractSwiftTests/JNI/JNIJavaKitTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ struct JNIJavaKitTests {
* public func function(javaLong: JavaLong, javaInteger: JavaInteger, int: Int64)
* }
*/
public static void function(java.lang.Long javaLong, java.lang.Integer javaInteger, long int) {
SwiftModule.$function(javaLong, javaInteger, int);
public static void function(java.lang.Long javaLong, java.lang.Integer javaInteger, long int_) {
SwiftModule.$function(javaLong, javaInteger, int_);
}
""",
"""
private static native void $function(java.lang.Long javaLong, java.lang.Integer javaInteger, long int);
private static native void $function(java.lang.Long javaLong, java.lang.Integer javaInteger, long int_);
""",
]
)
Expand All @@ -63,14 +63,14 @@ struct JNIJavaKitTests {
expectedChunks: [
"""
@_cdecl("Java_com_example_swift_SwiftModule__00024function__Ljava_lang_Long_2Ljava_lang_Integer_2J")
public func Java_com_example_swift_SwiftModule__00024function__Ljava_lang_Long_2Ljava_lang_Integer_2J(environment: UnsafeMutablePointer<JNIEnv?>!, thisClass: jclass, javaLong: jobject?, javaInteger: jobject?, int: jlong) {
public func Java_com_example_swift_SwiftModule__00024function__Ljava_lang_Long_2Ljava_lang_Integer_2J(environment: UnsafeMutablePointer<JNIEnv?>!, thisClass: jclass, javaLong: jobject?, javaInteger: jobject?, int_: jlong) {
guard let javaLong_unwrapped$ = javaLong else {
fatalError("javaLong was null in call to \\(#function), but Swift requires non-optional!")
}
guard let javaInteger_unwrapped$ = javaInteger else {
fatalError("javaInteger was null in call to \\(#function), but Swift requires non-optional!")
}
SwiftModule.function(javaLong: JavaLong(javaThis: javaLong_unwrapped$, environment: environment), javaInteger: JavaInteger(javaThis: javaInteger_unwrapped$, environment: environment), int: Int64(fromJNI: int, in: environment))
SwiftModule.function(javaLong: JavaLong(javaThis: javaLong_unwrapped$, environment: environment), javaInteger: JavaInteger(javaThis: javaInteger_unwrapped$, environment: environment), int: Int64(fromJNI: int_, in: environment))
}
"""
]
Expand Down
101 changes: 101 additions & 0 deletions Tests/JExtractSwiftTests/JavaKeywordTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,105 @@ struct JavaKeywordTests {
]
)
}

@Test
func jniParameterNames() throws {
let text =
"""
public struct Side {
public func scale(double: Double, int: Int64, `default`: Bool) -> Double
}
"""

try assertOutput(
input: text,
.jni,
.java,
detectChunkByInitialLines: 1,
expectedChunks: [
"public double scale(double double_, long int_, boolean default_) {",
"return Side.$scale(double_, int_, default_, this.$memoryAddress());",
]
)

try assertOutput(
input: text,
.jni,
.swift,
detectChunkByInitialLines: 1,
expectedChunks: [
"thisClass: jclass, double_: jdouble, int_: jlong, default_: jboolean, selfPointer: jlong) -> jdouble {",
"scale(double: Double(fromJNI: double_, in: environment), int: Int64(fromJNI: int_, in: environment), default: Bool(fromJNI: default_, in: environment))",
]
)
}

@Test
func jniEnumCasePayloadNames() throws {
let text =
"""
public enum Pricing {
case quote(slug: String, long: Int64)
}
"""

try assertOutput(
input: text,
.jni,
.java,
detectChunkByInitialLines: 1,
expectedChunks: [
"record Quote(java.lang.String slug, long long_) implements Case {}"
]
)

try assertOutput(
input: text,
.jni,
.swift,
detectChunkByInitialLines: 1,
expectedChunks: [
"if case let .quote(slug, long_) = self {"
]
)
}

@Test
func backtickedPropertyName() throws {
try assertOutput(
input: """
public struct Side {
public static var `default`: Side
}
""",
.jni,
.java,
detectChunkByInitialLines: 1,
expectedChunks: [
"public static Side getDefault(SwiftArena swiftArena) {"
],
notExpectedChunks: ["get`default`"]
)
}

@Test
func finalObjectMethodNames() throws {
try assertOutput(
input: """
public struct Lock {
public var `class`: String
public func wait()
public func notify(_ count: Int64)
}
""",
.jni,
.java,
detectChunkByInitialLines: 1,
expectedChunks: [
"public java.lang.String getClass_() {",
"public void wait_() {",
"public void notify_(long count) {",
]
)
}
}