Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
16 changes: 10 additions & 6 deletions Sources/JExtractSwift/ImportedDecls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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:).
Expand Down Expand Up @@ -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:
Expand All @@ -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
Expand Down
33 changes: 23 additions & 10 deletions Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
"""
Expand Down Expand Up @@ -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$);
}
Expand Down Expand Up @@ -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)));
}
"""
)
Expand Down Expand Up @@ -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: ", ")
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/JExtractSwift/Swift2JavaVisitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 8 additions & 0 deletions Sources/JExtractSwift/TranslatedType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
51 changes: 47 additions & 4 deletions Tests/JExtractSwiftTests/FuncImportTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
"""
)
Expand Down Expand Up @@ -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$);
}
}
"""
)
}
}