diff --git a/Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftClass.swift b/Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftClass.swift index fd0ce488..b4447f4f 100644 --- a/Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftClass.swift +++ b/Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftClass.swift @@ -13,8 +13,8 @@ //===----------------------------------------------------------------------===// public class MySwiftClass { - let x: Int64 - let y: Int64 + public let x: Int64 + public let y: Int64 public let byte: UInt8 = 0 public let constant: Int64 = 100 @@ -76,4 +76,12 @@ public class MySwiftClass { public func throwingFunction() throws { throw MySwiftClassError.swiftError } + + public func sumX(with other: MySwiftClass) -> Int64 { + return self.x + other.x + } + + public func copy() -> MySwiftClass { + return MySwiftClass(x: self.x, y: self.y) + } } diff --git a/Samples/JExtractJNISampleApp/src/test/java/com/example/swift/MySwiftClassTest.java b/Samples/JExtractJNISampleApp/src/test/java/com/example/swift/MySwiftClassTest.java index bf244416..4425d6b2 100644 --- a/Samples/JExtractJNISampleApp/src/test/java/com/example/swift/MySwiftClassTest.java +++ b/Samples/JExtractJNISampleApp/src/test/java/com/example/swift/MySwiftClassTest.java @@ -118,4 +118,25 @@ void isWarm() { assertFalse(c.isWarm()); } } + + @Test + void sumWithX() { + try (var arena = new ConfinedSwiftMemorySession()) { + MySwiftClass c1 = MySwiftClass.init(20, 10, arena); + MySwiftClass c2 = MySwiftClass.init(50, 10, arena); + assertEquals(70, c1.sumX(c2)); + } + } + + @Test + void copy() { + try (var arena = new ConfinedSwiftMemorySession()) { + MySwiftClass c1 = MySwiftClass.init(20, 10, arena); + MySwiftClass c2 = c1.copy(arena); + + assertEquals(20, c2.getX()); + assertEquals(10, c2.getY()); + assertNotEquals(c1.$memoryAddress(), c2.$memoryAddress()); + } + } } \ No newline at end of file diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index 4f4ad4d8..9c3d14b8 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -117,7 +117,7 @@ extension JNISwift2JavaGenerator { printer.println() for initializer in decl.initializers { - printInitializerBindings(&printer, initializer, type: decl) + printFunctionBinding(&printer, initializer) printer.println() } @@ -176,75 +176,82 @@ extension JNISwift2JavaGenerator { } private func printFunctionBinding(_ printer: inout CodePrinter, _ decl: ImportedFunc) { - guard let _ = translatedDecl(for: decl) else { + guard let translatedDecl = translatedDecl(for: decl) else { // Failed to translate. Skip. return } + var modifiers = ["public"] if decl.isStatic || decl.isInitializer || !decl.hasParent { - printStaticFunctionBinding(&printer, decl) - } else { - printMemberMethodBindings(&printer, decl) + modifiers.append("static") } - } - private func printStaticFunctionBinding(_ printer: inout CodePrinter, _ decl: ImportedFunc) { - printDeclDocumentation(&printer, decl) - printer.print( - "public static native \(renderFunctionSignature(decl));" - ) - } + let translatedSignature = translatedDecl.translatedFunctionSignature + let resultType = translatedSignature.resultType.javaType + var parameters = translatedDecl.translatedFunctionSignature.parameters.map(\.parameter.asParameter) + if translatedSignature.requiresSwiftArena { + parameters.append("SwiftArena swiftArena$") + } + let throwsClause = decl.isThrowing ? " throws Exception" : "" - /// Renders Java bindings for member methods - /// - /// Member methods are generated as a function that extracts the `selfPointer` - /// and passes it down to another native function along with the arguments - /// to call the Swift implementation. - private func printMemberMethodBindings(_ printer: inout CodePrinter, _ decl: ImportedFunc) { - let translatedDecl = translatedDecl(for: decl)! // We will only call this method if we can translate the decl. + let modifiersStr = modifiers.joined(separator: " ") + let parametersStr = parameters.joined(separator: ", ") printDeclDocumentation(&printer, decl) - printer.printBraceBlock("public \(renderFunctionSignature(decl))") { printer in - var arguments = translatedDecl.translatedFunctionSignature.parameters.map(\.name) - - let selfVarName = "self$" - arguments.append(selfVarName) + printer.printBraceBlock( + "\(modifiersStr) \(resultType) \(translatedDecl.name)(\(parametersStr))\(throwsClause)" + ) { printer in + printDowncall(&printer, decl) + } - let returnKeyword = translatedDecl.translatedFunctionSignature.resultType.isVoid ? "" : "return " + printNativeFunction(&printer, decl) + } - printer.print( - """ - long \(selfVarName) = this.$memoryAddress(); - \(returnKeyword)\(translatedDecl.parentName).$\(translatedDecl.name)(\(arguments.joined(separator: ", "))); - """ - ) + private func printNativeFunction(_ printer: inout CodePrinter, _ decl: ImportedFunc) { + let translatedDecl = translatedDecl(for: decl)! // Will always call with valid decl + let nativeSignature = translatedDecl.nativeFunctionSignature + let resultType = nativeSignature.result.javaType + var parameters = nativeSignature.parameters + if let selfParameter = nativeSignature.selfParameter { + parameters.append(selfParameter) } + let renderedParameters = parameters.map { "\($0.javaParameter.type) \($0.javaParameter.name)"}.joined(separator: ", ") - let returnType = translatedDecl.translatedFunctionSignature.resultType - var parameters = translatedDecl.translatedFunctionSignature.parameters.map(\.asParameter) - parameters.append("long selfPointer") - printer.print("private static native \(returnType) $\(translatedDecl.name)(\(parameters.joined(separator: ", ")));") + printer.print("private static native \(resultType) \(translatedDecl.nativeFunctionName)(\(renderedParameters));") } - private func printInitializerBindings(_ printer: inout CodePrinter, _ decl: ImportedFunc, type: ImportedNominalType) { - guard let translatedDecl = translatedDecl(for: decl) else { - // Failed to translate. Skip. - return + private func printDowncall( + _ printer: inout CodePrinter, + _ decl: ImportedFunc + ) { + let translatedDecl = translatedDecl(for: decl)! // We will only call this method if we can translate the decl. + let translatedFunctionSignature = translatedDecl.translatedFunctionSignature + + // Regular parameters. + var arguments = [String]() + for parameter in translatedFunctionSignature.parameters { + let lowered = parameter.conversion.render(&printer, parameter.parameter.name) + arguments.append(lowered) } - printDeclDocumentation(&printer, decl) - printer.printBraceBlock("public static \(renderFunctionSignature(decl))") { printer in - let initArguments = translatedDecl.translatedFunctionSignature.parameters.map(\.name) - printer.print( - """ - long self$ = \(type.qualifiedName).allocatingInit(\(initArguments.joined(separator: ", "))); - return new \(type.qualifiedName)(self$, swiftArena$); - """ - ) + // 'self' parameter. + if let selfParameter = translatedFunctionSignature.selfParameter { + let lowered = selfParameter.conversion.render(&printer, "this") + arguments.append(lowered) } - let parameters = translatedDecl.translatedFunctionSignature.parameters.map(\.asParameter) - printer.print("private static native long allocatingInit(\(parameters.joined(separator: ", ")));") + //=== Part 3: Downcall. + // TODO: If we always generate a native method and a "public" method, we can actually choose our own thunk names + // using the registry? + let downcall = "\(translatedDecl.parentName).\(translatedDecl.nativeFunctionName)(\(arguments.joined(separator: ", ")))" + + //=== Part 4: Convert the return value. + if translatedFunctionSignature.resultType.javaType.isVoid { + printer.print("\(downcall);") + } else { + let result = translatedFunctionSignature.resultType.conversion.render(&printer, downcall) + printer.print("return \(result);") + } } private func printDeclDocumentation(_ printer: inout CodePrinter, _ decl: ImportedFunc) { @@ -288,24 +295,4 @@ extension JNISwift2JavaGenerator { ) } } - - /// Renders a Java function signature - /// - /// `func method(x: Int, y: Int) -> Int` becomes - /// `long method(long x, long y)` - private func renderFunctionSignature(_ decl: ImportedFunc) -> String { - guard let translatedDecl = translatedDecl(for: decl) else { - fatalError("Unable to render function signature for a function that cannot be translated: \(decl)") - } - let resultType = translatedDecl.translatedFunctionSignature.resultType - var parameters = translatedDecl.translatedFunctionSignature.parameters.map(\.asParameter) - - if decl.isInitializer { - parameters.append("SwiftArena swiftArena$") - } - - let throwsClause = decl.isThrowing ? " throws Exception" : "" - - return "\(resultType) \(translatedDecl.name)(\(parameters.joined(separator: ", ")))\(throwsClause)" - } } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index 2e3ffff8..e0253624 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -39,7 +39,16 @@ extension JNISwift2JavaGenerator { let swiftModuleName: String func translate(_ decl: ImportedFunc) throws -> TranslatedFunctionDecl { + let nativeTranslation = NativeJavaTranslation() + + // Swift -> Java let translatedFunctionSignature = try translate(functionSignature: decl.functionSignature) + // Java -> Java (native) + let nativeFunctionSignature = try nativeTranslation.translate( + functionSignature: decl.functionSignature, + translatedFunctionSignature: translatedFunctionSignature + ) + // Types with no parent will be outputted inside a "module" class. let parentName = decl.parentType?.asNominalType?.nominalTypeDecl.qualifiedName ?? swiftModuleName @@ -52,68 +61,99 @@ extension JNISwift2JavaGenerator { return TranslatedFunctionDecl( name: javaName, + nativeFunctionName: "$\(javaName)", parentName: parentName, - translatedFunctionSignature: translatedFunctionSignature + translatedFunctionSignature: translatedFunctionSignature, + nativeFunctionSignature: nativeFunctionSignature ) } - func translate(functionSignature: SwiftFunctionSignature, isInitializer: Bool = false) throws -> TranslatedFunctionSignature { + func translate(functionSignature: SwiftFunctionSignature) throws -> TranslatedFunctionSignature { let parameters = try functionSignature.parameters.enumerated().map { idx, param in let parameterName = param.parameterName ?? "arg\(idx))" - return try translate(swiftParam: param, parameterName: parameterName) + return try translateParameter(swiftType: param.type, parameterName: parameterName) + } + + // 'self' + let selfParameter: TranslatedParameter? + if case .instance(let swiftSelf) = functionSignature.selfParameter { + selfParameter = try self.translateParameter( + swiftType: swiftSelf.type, + parameterName: swiftSelf.parameterName ?? "self" + ) + } else { + selfParameter = nil } return try TranslatedFunctionSignature( + selfParameter: selfParameter, parameters: parameters, - resultType: translate(swiftType: functionSignature.result.type) - ) - } - - func translate(swiftParam: SwiftParameter, parameterName: String) throws -> JavaParameter { - return try JavaParameter( - name: parameterName, - type: translate(swiftType: swiftParam.type) + resultType: translate(swiftResult: functionSignature.result) ) } - func translate(swiftType: SwiftType) throws -> JavaType { + func translateParameter(swiftType: SwiftType, parameterName: String) throws -> TranslatedParameter { switch swiftType { case .nominal(let nominalType): if let knownType = nominalType.nominalTypeDecl.knownTypeKind { - guard let javaType = translate(knownType: knownType) else { + guard let javaType = JNISwift2JavaGenerator.translate(knownType: knownType) else { throw JavaTranslationError.unsupportedSwiftType(swiftType) } - return javaType + + return TranslatedParameter( + parameter: JavaParameter(name: parameterName, type: javaType), + conversion: .placeholder + ) } - return .class(package: nil, name: nominalType.nominalTypeDecl.name) + // For now, we assume this is a JExtract class. + return TranslatedParameter( + parameter: JavaParameter( + name: parameterName, + type: .class(package: nil, name: nominalType.nominalTypeDecl.name) + ), + conversion: .valueMemoryAddress(.placeholder) + ) case .tuple([]): - return .void + return TranslatedParameter( + parameter: JavaParameter(name: parameterName, type: .void), + conversion: .placeholder + ) case .metatype, .optional, .tuple, .function, .existential, .opaque: throw JavaTranslationError.unsupportedSwiftType(swiftType) } } - func translate(knownType: SwiftKnownTypeDeclKind) -> JavaType? { - switch knownType { - case .bool: .boolean - case .int8: .byte - case .uint16: .char - case .int16: .short - case .int32: .int - case .int64: .long - case .float: .float - case .double: .double - case .void: .void - case .string: .javaLangString - case .int, .uint, .uint8, .uint32, .uint64, - .unsafeRawPointer, .unsafeMutableRawPointer, - .unsafePointer, .unsafeMutablePointer, - .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, - .unsafeBufferPointer, .unsafeMutableBufferPointer, .optional, .data, .dataProtocol: - nil + func translate( + swiftResult: SwiftResult + ) throws -> TranslatedResult { + switch swiftResult.type { + case .nominal(let nominalType): + if let knownType = nominalType.nominalTypeDecl.knownTypeKind { + guard let javaType = JNISwift2JavaGenerator.translate(knownType: knownType) else { + throw JavaTranslationError.unsupportedSwiftType(swiftResult.type) + } + + return TranslatedResult( + javaType: javaType, + conversion: .placeholder + ) + } + + // For now, we assume this is a JExtract class. + let javaType = JavaType.class(package: nil, name: nominalType.nominalTypeDecl.name) + return TranslatedResult( + javaType: javaType, + conversion: .constructSwiftValue(.placeholder, javaType) + ) + + case .tuple([]): + return TranslatedResult(javaType: .void, conversion: .placeholder) + + case .metatype, .optional, .tuple, .function, .existential, .opaque: + throw JavaTranslationError.unsupportedSwiftType(swiftResult.type) } } } @@ -122,16 +162,106 @@ extension JNISwift2JavaGenerator { /// Java function name let name: String + /// The name of the native function + let nativeFunctionName: String + /// The name of the Java parent scope this function is declared in let parentName: String - /// Function signature + /// Function signature of the Java function the user will call let translatedFunctionSignature: TranslatedFunctionSignature + + /// Function signature of the native function that will be implemented by Swift + let nativeFunctionSignature: NativeFunctionSignature + } + + static func translate(knownType: SwiftKnownTypeDeclKind) -> JavaType? { + switch knownType { + case .bool: .boolean + case .int8: .byte + case .uint16: .char + case .int16: .short + case .int32: .int + case .int64: .long + case .float: .float + case .double: .double + case .void: .void + case .string: .javaLangString + case .int, .uint, .uint8, .uint32, .uint64, + .unsafeRawPointer, .unsafeMutableRawPointer, + .unsafePointer, .unsafeMutablePointer, + .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, + .unsafeBufferPointer, .unsafeMutableBufferPointer, .optional, .data, .dataProtocol: + nil + } } struct TranslatedFunctionSignature { - let parameters: [JavaParameter] - let resultType: JavaType + let selfParameter: TranslatedParameter? + let parameters: [TranslatedParameter] + let resultType: TranslatedResult + + var requiresSwiftArena: Bool { + return self.resultType.conversion.requiresSwiftArena + } + } + + /// Represent a Swift API parameter translated to Java. + struct TranslatedParameter { + let parameter: JavaParameter + let conversion: JavaNativeConversionStep + } + + /// Represent a Swift API result translated to Java. + struct TranslatedResult { + let javaType: JavaType + + /// Represents how to convert the Java native result into a user-facing result. + let conversion: JavaNativeConversionStep + } + + /// Describes how to convert values between Java types and the native Java function + enum JavaNativeConversionStep { + /// The value being converted + case placeholder + + /// `value.$memoryAddress()` + indirect case valueMemoryAddress(JavaNativeConversionStep) + + /// Call `new \(Type)(\(placeholder), swiftArena$)` + indirect case constructSwiftValue(JavaNativeConversionStep, JavaType) + + /// Returns the conversion string applied to the placeholder. + func render(_ printer: inout CodePrinter, _ placeholder: String) -> String { + // NOTE: 'printer' is used if the conversion wants to cause side-effects. + // E.g. storing a temporary values into a variable. + switch self { + case .placeholder: + return placeholder + + case .valueMemoryAddress: + return "\(placeholder).$memoryAddress()" + + case .constructSwiftValue(let inner, let javaType): + let inner = inner.render(&printer, placeholder) + return "new \(javaType.className!)(\(inner), swiftArena$)" + + } + } + + /// Whether the conversion uses SwiftArena. + var requiresSwiftArena: Bool { + switch self { + case .placeholder: + return false + + case .constructSwiftValue: + return true + + case .valueMemoryAddress(let inner): + return inner.requiresSwiftArena + } + } } enum JavaTranslationError: Error { diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift new file mode 100644 index 00000000..461c7301 --- /dev/null +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift @@ -0,0 +1,209 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import JavaTypes + +extension JNISwift2JavaGenerator { + + struct NativeJavaTranslation { + /// Translates a Swift function into the native JNI method signature. + func translate( + functionSignature: SwiftFunctionSignature, + translatedFunctionSignature: TranslatedFunctionSignature + ) throws -> NativeFunctionSignature { + let parameters = try zip(translatedFunctionSignature.parameters, functionSignature.parameters).map { translatedParameter, swiftParameter in + let parameterName = translatedParameter.parameter.name + return try translate(swiftParameter: swiftParameter, parameterName: parameterName) + } + + // Lower the self parameter. + let nativeSelf: NativeParameter? = switch functionSignature.selfParameter { + case .instance(let selfParameter): + try translate( + swiftParameter: selfParameter, + parameterName: selfParameter.parameterName ?? "self" + ) + case nil, .initializer(_), .staticMethod(_): + nil + } + + return try NativeFunctionSignature( + selfParameter: nativeSelf, + parameters: parameters, + result: translate(swiftResult: functionSignature.result) + ) + } + + func translate( + swiftParameter: SwiftParameter, + parameterName: String + ) throws -> NativeParameter { + switch swiftParameter.type { + case .nominal(let nominalType): + if let knownType = nominalType.nominalTypeDecl.knownTypeKind { + guard let javaType = JNISwift2JavaGenerator.translate(knownType: knownType), javaType.implementsJavaValue else { + throw JavaTranslationError.unsupportedSwiftType(swiftParameter.type) + } + + return NativeParameter( + javaParameter: JavaParameter(name: parameterName, type: javaType), + conversion: .initFromJNI(.placeholder, swiftType: swiftParameter.type) + ) + } + + case .tuple([]): + return NativeParameter( + javaParameter: JavaParameter(name: parameterName, type: .void), + conversion: .placeholder + ) + + case .metatype, .optional, .tuple, .function, .existential, .opaque: + throw JavaTranslationError.unsupportedSwiftType(swiftParameter.type) + } + + // Classes are passed as the pointer. + return NativeParameter( + javaParameter: JavaParameter(name: parameterName, type: .long), + conversion: .pointee(.extractSwiftValue(.placeholder, swiftType: swiftParameter.type)) + ) + } + + func translate( + swiftResult: SwiftResult + ) throws -> NativeResult { + switch swiftResult.type { + case .nominal(let nominalType): + if let knownType = nominalType.nominalTypeDecl.knownTypeKind { + guard let javaType = JNISwift2JavaGenerator.translate(knownType: knownType), javaType.implementsJavaValue else { + throw JavaTranslationError.unsupportedSwiftType(swiftResult.type) + } + + return NativeResult( + javaType: javaType, + conversion: .getJNIValue(.placeholder) + ) + } + + case .tuple([]): + return NativeResult( + javaType: .void, + conversion: .placeholder + ) + + case .metatype, .optional, .tuple, .function, .existential, .opaque: + throw JavaTranslationError.unsupportedSwiftType(swiftResult.type) + } + + // TODO: Handle other classes, for example from JavaKit macros. + // for now we assume all passed in classes are JExtract generated + // so we pass the pointer. + return NativeResult( + javaType: .long, + conversion: .getJNIValue(.allocateSwiftValue(name: "result", swiftType: swiftResult.type)) + ) + } + } + + struct NativeFunctionSignature { + let selfParameter: NativeParameter? + let parameters: [NativeParameter] + let result: NativeResult + } + + struct NativeParameter { + let javaParameter: JavaParameter + + var jniType: JNIType { + javaParameter.type.jniType + } + + /// Represents how to convert the JNI parameter to a Swift parameter + let conversion: NativeSwiftConversionStep + } + + struct NativeResult { + let javaType: JavaType + let conversion: NativeSwiftConversionStep + } + + /// Describes how to convert values between Java types and Swift through JNI + enum NativeSwiftConversionStep { + /// The value being converted + case placeholder + + /// `value.getJNIValue(in:)` + indirect case getJNIValue(NativeSwiftConversionStep) + + /// `SwiftType(from: value, in: environment!)` + indirect case initFromJNI(NativeSwiftConversionStep, swiftType: SwiftType) + + /// Extracts a swift type at a pointer given by a long. + indirect case extractSwiftValue(NativeSwiftConversionStep, swiftType: SwiftType) + + /// Allocate memory for a Swift value and outputs the pointer + indirect case allocateSwiftValue(name: String, swiftType: SwiftType) + + /// The thing to which the pointer typed, which is the `pointee` property + /// of the `Unsafe(Mutable)Pointer` types in Swift. + indirect case pointee(NativeSwiftConversionStep) + + + /// Returns the conversion string applied to the placeholder. + func render(_ printer: inout CodePrinter, _ placeholder: String) -> String { + // NOTE: 'printer' is used if the conversion wants to cause side-effects. + // E.g. storing a temporary values into a variable. + switch self { + case .placeholder: + return placeholder + + case .getJNIValue(let inner): + let inner = inner.render(&printer, placeholder) + return "\(inner).getJNIValue(in: environment!)" + + case .initFromJNI(let inner, let swiftType): + let inner = inner.render(&printer, placeholder) + return "\(swiftType)(fromJNI: \(inner), in: environment!)" + + case .extractSwiftValue(let inner, let swiftType): + let inner = inner.render(&printer, placeholder) + printer.print( + """ + assert(\(inner) != 0, "\(inner) memory address was null") + let \(inner)Bits$ = Int(Int64(fromJNI: \(inner), in: environment!)) + guard let \(inner)$ = UnsafeMutablePointer<\(swiftType)>(bitPattern: \(inner)Bits$) else { + fatalError("\(inner) memory address was null in call to \\(#function)!") + } + """ + ) + return "\(inner)$" + + case .allocateSwiftValue(let name, let swiftType): + let pointerName = "\(name)$" + let bitsName = "\(name)Bits$" + printer.print( + """ + let \(pointerName) = UnsafeMutablePointer<\(swiftType)>.allocate(capacity: 1) + \(pointerName).initialize(to: \(placeholder)) + let \(bitsName) = Int64(Int(bitPattern: \(pointerName))) + """ + ) + return bitsName + + case .pointee(let inner): + let inner = inner.render(&printer, placeholder) + return "\(inner).pointee" + } + } + } +} diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index 38d4ff79..d8b6b275 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -98,7 +98,7 @@ extension JNISwift2JavaGenerator { printHeader(&printer) for initializer in type.initializers { - printInitializerThunk(&printer, initializer) + printSwiftFunctionThunk(&printer, initializer) printer.println() } @@ -115,141 +115,105 @@ extension JNISwift2JavaGenerator { printDestroyFunctionThunk(&printer, type) } - private func printInitializerThunk(_ printer: inout CodePrinter, _ decl: ImportedFunc) { - guard let translatedDecl = translatedDecl(for: decl) else { - // Failed to translate. Skip. - return - } - - let typeName = translatedDecl.parentName - - printCDecl( - &printer, - javaMethodName: "allocatingInit", - parentName: translatedDecl.parentName, - parameters: translatedDecl.translatedFunctionSignature.parameters, - isStatic: true, - resultType: .long - ) { printer in - let downcallArguments = renderDowncallArguments( - swiftFunctionSignature: decl.functionSignature, - translatedFunctionSignature: translatedDecl.translatedFunctionSignature - ) - // TODO: Throwing initializers - printer.print( - """ - let self$ = UnsafeMutablePointer<\(typeName)>.allocate(capacity: 1) - self$.initialize(to: \(typeName)(\(downcallArguments))) - return Int64(Int(bitPattern: self$)).getJNIValue(in: environment) - """ - ) - } - } - private func printSwiftFunctionThunk( _ printer: inout CodePrinter, _ decl: ImportedFunc ) { - guard let _ = translatedDecl(for: decl) else { + guard let translatedDecl = translatedDecl(for: decl) else { // Failed to translate. Skip. return } - // Free functions does not have a parent - if decl.isStatic || !decl.hasParent { - self.printSwiftStaticFunctionThunk(&printer, decl) - } else { - self.printSwiftMemberFunctionThunk(&printer, decl) - } - } + let nativeSignature = translatedDecl.nativeFunctionSignature + var parameters = nativeSignature.parameters - private func printSwiftStaticFunctionThunk(_ printer: inout CodePrinter, _ decl: ImportedFunc) { - let translatedDecl = self.translatedDecl(for: decl)! // We will only call this method if we can translate the decl. - - printCDecl( - &printer, - javaMethodName: translatedDecl.name, - parentName: translatedDecl.parentName, - parameters: translatedDecl.translatedFunctionSignature.parameters, - isStatic: true, - resultType: translatedDecl.translatedFunctionSignature.resultType - ) { printer in - // For free functions the parent is the Swift module - let parentName = decl.parentType?.asNominalTypeDeclaration?.qualifiedName ?? swiftModuleName - self.printFunctionDowncall(&printer, decl, calleeName: parentName) + if let selfParameter = nativeSignature.selfParameter { + parameters.append(selfParameter) } - } - - private func printSwiftMemberFunctionThunk(_ printer: inout CodePrinter, _ decl: ImportedFunc) { - let translatedDecl = self.translatedDecl(for: decl)! // We will only call this method if can translate the decl. - let swiftParentName = decl.parentType!.asNominalTypeDeclaration!.qualifiedName - let selfPointerParam = JavaParameter(name: "selfPointer", type: .long) printCDecl( &printer, - javaMethodName: "$\(translatedDecl.name)", + javaMethodName: translatedDecl.nativeFunctionName, parentName: translatedDecl.parentName, - parameters: translatedDecl.translatedFunctionSignature.parameters + [ - selfPointerParam - ], - isStatic: true, - resultType: translatedDecl.translatedFunctionSignature.resultType + parameters: parameters.map(\.javaParameter), + resultType: nativeSignature.result.javaType.jniType ) { printer in - let selfVar = self.printSelfJLongToUnsafeMutablePointer(&printer, - swiftParentName: swiftParentName, selfPointerParam) - self.printFunctionDowncall(&printer, decl, calleeName: "\(selfVar).pointee") + self.printFunctionDowncall(&printer, decl) } } private func printFunctionDowncall( _ printer: inout CodePrinter, - _ decl: ImportedFunc, - calleeName: String + _ decl: ImportedFunc ) { guard let translatedDecl = self.translatedDecl(for: decl) else { fatalError("Cannot print function downcall for a function that can't be translated: \(decl)") } - let swiftReturnType = decl.functionSignature.result.type + let nativeSignature = translatedDecl.nativeFunctionSignature let tryClause: String = decl.isThrowing ? "try " : "" + // Regular parameters. + var arguments = [String]() + for parameter in nativeSignature.parameters { + let lowered = parameter.conversion.render(&printer, parameter.javaParameter.name) + arguments.append(lowered) + } + + // Callee + let callee: String = switch decl.functionSignature.selfParameter { + case .instance(let swiftSelf): + nativeSignature.selfParameter!.conversion.render( + &printer, + swiftSelf.parameterName ?? "self" + ) + case .staticMethod(let selfType), .initializer(let selfType): + "\(selfType)" + case .none: + swiftModuleName + } + + // Build the result let result: String switch decl.apiKind { case .function, .initializer: - let downcallParameters = renderDowncallArguments( - swiftFunctionSignature: decl.functionSignature, - translatedFunctionSignature: translatedDecl.translatedFunctionSignature - ) - result = "\(tryClause)\(calleeName).\(decl.name)(\(downcallParameters))" + let downcallArguments = zip( + decl.functionSignature.parameters, + arguments + ).map { originalParam, argument in + let label = originalParam.argumentLabel.map { "\($0): " } ?? "" + return "\(label)\(argument)" + } + .joined(separator: ", ") + result = "\(tryClause)\(callee).\(decl.name)(\(downcallArguments))" case .getter: - result = "\(tryClause)\(calleeName).\(decl.name)" + result = "\(tryClause)\(callee).\(decl.name)" case .setter: - guard let newValueParameter = decl.functionSignature.parameters.first else { + guard let newValueArgument = arguments.first else { fatalError("Setter did not contain newValue parameter: \(decl)") } - result = "\(calleeName).\(decl.name) = \(renderJNIToSwiftConversion("newValue", type: newValueParameter.type))" + result = "\(callee).\(decl.name) = \(newValueArgument)" } - let returnStatement = - if swiftReturnType.isVoid { - result + // Lower the result. + let innerBody: String + if !decl.functionSignature.result.type.isVoid { + let loweredResult = nativeSignature.result.conversion.render(&printer, result) + innerBody = "return \(loweredResult)" } else { - """ - let result = \(result) - return result.getJNIValue(in: environment) - """ + innerBody = result } if decl.isThrowing { - let dummyReturn = - !swiftReturnType.isVoid ? "return \(swiftReturnType).jniPlaceholderValue" : "" + // TODO: Handle classes for dummy value + let dummyReturn = !nativeSignature.result.javaType.isVoid ? "return \(decl.functionSignature.result.type).jniPlaceholderValue" : "" printer.print( """ do { - \(returnStatement) + \(innerBody) } catch { environment.throwAsException(error) \(dummyReturn) @@ -257,7 +221,7 @@ extension JNISwift2JavaGenerator { """ ) } else { - printer.print(returnStatement) + printer.print(innerBody) } } @@ -266,8 +230,7 @@ extension JNISwift2JavaGenerator { javaMethodName: String, parentName: String, parameters: [JavaParameter], - isStatic: Bool, - resultType: JavaType, + resultType: JNIType, _ body: (inout CodePrinter) -> Void ) { let jniSignature = parameters.reduce(into: "") { signature, parameter in @@ -283,16 +246,15 @@ extension JNISwift2JavaGenerator { + jniSignature.escapedJNIIdentifier let translatedParameters = parameters.map { - "\($0.name): \($0.type.jniTypeName)" + "\($0.name): \($0.type.jniType)" } - let thisParameter = isStatic ? "thisClass: jclass" : "thisObject: jobject" let thunkParameters = [ "environment: UnsafeMutablePointer!", - thisParameter + "thisClass: jclass" ] + translatedParameters - let thunkReturnType = !resultType.isVoid ? " -> \(resultType.jniTypeName)" : "" + let thunkReturnType = resultType != .void ? " -> \(resultType)" : "" // TODO: Think about function overloads printer.printBraceBlock( @@ -326,7 +288,6 @@ extension JNISwift2JavaGenerator { parameters: [ selfPointerParam ], - isStatic: true, resultType: .void ) { printer in let parentName = type.qualifiedName @@ -347,7 +308,9 @@ extension JNISwift2JavaGenerator { /// - Returns: name of the created "self" variable private func printSelfJLongToUnsafeMutablePointer( _ printer: inout CodePrinter, - swiftParentName: String, _ selfPointerParam: JavaParameter) -> String { + swiftParentName: String, + _ selfPointerParam: JavaParameter + ) -> String { let newSelfParamName = "self$" printer.print( """ @@ -363,26 +326,6 @@ extension JNISwift2JavaGenerator { ) return newSelfParamName } - - - /// Renders the arguments for making a downcall - private func renderDowncallArguments( - swiftFunctionSignature: SwiftFunctionSignature, - translatedFunctionSignature: TranslatedFunctionSignature - ) -> String { - zip( - swiftFunctionSignature.parameters, - translatedFunctionSignature.parameters - ).map { originalParam, translatedParam in - let label = originalParam.argumentLabel.map { "\($0): " } ?? "" - return "\(label)\(originalParam.type)(fromJNI: \(translatedParam.name), in: environment!)" - } - .joined(separator: ", ") - } - - private func renderJNIToSwiftConversion(_ variableName: String, type: SwiftType) -> String { - "\(type)(fromJNI: \(variableName), in: environment!)" - } } extension String { diff --git a/Sources/JExtractSwiftLib/JNI/JNIType.swift b/Sources/JExtractSwiftLib/JNI/JNIType.swift new file mode 100644 index 00000000..d152942a --- /dev/null +++ b/Sources/JExtractSwiftLib/JNI/JNIType.swift @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import JavaTypes + +/// Represents types that are able to be passed over a JNI boundary. +/// +/// - SeeAlso: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html +enum JNIType { + case jboolean + case jfloat + case jdouble + case jbyte + case jchar + case jshort + case jint + case jlong + case void + case jstring + case jclass + case jthrowable + case jobject + case jbooleanArray + case jbyteArray + case jcharArray + case jshortArray + case jintArray + case jlongArray + case jfloatArray + case jdoubleArray + case jobjectArray +} + +extension JavaType { + var jniType: JNIType { + switch self { + case .boolean: .jboolean + case .byte: .jbyte + case .char: .jchar + case .short: .jshort + case .int: .jint + case .long: .jlong + case .float: .jfloat + case .double: .jdouble + case .void: .void + case .array(.boolean): .jbooleanArray + case .array(.byte): .jbyteArray + case .array(.char): .jcharArray + case .array(.short): .jshortArray + case .array(.int): .jintArray + case .array(.long): .jlongArray + case .array(.float): .jfloatArray + case .array(.double): .jdoubleArray + case .array: .jobjectArray + case .javaLangString: .jstring + case .javaLangClass: .jclass + case .javaLangThrowable: .jthrowable + case .class: .jobject + } + } + + /// Returns whether this type returns `JavaValue` from JavaKit + var implementsJavaValue: Bool { + switch self { + case .boolean, .byte, .char, .short, .int, .long, .float, .double, .void, .javaLangString: + true + default: + false + } + } +} diff --git a/Sources/JExtractSwiftLib/JavaConstants/JavaTypes.swift b/Sources/JExtractSwiftLib/JavaConstants/JavaTypes.swift index 633943ef..02850801 100644 --- a/Sources/JExtractSwiftLib/JavaConstants/JavaTypes.swift +++ b/Sources/JExtractSwiftLib/JavaConstants/JavaTypes.swift @@ -29,4 +29,14 @@ extension JavaType { static var javaLangRunnable: JavaType { .class(package: "java.lang", name: "Runnable") } + + /// The description of the type java.lang.Class. + static var javaLangClass: JavaType { + .class(package: "java.lang", name: "Class") + } + + /// The description of the type java.lang.Throwable. + static var javaLangThrowable: JavaType { + .class(package: "java.lang", name: "Throwable") + } } diff --git a/Tests/JExtractSwiftTests/JNI/JNIClassTests.swift b/Tests/JExtractSwiftTests/JNI/JNIClassTests.swift index 5970e7a0..cfe78fbd 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIClassTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIClassTests.swift @@ -35,6 +35,9 @@ struct JNIClassTests { } public func doSomething(x: Int64) {} + + public func copy() -> MyClass {} + public func isEqual(to other: MyClass) -> Bool {} } """ @@ -103,7 +106,12 @@ struct JNIClassTests { * public static func method() * } */ - public static native void method(); + public static void method() { + MyClass.$method(); + } + """, + """ + private static native void $method(); """ ] ) @@ -118,8 +126,8 @@ struct JNIClassTests { detectChunkByInitialLines: 1, expectedChunks: [ """ - @_cdecl("Java_com_example_swift_MyClass_method__") - func Java_com_example_swift_MyClass_method__(environment: UnsafeMutablePointer!, thisClass: jclass) { + @_cdecl("Java_com_example_swift_MyClass__00024method__") + func Java_com_example_swift_MyClass__00024method__(environment: UnsafeMutablePointer!, thisClass: jclass) { MyClass.method() } """ @@ -142,8 +150,7 @@ struct JNIClassTests { * } */ public static MyClass init(long x, long y, SwiftArena swiftArena$) { - long self$ = MyClass.allocatingInit(x, y); - return new MyClass(self$, swiftArena$); + return new MyClass(MyClass.$init(x, y), swiftArena$); } """, """ @@ -154,15 +161,14 @@ struct JNIClassTests { * } */ public static MyClass init(SwiftArena swiftArena$) { - long self$ = MyClass.allocatingInit(); - return new MyClass(self$, swiftArena$); + return new MyClass(MyClass.$init(), swiftArena$); } """, """ - private static native long allocatingInit(long x, long y); + private static native long $init(long x, long y); """, """ - private static native long allocatingInit(); + private static native long $init(); """ ] ) @@ -177,19 +183,21 @@ struct JNIClassTests { detectChunkByInitialLines: 1, expectedChunks: [ """ - @_cdecl("Java_com_example_swift_MyClass_allocatingInit__") - func Java_com_example_swift_MyClass_allocatingInit__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { - let self$ = UnsafeMutablePointer.allocate(capacity: 1) - self$.initialize(to: MyClass()) - return Int64(Int(bitPattern: self$)).getJNIValue(in: environment) + @_cdecl("Java_com_example_swift_MyClass__00024init__JJ") + func Java_com_example_swift_MyClass__00024init__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, x: jlong, y: jlong) -> jlong { + let result$ = UnsafeMutablePointer.allocate(capacity: 1) + result$.initialize(to: MyClass.init(x: Int64(fromJNI: x, in: environment!), y: Int64(fromJNI: y, in: environment!))) + let resultBits$ = Int64(Int(bitPattern: result$)) + return resultBits$.getJNIValue(in: environment!) } """, """ - @_cdecl("Java_com_example_swift_MyClass_allocatingInit__JJ") - func Java_com_example_swift_MyClass_allocatingInit__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, x: jlong, y: jlong) -> jlong { - let self$ = UnsafeMutablePointer.allocate(capacity: 1) - self$.initialize(to: MyClass(x: Int64(fromJNI: x, in: environment!), y: Int64(fromJNI: y, in: environment!))) - return Int64(Int(bitPattern: self$)).getJNIValue(in: environment) + @_cdecl("Java_com_example_swift_MyClass__00024init__") + func Java_com_example_swift_MyClass__00024init__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { + let result$ = UnsafeMutablePointer.allocate(capacity: 1) + result$.initialize(to: MyClass.init()) + let resultBits$ = Int64(Int(bitPattern: result$)) + return resultBits$.getJNIValue(in: environment!) } """ ] @@ -238,12 +246,11 @@ struct JNIClassTests { * } */ public void doSomething(long x) { - long self$ = this.$memoryAddress(); - MyClass.$doSomething(x, self$); + MyClass.$doSomething(x, this.$memoryAddress()); } """, """ - private static native void $doSomething(long x, long selfPointer); + private static native void $doSomething(long x, long self); """ ] ) @@ -259,12 +266,9 @@ struct JNIClassTests { expectedChunks: [ """ @_cdecl("Java_com_example_swift_MyClass__00024doSomething__JJ") - func Java_com_example_swift_MyClass__00024doSomething__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, x: jlong, 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$)) + func Java_com_example_swift_MyClass__00024doSomething__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, x: jlong, self: jlong) { + assert(self != 0, "self memory address was null") + let selfBits$ = Int(Int64(fromJNI: self, in: environment!)) guard let self$ = UnsafeMutablePointer(bitPattern: selfBits$) else { fatalError("self memory address was null in call to \\(#function)!") } @@ -274,4 +278,108 @@ struct JNIClassTests { ] ) } + + @Test + func methodReturningClass_javaBindings() throws { + try assertOutput( + input: source, + .jni, + .java, + expectedChunks: [ + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func copy() -> MyClass + * } + */ + public MyClass copy(SwiftArena swiftArena$) { + return new MyClass(MyClass.$copy(this.$memoryAddress()), swiftArena$); + } + """, + """ + private static native long $copy(long self); + """ + ] + ) + } + + @Test + func methodReturningClass_swiftThunks() throws { + try assertOutput( + input: source, + .jni, + .swift, + detectChunkByInitialLines: 1, + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_MyClass__00024copy__J") + func Java_com_example_swift_MyClass__00024copy__J(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jlong { + assert(self != 0, "self memory address was null") + let selfBits$ = Int(Int64(fromJNI: self, in: environment!)) + guard let self$ = UnsafeMutablePointer(bitPattern: selfBits$) else { + fatalError("self memory address was null in call to \\(#function)!") + } + let result$ = UnsafeMutablePointer.allocate(capacity: 1) + result$.initialize(to: self$.pointee.copy()) + let resultBits$ = Int64(Int(bitPattern: result$)) + return resultBits$.getJNIValue(in: environment!) + } + """, + ] + ) + } + + @Test + func classAsParameter_javaBindings() throws { + try assertOutput( + input: source, + .jni, + .java, + expectedChunks: [ + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func isEqual(to other: MyClass) -> Bool + * } + */ + public boolean isEqual(MyClass other) { + return MyClass.$isEqual(other.$memoryAddress(), this.$memoryAddress()); + } + """, + """ + private static native boolean $isEqual(long other, long self); + """ + ] + ) + } + + @Test + func classAsParameter_swiftThunks() throws { + try assertOutput( + input: source, + .jni, + .swift, + detectChunkByInitialLines: 1, + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_MyClass__00024isEqual__JJ") + func Java_com_example_swift_MyClass__00024isEqual__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, other: jlong, self: jlong) -> jboolean { + assert(other != 0, "other memory address was null") + let otherBits$ = Int(Int64(fromJNI: other, in: environment!)) + guard let other$ = UnsafeMutablePointer(bitPattern: otherBits$) else { + fatalError("other memory address was null in call to \\(#function)!") + } + assert(self != 0, "self memory address was null") + let selfBits$ = Int(Int64(fromJNI: self, in: environment!)) + guard let self$ = UnsafeMutablePointer(bitPattern: selfBits$) else { + fatalError("self memory address was null in call to \\(#function)!") + } + return self$.pointee.isEqual(to: other$.pointee).getJNIValue(in: environment!) + } + """, + ] + ) + } } diff --git a/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift b/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift index d6a030a8..4696253c 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift @@ -67,7 +67,12 @@ struct JNIModuleTests { * public func helloWorld() * } */ - public static native void helloWorld(); + public static void helloWorld() { + SwiftModule.$helloWorld(); + } + """, + """ + private static native void $helloWorld(); """, """ /** @@ -76,7 +81,12 @@ struct JNIModuleTests { * public func takeIntegers(i1: Int8, i2: Int16, i3: Int32, i4: Int64) -> UInt16 * } */ - public static native char takeIntegers(byte i1, short i2, int i3, long i4); + public static char takeIntegers(byte i1, short i2, int i3, long i4) { + return SwiftModule.$takeIntegers(i1, i2, i3, i4); + } + """, + """ + private static native char $takeIntegers(byte i1, short i2, int i3, long i4); """, """ /** @@ -85,7 +95,12 @@ struct JNIModuleTests { * public func otherPrimitives(b: Bool, f: Float, d: Double) * } */ - public static native void otherPrimitives(boolean b, float f, double d); + public static void otherPrimitives(boolean b, float f, double d) { + SwiftModule.$otherPrimitives(b, f, d); + } + """, + """ + private static native void $otherPrimitives(boolean b, float f, double d); """ ] ) @@ -100,21 +115,20 @@ struct JNIModuleTests { detectChunkByInitialLines: 1, expectedChunks: [ """ - @_cdecl("Java_com_example_swift_SwiftModule_helloWorld__") - func Java_com_example_swift_SwiftModule_helloWorld__(environment: UnsafeMutablePointer!, thisClass: jclass) { + @_cdecl("Java_com_example_swift_SwiftModule__00024helloWorld__") + func Java_com_example_swift_SwiftModule__00024helloWorld__(environment: UnsafeMutablePointer!, thisClass: jclass) { SwiftModule.helloWorld() } """, """ - @_cdecl("Java_com_example_swift_SwiftModule_takeIntegers__BSIJ") - func Java_com_example_swift_SwiftModule_takeIntegers__BSIJ(environment: UnsafeMutablePointer!, thisClass: jclass, i1: jbyte, i2: jshort, i3: jint, i4: jlong) -> jchar { - let result = SwiftModule.takeIntegers(i1: Int8(fromJNI: i1, in: environment!), i2: Int16(fromJNI: i2, in: environment!), i3: Int32(fromJNI: i3, in: environment!), i4: Int64(fromJNI: i4, in: environment!)) - return result.getJNIValue(in: environment) - } + @_cdecl("Java_com_example_swift_SwiftModule__00024takeIntegers__BSIJ") + func Java_com_example_swift_SwiftModule__00024takeIntegers__BSIJ(environment: UnsafeMutablePointer!, thisClass: jclass, i1: jbyte, i2: jshort, i3: jint, i4: jlong) -> jchar { + return SwiftModule.takeIntegers(i1: Int8(fromJNI: i1, in: environment!), i2: Int16(fromJNI: i2, in: environment!), i3: Int32(fromJNI: i3, in: environment!), i4: Int64(fromJNI: i4, in: environment!)).getJNIValue(in: environment!) + } """, """ - @_cdecl("Java_com_example_swift_SwiftModule_otherPrimitives__ZFD") - func Java_com_example_swift_SwiftModule_otherPrimitives__ZFD(environment: UnsafeMutablePointer!, thisClass: jclass, b: jboolean, f: jfloat, d: jdouble) { + @_cdecl("Java_com_example_swift_SwiftModule__00024otherPrimitives__ZFD") + func Java_com_example_swift_SwiftModule__00024otherPrimitives__ZFD(environment: UnsafeMutablePointer!, thisClass: jclass, b: jboolean, f: jfloat, d: jdouble) { SwiftModule.otherPrimitives(b: Bool(fromJNI: b, in: environment!), f: Float(fromJNI: f, in: environment!), d: Double(fromJNI: d, in: environment!)) } """ @@ -136,8 +150,13 @@ struct JNIModuleTests { * public func copy(_ string: String) -> String * } */ - public static native java.lang.String copy(java.lang.String string); + public static java.lang.String copy(java.lang.String string) { + return SwiftModule.$copy(string); + } """, + """ + private static native java.lang.String $copy(java.lang.String string); + """ ] ) } @@ -151,10 +170,9 @@ struct JNIModuleTests { detectChunkByInitialLines: 1, expectedChunks: [ """ - @_cdecl("Java_com_example_swift_SwiftModule_copy__Ljava_lang_String_2") - func Java_com_example_swift_SwiftModule_copy__Ljava_lang_String_2(environment: UnsafeMutablePointer!, thisClass: jclass, string: jstring?) -> jstring? { - let result = SwiftModule.copy(String(fromJNI: string, in: environment!)) - return result.getJNIValue(in: environment) + @_cdecl("Java_com_example_swift_SwiftModule__00024copy__Ljava_lang_String_2") + func Java_com_example_swift_SwiftModule__00024copy__Ljava_lang_String_2(environment: UnsafeMutablePointer!, thisClass: jclass, string: jstring) -> jstring { + return SwiftModule.copy(String(fromJNI: string, in: environment!)).getJNIValue(in: environment!) } """, ] @@ -175,7 +193,12 @@ struct JNIModuleTests { * public func methodA() throws * } */ - public static native void methodA() throws Exception; + public static void methodA() throws Exception { + SwiftModule.$methodA(); + } + """, + """ + private static native void $methodA(); """, """ /** @@ -184,8 +207,13 @@ struct JNIModuleTests { * public func methodB() throws -> Int64 * } */ - public static native long methodB() throws Exception; + public static long methodB() throws Exception { + return SwiftModule.$methodB(); + } """, + """ + private static native long $methodB(); + """ ] ) } @@ -199,9 +227,9 @@ struct JNIModuleTests { detectChunkByInitialLines: 1, expectedChunks: [ """ - @_cdecl("Java_com_example_swift_SwiftModule_methodA__") - func Java_com_example_swift_SwiftModule_methodA__(environment: UnsafeMutablePointer!, thisClass: jclass) { - do { + @_cdecl("Java_com_example_swift_SwiftModule__00024methodA__") + func Java_com_example_swift_SwiftModule__00024methodA__(environment: UnsafeMutablePointer!, thisClass: jclass) { + do { try SwiftModule.methodA() } catch { environment.throwAsException(error) @@ -209,11 +237,10 @@ struct JNIModuleTests { } """, """ - @_cdecl("Java_com_example_swift_SwiftModule_methodB__") - func Java_com_example_swift_SwiftModule_methodB__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { - do { - let result = try SwiftModule.methodB() - return result.getJNIValue(in: environment) + @_cdecl("Java_com_example_swift_SwiftModule__00024methodB__") + func Java_com_example_swift_SwiftModule__00024methodB__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { + do { + return try SwiftModule.methodB().getJNIValue(in: environment!) } catch { environment.throwAsException(error) return Int64.jniPlaceholderValue diff --git a/Tests/JExtractSwiftTests/JNI/JNIStructTests.swift b/Tests/JExtractSwiftTests/JNI/JNIStructTests.swift index 47b1c4dc..a4084654 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIStructTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIStructTests.swift @@ -98,12 +98,11 @@ struct JNIStructTests { * } */ public static MyStruct init(long x, long y, SwiftArena swiftArena$) { - long self$ = MyStruct.allocatingInit(x, y); - return new MyStruct(self$, swiftArena$); + return new MyStruct(MyStruct.$init(x, y), swiftArena$); } """, """ - private static native long allocatingInit(long x, long y); + private static native long $init(long x, long y); """, ] ) @@ -118,11 +117,12 @@ struct JNIStructTests { detectChunkByInitialLines: 1, expectedChunks: [ """ - @_cdecl("Java_com_example_swift_MyStruct_allocatingInit__JJ") - func Java_com_example_swift_MyStruct_allocatingInit__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, x: jlong, y: jlong) -> jlong { - let self$ = UnsafeMutablePointer.allocate(capacity: 1) - self$.initialize(to: MyStruct(x: Int64(fromJNI: x, in: environment!), y: Int64(fromJNI: y, in: environment!))) - return Int64(Int(bitPattern: self$)).getJNIValue(in: environment) + @_cdecl("Java_com_example_swift_MyStruct__00024init__JJ") + func Java_com_example_swift_MyStruct__00024init__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, x: jlong, y: jlong) -> jlong { + let result$ = UnsafeMutablePointer.allocate(capacity: 1) + result$.initialize(to: MyStruct.init(x: Int64(fromJNI: x, in: environment!), y: Int64(fromJNI: y, in: environment!))) + let resultBits$ = Int64(Int(bitPattern: result$)) + return resultBits$.getJNIValue(in: environment!) } """ ] @@ -170,12 +170,11 @@ struct JNIStructTests { * } */ public void doSomething(long x) { - long self$ = this.$memoryAddress(); - MyStruct.$doSomething(x, self$); + MyStruct.$doSomething(x, this.$memoryAddress()); } """, """ - private static native void $doSomething(long x, long selfPointer); + private static native void $doSomething(long x, long self); """ ] ) @@ -191,12 +190,9 @@ struct JNIStructTests { expectedChunks: [ """ @_cdecl("Java_com_example_swift_MyStruct__00024doSomething__JJ") - func Java_com_example_swift_MyStruct__00024doSomething__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, x: jlong, 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$)) + func Java_com_example_swift_MyStruct__00024doSomething__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, x: jlong, self: jlong) { + assert(self != 0, "self memory address was null") + let selfBits$ = Int(Int64(fromJNI: self, in: environment!)) guard let self$ = UnsafeMutablePointer(bitPattern: selfBits$) else { fatalError("self memory address was null in call to \\(#function)!") } diff --git a/Tests/JExtractSwiftTests/JNI/JNIVariablesTests.swift b/Tests/JExtractSwiftTests/JNI/JNIVariablesTests.swift index 272d27b5..5757e8da 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIVariablesTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIVariablesTests.swift @@ -48,12 +48,11 @@ struct JNIVariablesTests { * } */ public long getConstant() { - long self$ = this.$memoryAddress(); - return MyClass.$getConstant(self$); + return MyClass.$getConstant(this.$memoryAddress()); } """, """ - private static native long $getConstant(long selfPointer); + private static native long $getConstant(long self); """ ]) } @@ -68,17 +67,13 @@ struct JNIVariablesTests { expectedChunks: [ """ @_cdecl("Java_com_example_swift_MyClass__00024getConstant__J") - func Java_com_example_swift_MyClass__00024getConstant__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> 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$)) + func Java_com_example_swift_MyClass__00024getConstant__J(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jlong { + assert(self != 0, "self memory address was null") + let selfBits$ = Int(Int64(fromJNI: self, in: environment!)) guard let self$ = UnsafeMutablePointer(bitPattern: selfBits$) else { fatalError("self memory address was null in call to \\(#function)!") } - let result = self$.pointee.constant - return result.getJNIValue(in: environment) + return self$.pointee.constant.getJNIValue(in: environment!) } """ ] @@ -101,8 +96,7 @@ struct JNIVariablesTests { * } */ public long getMutable() { - long self$ = this.$memoryAddress(); - return MyClass.$getMutable(self$); + return MyClass.$getMutable(this.$memoryAddress()); } """, """ @@ -113,15 +107,14 @@ struct JNIVariablesTests { * } */ public void setMutable(long newValue) { - long self$ = this.$memoryAddress(); - MyClass.$setMutable(newValue, self$); + MyClass.$setMutable(newValue, this.$memoryAddress()); } """, """ - private static native long $getMutable(long selfPointer); + private static native long $getMutable(long self); """, """ - private static native void $setMutable(long newValue, long selfPointer); + private static native void $setMutable(long newValue, long self); """ ] ) @@ -137,27 +130,20 @@ struct JNIVariablesTests { expectedChunks: [ """ @_cdecl("Java_com_example_swift_MyClass__00024getMutable__J") - func Java_com_example_swift_MyClass__00024getMutable__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> 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$)) + func Java_com_example_swift_MyClass__00024getMutable__J(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jlong { + assert(self != 0, "self memory address was null") + let selfBits$ = Int(Int64(fromJNI: self, in: environment!)) guard let self$ = UnsafeMutablePointer(bitPattern: selfBits$) else { fatalError("self memory address was null in call to \\(#function)!") } - let result = self$.pointee.mutable - return result.getJNIValue(in: environment) + return self$.pointee.mutable.getJNIValue(in: environment!) } """, """ @_cdecl("Java_com_example_swift_MyClass__00024setMutable__JJ") - func Java_com_example_swift_MyClass__00024setMutable__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, newValue: jlong, 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$)) + func Java_com_example_swift_MyClass__00024setMutable__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, newValue: jlong, self: jlong) { + assert(self != 0, "self memory address was null") + let selfBits$ = Int(Int64(fromJNI: self, in: environment!)) guard let self$ = UnsafeMutablePointer(bitPattern: selfBits$) else { fatalError("self memory address was null in call to \\(#function)!") } @@ -184,12 +170,11 @@ struct JNIVariablesTests { * } */ public long getComputed() { - long self$ = this.$memoryAddress(); - return MyClass.$getComputed(self$); + return MyClass.$getComputed(this.$memoryAddress()); } """, """ - private static native long $getComputed(long selfPointer); + private static native long $getComputed(long self); """, ] ) @@ -205,18 +190,13 @@ struct JNIVariablesTests { expectedChunks: [ """ @_cdecl("Java_com_example_swift_MyClass__00024getComputed__J") - func Java_com_example_swift_MyClass__00024getComputed__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> 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$)) + func Java_com_example_swift_MyClass__00024getComputed__J(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jlong { + assert(self != 0, "self memory address was null") + let selfBits$ = Int(Int64(fromJNI: self, in: environment!)) guard let self$ = UnsafeMutablePointer(bitPattern: selfBits$) else { fatalError("self memory address was null in call to \\(#function)!") } - - let result = self$.pointee.computed - return result.getJNIValue(in: environment) + return self$.pointee.computed.getJNIValue(in: environment!) } """, ] @@ -239,12 +219,11 @@ struct JNIVariablesTests { * } */ public long getComputedThrowing() throws Exception { - long self$ = this.$memoryAddress(); - return MyClass.$getComputedThrowing(self$); + return MyClass.$getComputedThrowing(this.$memoryAddress()); } """, """ - private static native long $getComputedThrowing(long selfPointer); + private static native long $getComputedThrowing(long self); """, ] ) @@ -260,19 +239,14 @@ struct JNIVariablesTests { expectedChunks: [ """ @_cdecl("Java_com_example_swift_MyClass__00024getComputedThrowing__J") - func Java_com_example_swift_MyClass__00024getComputedThrowing__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> 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$)) + func Java_com_example_swift_MyClass__00024getComputedThrowing__J(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jlong { + assert(self != 0, "self memory address was null") + let selfBits$ = Int(Int64(fromJNI: self, in: environment!)) guard let self$ = UnsafeMutablePointer(bitPattern: selfBits$) else { fatalError("self memory address was null in call to \\(#function)!") } - do { - let result = try self$.pointee.computedThrowing - return result.getJNIValue(in: environment) + return try self$.pointee.computedThrowing.getJNIValue(in: environment!) } catch { environment.throwAsException(error) return Int64.jniPlaceholderValue @@ -299,8 +273,7 @@ struct JNIVariablesTests { * } */ public long getGetterAndSetter() { - long self$ = this.$memoryAddress(); - return MyClass.$getGetterAndSetter(self$); + return MyClass.$getGetterAndSetter(this.$memoryAddress()); } """, """ @@ -311,15 +284,14 @@ struct JNIVariablesTests { * } */ public void setGetterAndSetter(long newValue) { - long self$ = this.$memoryAddress(); - MyClass.$setGetterAndSetter(newValue, self$); + MyClass.$setGetterAndSetter(newValue, this.$memoryAddress()); } """, """ - private static native long $getGetterAndSetter(long selfPointer); + private static native long $getGetterAndSetter(long self); """, """ - private static native void $setGetterAndSetter(long newValue, long selfPointer); + private static native void $setGetterAndSetter(long newValue, long self); """ ] ) @@ -335,32 +307,23 @@ struct JNIVariablesTests { expectedChunks: [ """ @_cdecl("Java_com_example_swift_MyClass__00024getGetterAndSetter__J") - func Java_com_example_swift_MyClass__00024getGetterAndSetter__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> 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$)) + func Java_com_example_swift_MyClass__00024getGetterAndSetter__J(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jlong { + assert(self != 0, "self memory address was null") + let selfBits$ = Int(Int64(fromJNI: self, in: environment!)) guard let self$ = UnsafeMutablePointer(bitPattern: selfBits$) else { fatalError("self memory address was null in call to \\(#function)!") } - - let result = self$.pointee.getterAndSetter - return result.getJNIValue(in: environment) + return self$.pointee.getterAndSetter.getJNIValue(in: environment!) } """, """ @_cdecl("Java_com_example_swift_MyClass__00024setGetterAndSetter__JJ") - func Java_com_example_swift_MyClass__00024setGetterAndSetter__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, newValue: jlong, 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$)) + func Java_com_example_swift_MyClass__00024setGetterAndSetter__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, newValue: jlong, self: jlong) { + assert(self != 0, "self memory address was null") + let selfBits$ = Int(Int64(fromJNI: self, in: environment!)) guard let self$ = UnsafeMutablePointer(bitPattern: selfBits$) else { fatalError("self memory address was null in call to \\(#function)!") } - self$.pointee.getterAndSetter = Int64(fromJNI: newValue, in: environment!) } """ @@ -384,8 +347,7 @@ struct JNIVariablesTests { * } */ public boolean isSomeBoolean() { - long self$ = this.$memoryAddress(); - return MyClass.$isSomeBoolean(self$); + return MyClass.$isSomeBoolean(this.$memoryAddress()); } """, """ @@ -396,15 +358,14 @@ struct JNIVariablesTests { * } */ public void setSomeBoolean(boolean newValue) { - long self$ = this.$memoryAddress(); - MyClass.$setSomeBoolean(newValue, self$); + MyClass.$setSomeBoolean(newValue, this.$memoryAddress()); } """, """ - private static native boolean $isSomeBoolean(long selfPointer); + private static native boolean $isSomeBoolean(long self); """, """ - private static native void $setSomeBoolean(boolean newValue, long selfPointer); + private static native void $setSomeBoolean(boolean newValue, long self); """ ] ) @@ -420,27 +381,20 @@ struct JNIVariablesTests { expectedChunks: [ """ @_cdecl("Java_com_example_swift_MyClass__00024isSomeBoolean__J") - func Java_com_example_swift_MyClass__00024isSomeBoolean__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$)) + func Java_com_example_swift_MyClass__00024isSomeBoolean__J(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jboolean { + assert(self != 0, "self memory address was null") + let selfBits$ = Int(Int64(fromJNI: self, in: environment!)) guard let self$ = UnsafeMutablePointer(bitPattern: selfBits$) else { fatalError("self memory address was null in call to \\(#function)!") } - let result = self$.pointee.someBoolean - return result.getJNIValue(in: environment) + return self$.pointee.someBoolean.getJNIValue(in: environment!) } """, """ @_cdecl("Java_com_example_swift_MyClass__00024setSomeBoolean__ZJ") - func Java_com_example_swift_MyClass__00024setSomeBoolean__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$)) + func Java_com_example_swift_MyClass__00024setSomeBoolean__ZJ(environment: UnsafeMutablePointer!, thisClass: jclass, newValue: jboolean, self: jlong) { + assert(self != 0, "self memory address was null") + let selfBits$ = Int(Int64(fromJNI: self, in: environment!)) guard let self$ = UnsafeMutablePointer(bitPattern: selfBits$) else { fatalError("self memory address was null in call to \\(#function)!") } @@ -467,8 +421,7 @@ struct JNIVariablesTests { * } */ public boolean isBoolean() { - long self$ = this.$memoryAddress(); - return MyClass.$isBoolean(self$); + return MyClass.$isBoolean(this.$memoryAddress()); } """, """ @@ -479,15 +432,14 @@ struct JNIVariablesTests { * } */ public void setBoolean(boolean newValue) { - long self$ = this.$memoryAddress(); - MyClass.$setBoolean(newValue, self$); + MyClass.$setBoolean(newValue, this.$memoryAddress()); } """, """ - private static native boolean $isBoolean(long selfPointer); + private static native boolean $isBoolean(long self); """, """ - private static native void $setBoolean(boolean newValue, long selfPointer); + private static native void $setBoolean(boolean newValue, long self); """ ] ) @@ -503,27 +455,20 @@ struct JNIVariablesTests { 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$)) + func Java_com_example_swift_MyClass__00024isBoolean__J(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jboolean { + assert(self != 0, "self memory address was null") + let selfBits$ = Int(Int64(fromJNI: self, in: environment!)) 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) + return self$.pointee.isBoolean.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$)) + func Java_com_example_swift_MyClass__00024setBoolean__ZJ(environment: UnsafeMutablePointer!, thisClass: jclass, newValue: jboolean, self: jlong) { + assert(self != 0, "self memory address was null") + let selfBits$ = Int(Int64(fromJNI: self, in: environment!)) guard let self$ = UnsafeMutablePointer(bitPattern: selfBits$) else { fatalError("self memory address was null in call to \\(#function)!") }