diff --git a/JavaSwiftKitDemo/src/main/java/org/example/HelloJava2Swift.java b/JavaSwiftKitDemo/src/main/java/org/example/HelloJava2Swift.java index 3b1ea973..4aa00b25 100644 --- a/JavaSwiftKitDemo/src/main/java/org/example/HelloJava2Swift.java +++ b/JavaSwiftKitDemo/src/main/java/org/example/HelloJava2Swift.java @@ -60,9 +60,12 @@ static void tests() { JavaKitExample.globalTakeInt(1337); - MySwiftClass obj = MySwiftClass.init(2222, 7777); + MySwiftClass obj = new MySwiftClass(2222, 7777); SwiftKit.retain(obj.$memorySegment()); System.out.println("[java] obj ref count = " + SwiftKit.retainCount(obj.$memorySegment())); + + obj.voidMethod(); + obj.takeIntMethod(42); } } diff --git a/Sources/JExtractSwift/ImportedDecls.swift b/Sources/JExtractSwift/ImportedDecls.swift index 8ae683dc..a1254d51 100644 --- a/Sources/JExtractSwift/ImportedDecls.swift +++ b/Sources/JExtractSwift/ImportedDecls.swift @@ -48,6 +48,14 @@ public struct ImportedNominalType: ImportedDecl { javaType: javaType ) } + + /// The Java class name without the package. + public var javaClassName: String { + switch javaType { + case .class(package: _, name: let name): name + default: javaType.description + } + } } public enum NominalTypeKind { @@ -116,7 +124,7 @@ public struct ImportedFunc: ImportedDecl, CustomStringConvertible { /// this will contain that declaration's imported name. /// /// This is necessary when rendering accessor Java code we need the type that "self" is expecting to have. - var parentName: TranslatedType? + public var parentName: TranslatedType? public var hasParent: Bool { parentName != nil } /// This is a full name such as init(cap:name:). @@ -152,7 +160,7 @@ public struct ImportedFunc: ImportedDecl, CustomStringConvertible { // // allocating initializer takes a Self.Type instead, but it's also a pointer switch selfVariant { - case nil: + case nil, .wrapper: break case .pointer: @@ -174,10 +182,6 @@ public struct ImportedFunc: ImportedDecl, CustomStringConvertible { type: parentForSelf ) ) - - case .wrapper: - let selfParam: FunctionParameterSyntax = "self$: \(raw: parentName.swiftTypeName)" - params.append(ImportedParam(param: selfParam, type: parentName)) } // TODO: add any metadata for generics and other things we may need to add here diff --git a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift index 262a6ccd..46274c35 100644 --- a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift @@ -28,7 +28,7 @@ extension Swift2JavaTranslator { var printer = CodePrinter() for (_, ty) in importedTypes.sorted(by: { (lhs, rhs) in lhs.key < rhs.key }) { - let filename = "\(ty.javaType).java" + let filename = "\(ty.javaClassName).java" log.info("Printing contents: \(filename)") printImportedClass(&printer, ty) @@ -165,7 +165,7 @@ extension Swift2JavaTranslator { } public func printClass(_ printer: inout CodePrinter, _ decl: ImportedNominalType, body: (inout CodePrinter) -> Void) { - printer.printTypeDecl("public final class \(decl.javaType)") { printer in + printer.printTypeDecl("public final class \(decl.javaClassName)") { printer in // ==== Storage of the class // FIXME: implement the self storage for the memory address and accessors printClassSelfProperty(&printer, decl) @@ -274,7 +274,7 @@ extension Swift2JavaTranslator { printer.print( """ /** Instances are created using static {@code init} methods rather than through the constructor directly. */ - private \(decl.javaType)(MemorySegment selfMemorySegment) { + private \(decl.javaClassName)(MemorySegment selfMemorySegment) { this.selfMemorySegment = selfMemorySegment; } """ @@ -412,24 +412,32 @@ extension Swift2JavaTranslator { printMethodDowncallHandleForAddrDesc(&printer) } + printClassInitializerConstructor(&printer, decl, parentName: parentName) + } + + public func printClassInitializerConstructor( + _ printer: inout CodePrinter, + _ decl: ImportedFunc, + parentName: TranslatedType + ) { + let descClassIdentifier = renderDescClassName(decl) printer.print( """ /** - * Create an instance of the {@code \(parentName.javaType)} Swift class. + * Create an instance of {@code \(parentName.unqualifiedJavaTypeName)}. * - * {@snippet lang=Swift: + * {@snippet lang=swift : * \(decl.swiftDeclRaw ?? "") * } */ - public static \(parentName.javaType) init(\(renderJavaParamDecls(decl, selfVariant: .none))) { + public \(parentName.unqualifiedJavaTypeName)(\(renderJavaParamDecls(decl, selfVariant: .wrapper))) { var mh$ = \(descClassIdentifier).HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall(\(renderForwardParams(decl, selfVariant: nil))); } - var self = (MemorySegment) mh$.invokeExact(\(renderForwardParams(decl, selfVariant: nil)), TYPE_METADATA); - return new \(parentName.javaType)(self); + this.selfMemorySegment = (MemorySegment) mh$.invokeExact(\(renderForwardParams(decl, selfVariant: nil)), TYPE_METADATA); } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); } @@ -542,8 +550,8 @@ extension Swift2JavaTranslator { * \(/*TODO: make a printSnippet func*/decl.swiftDeclRaw ?? "") * } */ - public static \(returnTy) \(decl.baseIdentifier)(\(renderJavaParamDecls(decl, selfVariant: .wrapper))) { - \(maybeReturnCast) \(decl.baseIdentifier)(\(renderForwardParams(decl, selfVariant: .memorySegment))); + public \(returnTy) \(decl.baseIdentifier)(\(renderJavaParamDecls(decl, selfVariant: .wrapper))) { + \(maybeReturnCast) \(decl.baseIdentifier)(\(renderForwardParams(decl, selfVariant: .wrapper))); } """ ) @@ -630,6 +638,11 @@ extension Swift2JavaTranslator { ps.append(param) } + // Add the forwarding "self" + if selfVariant == .wrapper && !decl.isInit { + ps.append("$memorySegment()") + } + return ps.joined(separator: ", ") } diff --git a/Sources/JExtractSwift/Swift2JavaVisitor.swift b/Sources/JExtractSwift/Swift2JavaVisitor.swift index a4190889..e4524461 100644 --- a/Sources/JExtractSwift/Swift2JavaVisitor.swift +++ b/Sources/JExtractSwift/Swift2JavaVisitor.swift @@ -163,7 +163,7 @@ final class Swift2JavaVisitor: SyntaxVisitor { } let initIdentifier = - "init(\(params.compactMap { $0.effectiveName ?? "_" }.joined(separator: ":")))" + "init(\(String(params.flatMap { "\($0.effectiveName ?? "_"):" })))" var funcDecl = ImportedFunc( parentName: currentType.translatedType, diff --git a/Sources/JExtractSwift/TranslatedType.swift b/Sources/JExtractSwift/TranslatedType.swift index 1f6ddd20..8262aff4 100644 --- a/Sources/JExtractSwift/TranslatedType.swift +++ b/Sources/JExtractSwift/TranslatedType.swift @@ -223,6 +223,14 @@ public struct TranslatedType { var swiftTypeName: String { originalSwiftType.trimmedDescription } + + /// Produce the "unqualified" Java type name. + var unqualifiedJavaTypeName: String { + switch javaType { + case .class(package: _, name: let name): name + default: javaType.description + } + } } /// Describes the C-compatible layout as it should be referenced from Java. diff --git a/Tests/JExtractSwiftTests/FuncImportTests.swift b/Tests/JExtractSwiftTests/FuncImportTests.swift index 3b4695c7..4c210b3e 100644 --- a/Tests/JExtractSwiftTests/FuncImportTests.swift +++ b/Tests/JExtractSwiftTests/FuncImportTests.swift @@ -331,8 +331,8 @@ final class MethodImportTests { * public func helloMemberFunction() * } */ - public static void helloMemberFunction(com.example.swift.MySwiftClass self$) { - helloMemberFunction(self$); + public void helloMemberFunction() { + helloMemberFunction($memorySegment()); } """ ) @@ -364,11 +364,54 @@ final class MethodImportTests { * public func makeInt() -> Int * } */ - public static long makeInt(com.example.swift.MySwiftClass self$) { - return (long) makeInt(self$); + public long makeInt() { + return (long) makeInt($memorySegment()); } """ ) } + @Test func class_constructor() async throws { + let st = Swift2JavaTranslator( + javaPackage: "com.example.swift", + swiftModuleName: "__FakeModule" + ) + st.log.logLevel = .trace + + try await st.analyze(swiftInterfacePath: "/fake/__FakeModule/SwiftFile.swiftinterface", text: class_interfaceFile) + + let initDecl: ImportedFunc = st.importedTypes["MySwiftClass"]!.initializers.first { + $0.identifier == "init(len:cap:)" + }! + + let output = CodePrinter.toString { printer in + st.printClassInitializerConstructor(&printer, initDecl, parentName: initDecl.parentName!) + } + + assertOutput( + output, + expected: + """ + /** + * Create an instance of {@code MySwiftClass}. + * + * {@snippet lang=swift : + * public init(len: Swift.Int, cap: Swift.Int) + * } + */ + public MySwiftClass(long len, long cap) { + var mh$ = init_len_cap.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall(len, cap); + } + + this.selfMemorySegment = (MemorySegment) mh$.invokeExact(len, cap, TYPE_METADATA); + } catch (Throwable ex$) { + throw new AssertionError(\"should not reach here\", ex$); + } + } + """ + ) + } }