diff --git a/Package.swift b/Package.swift index e3a5b37b..1702da7a 100644 --- a/Package.swift +++ b/Package.swift @@ -152,7 +152,7 @@ let package = Package( .library( name: "SwiftJava", type: .dynamic, - targets: ["SwiftJava"] + targets: ["SwiftJava", "SwiftJavaRuntimeSupport"] ), .library( @@ -226,12 +226,6 @@ let package = Package( ] ), - // Support library written in Swift for SwiftKit "Java" - .library( - name: "SwiftJavaRuntimeSupport", - targets: ["SwiftJavaRuntimeSupport"] - ), - .library( name: "SwiftRuntimeFunctions", type: .dynamic, @@ -311,6 +305,7 @@ let package = Package( exclude: ["swift-java.config"], swiftSettings: [ .swiftLanguageMode(.v5), + .enableUpcomingFeature("ImplicitOpenExistentials"), .unsafeFlags( ["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"], .when(platforms: [.macOS, .linux, .windows]) diff --git a/Samples/SwiftJavaExtractJNISampleApp/Package.swift b/Samples/SwiftJavaExtractJNISampleApp/Package.swift index 121ffad8..c30326cd 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/Package.swift +++ b/Samples/SwiftJavaExtractJNISampleApp/Package.swift @@ -110,7 +110,6 @@ let package = Package( dependencies: [ .product(name: "SwiftJava", package: "swift-java"), .product(name: "CSwiftJavaJNI", package: "swift-java"), - .product(name: "SwiftJavaRuntimeSupport", package: "swift-java"), ], exclude: [ "swift-java.config" diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Benchmark.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Benchmark.swift new file mode 100644 index 00000000..4f96b528 --- /dev/null +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Benchmark.swift @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 +// +//===----------------------------------------------------------------------===// + +public struct BasicStruct { + public var value: Int + public init(value: Int) { + self.value = value + } +} + +public struct GenericStruct { + public var value: Int + public init(value: Int) { + self.value = value + } +} + +public func makeGenericStruct(value: Int) -> GenericStruct { + GenericStruct(value: value) +} diff --git a/Samples/SwiftJavaExtractJNISampleApp/src/jmh/java/com/example/swift/GenericBenchmark.java b/Samples/SwiftJavaExtractJNISampleApp/src/jmh/java/com/example/swift/GenericBenchmark.java new file mode 100644 index 00000000..63a4aeb4 --- /dev/null +++ b/Samples/SwiftJavaExtractJNISampleApp/src/jmh/java/com/example/swift/GenericBenchmark.java @@ -0,0 +1,71 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 +// +//===----------------------------------------------------------------------===// + +package com.example.swift; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import org.swift.swiftkit.core.ClosableSwiftArena; +import org.swift.swiftkit.core.ConfinedSwiftMemorySession; +import org.swift.swiftkit.core.SwiftArena; + +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Fork(value = 3, jvmArgsAppend = { "--enable-native-access=ALL-UNNAMED" }) +public class GenericBenchmark { + @State(Scope.Benchmark) + public static class BenchmarkState { + ClosableSwiftArena arena; + BasicStruct basicStruct; + GenericStruct genericStruct; + + @Setup(Level.Trial) + public void beforeAll() { + arena = SwiftArena.ofConfined(); + basicStruct = BasicStruct.init(42, arena); + genericStruct = MySwiftLibrary.makeGenericStruct(42, arena); + } + + @TearDown(Level.Trial) + public void afterAll() { + arena.close(); + } + } + + @Benchmark + public long basicStruct_basicGetter(BenchmarkState state, Blackhole bh) { + return state.basicStruct.getValue(); + } + + @Benchmark + public long genericStruct_basicGetter(BenchmarkState state, Blackhole bh) { + return state.genericStruct.getValue(); + } + + @Benchmark + public String basicStruct_toString(BenchmarkState state, Blackhole bh) { + // toString is internally implemented as a generic method in Swift + return state.basicStruct.toString(); + } + + @Benchmark + public String genericStruct_toString(BenchmarkState state, Blackhole bh) { + // toString is internally implemented as a generic method in Swift + return state.genericStruct.toString(); + } +} diff --git a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift index 21fb23bd..b2438dd3 100644 --- a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift +++ b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift @@ -993,14 +993,6 @@ extension LoweredFunctionSignature { .joined(separator: ", ") resultExpr = "\(callee)(\(raw: arguments))" - case .synthesizedFunction(let function): - switch function { - case .toString: - resultExpr = "String(describing: \(callee))" - case .toDebugString: - resultExpr = "String(reflecting: \(callee))" - } - case .getter: assert(paramExprs.isEmpty) resultExpr = callee diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift index 239f5551..50024c75 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift @@ -160,7 +160,7 @@ extension FFMSwift2JavaGenerator { switch decl.apiKind { case .getter, .subscriptGetter: decl.javaGetterName case .setter, .subscriptSetter: decl.javaSetterName - case .function, .synthesizedFunction, .initializer, .enumCase: decl.name + case .function, .initializer, .enumCase: decl.name } // Signature. diff --git a/Sources/JExtractSwiftLib/ImportedDecls.swift b/Sources/JExtractSwiftLib/ImportedDecls.swift index 136647b4..0172f6b4 100644 --- a/Sources/JExtractSwiftLib/ImportedDecls.swift +++ b/Sources/JExtractSwiftLib/ImportedDecls.swift @@ -17,14 +17,8 @@ import SwiftSyntax /// Any imported (Swift) declaration protocol ImportedDecl: AnyObject {} -package enum SynthesizedAPI: Equatable { - case toString - case toDebugString -} - package enum SwiftAPIKind: Equatable { case function - case synthesizedFunction(SynthesizedAPI) case initializer case getter case setter @@ -178,7 +172,7 @@ public final class ImportedFunc: ImportedDecl, CustomStringConvertible { case .getter: "getter:" case .setter: "setter:" case .enumCase: "case:" - case .function, .synthesizedFunction, .initializer: "" + case .function, .initializer: "" case .subscriptGetter: "subscriptGetter:" case .subscriptSetter: "subscriptSetter:" } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index dcbc9a8c..736e1ba3 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -295,6 +295,20 @@ extension JNISwift2JavaGenerator { printTypeMetadataAddressFunction(&printer, decl) printer.println() + + printer.print( + """ + public String toString() { + return SwiftObjects.toString(this.$memoryAddress(), this.$typeMetadataAddress()); + } + + public String toDebugString() { + return SwiftObjects.toDebugString(this.$memoryAddress(), this.$typeMetadataAddress()); + } + """ + ) + printer.println() + printDestroyFunction(&printer, decl) } } @@ -394,17 +408,9 @@ extension JNISwift2JavaGenerator { ) } - // TODO: Consider whether all of these "utility" functions can be printed using our existing printing logic. - if decl.swiftNominal.isGeneric { - printer.printBraceBlock("public Discriminator getDiscriminator()") { printer in - printer.print("return Discriminator.values()[$getDiscriminator(this.$memoryAddress(), this.$typeMetadataAddress())];") - } - printer.print("private static native int $getDiscriminator(long selfPointer, long selfTypePointer);") - } else { - printer.printBraceBlock("public Discriminator getDiscriminator()") { printer in - printer.print("return Discriminator.values()[$getDiscriminator(this.$memoryAddress())];") - } - printer.print("private static native int $getDiscriminator(long selfPointer);") + printer.printBraceBlock("public Discriminator getDiscriminator()") { printer in + printer.print("var raw = SwiftObjects.getRawDiscriminator(this.$memoryAddress(), this.$typeMetadataAddress());") + printer.print("return Discriminator.values()[raw];") } } @@ -717,20 +723,10 @@ extension JNISwift2JavaGenerator { } } else { printer.print("private static native long $typeMetadataAddressDowncall();") - - let funcName = "$typeMetadataAddress" printer.print("@Override") printer.printBraceBlock("public long $typeMetadataAddress()") { printer in - printer.print( - """ - long self$ = this.$memoryAddress(); - if (CallTraces.TRACE_DOWNCALLS) { - CallTraces.traceDowncall("\(type.swiftNominal.name).\(funcName)", - "this", this, - "self", self$); - } - """ - ) + // INFO: We are omitting `CallTraces.traceDowncall` here. + // It internally calls `toString`, which in turn calls `$typeMetadataAddress`, creating an infinite loop. printer.print("return \(type.swiftNominal.name).$typeMetadataAddressDowncall();") } } @@ -738,21 +734,14 @@ extension JNISwift2JavaGenerator { /// Prints the destroy function for a `JNISwiftInstance` private func printDestroyFunction(_ printer: inout CodePrinter, _ type: ImportedNominalType) { - let isGeneric = type.swiftNominal.isGeneric - if isGeneric { - printer.print("private static native void $destroy(long selfPointer, long selfTypePointer);") - } else { - printer.print("private static native void $destroy(long selfPointer);") - } - let funcName = "$createDestroyFunction" printer.print("@Override") printer.printBraceBlock("public Runnable \(funcName)()") { printer in printer.print("long self$ = this.$memoryAddress();") - if isGeneric { + printer.print("long selfType$ = this.$typeMetadataAddress();") + if type.swiftNominal.isGeneric { printer.print( """ - long selfType$ = this.$typeMetadataAddress(); if (CallTraces.TRACE_DOWNCALLS) { CallTraces.traceDowncall("\(type.swiftNominal.name).\(funcName)", "this", this, @@ -765,7 +754,7 @@ extension JNISwift2JavaGenerator { if (CallTraces.TRACE_DOWNCALLS) { CallTraces.traceDowncall("\(type.swiftNominal.name).$destroy", "self", self$, "selfType", selfType$); } - \(type.swiftNominal.name).$destroy(self$, selfType$); + SwiftObjects.destroy(self$, selfType$); } }; """ @@ -784,7 +773,7 @@ extension JNISwift2JavaGenerator { if (CallTraces.TRACE_DOWNCALLS) { CallTraces.traceDowncall("\(type.swiftNominal.name).$destroy", "self", self$); } - \(type.swiftNominal.name).$destroy(self$); + SwiftObjects.destroy(self$, selfType$); } }; """ diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index 3433aff4..32765ca1 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -229,7 +229,7 @@ extension JNISwift2JavaGenerator { switch decl.apiKind { case .getter, .subscriptGetter: decl.javaGetterName case .setter, .subscriptSetter: decl.javaSetterName - case .function, .synthesizedFunction, .initializer, .enumCase: decl.name + case .function, .initializer, .enumCase: decl.name } // Swift -> Java diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index 0870f584..73a40924 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -13,7 +13,6 @@ //===----------------------------------------------------------------------===// import JavaTypes -import SwiftSyntax #if canImport(FoundationEssentials) import FoundationEssentials @@ -259,7 +258,7 @@ extension JNISwift2JavaGenerator { } if type.swiftNominal.kind == .enum { - printEnumDiscriminator(&printer, type) + printEnumRawDiscriminator(&printer, type) printer.println() if !type.swiftNominal.isGeneric { @@ -283,7 +282,6 @@ extension JNISwift2JavaGenerator { printSpecificTypeThunks(&printer, type) printTypeMetadataAddressThunk(&printer, type) printer.println() - printDestroyFunctionThunk(&printer, type) } private func printProtocolThunks(_ printer: inout CodePrinter, _ type: ImportedNominalType) throws { @@ -294,51 +292,14 @@ extension JNISwift2JavaGenerator { try printSwiftInterfaceWrapper(&printer, protocolWrapper) } - private func printEnumDiscriminator(_ printer: inout CodePrinter, _ type: ImportedNominalType) { + private func printEnumRawDiscriminator(_ printer: inout CodePrinter, _ type: ImportedNominalType) { if type.cases.isEmpty { return } - let selfPointerParam = JavaParameter(name: "selfPointer", type: .long) - var parameters = [selfPointerParam] - if type.swiftNominal.isGeneric { - parameters.append(JavaParameter(name: "selfTypePointer", type: .long)) - } - - printCDecl( - &printer, - javaMethodName: "$getDiscriminator", - parentName: type.swiftNominal.name, - parameters: parameters, - resultType: .int - ) { printer in - if type.swiftNominal.isGeneric { - let knownTypes = SwiftKnownTypes(symbolTable: lookupContext.symbolTable) - let discriminatorFunctionSignature = SwiftFunctionSignature( - selfParameter: .instance(convention: .byValue, swiftType: type.swiftType), - parameters: [], - result: .init(convention: .direct, type: knownTypes.int), - effectSpecifiers: [], - genericParameters: [], - genericRequirements: [] - ) - printFunctionOpenerCall( - &printer, - .init( - module: swiftModuleName, - swiftDecl: DeclSyntax("func getDiscriminator() -> Int"), - name: "getDiscriminator", - apiKind: .function, - functionSignature: discriminatorFunctionSignature - ) - ) - } else { - let selfPointer = self.printSelfJLongToUnsafeMutablePointer( - &printer, - swiftParentName: type.swiftNominal.name, - selfPointerParam - ) - printer.printBraceBlock("switch (\(selfPointer).pointee)") { printer in + printer.printBraceBlock("extension \(type.swiftNominal.qualifiedName): _RawDiscriminatorRepresentable") { printer in + printer.printBraceBlock("public var _rawDiscriminator: Int32") { printer in + printer.printBraceBlock("switch self") { printer in for (idx, enumCase) in type.cases.enumerated() { printer.print("case .\(enumCase.name): return \(idx)") } @@ -623,14 +584,6 @@ extension JNISwift2JavaGenerator { .joined(separator: ", ") result = "\(tryClause)\(callee).\(decl.name)(\(downcallArguments))" - case .synthesizedFunction(let function): - switch function { - case .toString: - result = "String(describing: \(callee))" - case .toDebugString: - result = "String(reflecting: \(callee))" - } - case .enumCase: let downcallArguments = zip( decl.functionSignature.parameters, @@ -814,55 +767,6 @@ extension JNISwift2JavaGenerator { } } - /// Prints the implementation of the destroy function. - private func printDestroyFunctionThunk(_ printer: inout CodePrinter, _ type: ImportedNominalType) { - let selfPointerParam = JavaParameter(name: "selfPointer", type: .long) - var parameters = [selfPointerParam] - if type.swiftNominal.isGeneric { - parameters.append(JavaParameter(name: "selfTypePointer", type: .long)) - } - - printCDecl( - &printer, - javaMethodName: "$destroy", - parentName: type.swiftNominal.qualifiedName, - parameters: parameters, - resultType: .void - ) { printer in - if type.swiftNominal.isGeneric { - let destroyFunctionSignature = SwiftFunctionSignature( - selfParameter: .instance(convention: .byValue, swiftType: type.swiftType), - parameters: [], - result: .void, - effectSpecifiers: [], - genericParameters: [], - genericRequirements: [] - ) - printFunctionOpenerCall( - &printer, - .init( - module: swiftModuleName, - swiftDecl: DeclSyntax("func destroy()"), - name: "destroy", - apiKind: .function, - functionSignature: destroyFunctionSignature - ) - ) - } else { - let parentName = type.qualifiedName - let selfVar = self.printSelfJLongToUnsafeMutablePointer(&printer, swiftParentName: parentName, selfPointerParam) - // Deinitialize the pointer allocated (which will call the VWT destroy method) - // then deallocate the memory. - printer.print( - """ - \(selfVar).deinitialize(count: 1) - \(selfVar).deallocate() - """ - ) - } - } - } - /// Prints thunks for specific known types like Foundation.Date, Foundation.Data private func printSpecificTypeThunks(_ printer: inout CodePrinter, _ type: ImportedNominalType) { guard let knownType = type.swiftNominal.knownTypeKind else { return } @@ -1008,12 +912,6 @@ extension JNISwift2JavaGenerator { for method in type.methods { printFunctionDecl(&printer, decl: method, skipMethodBody: true) } - - if type.swiftNominal.kind == .enum { - printer.print("static func _getDiscriminator(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jint") - } - - printer.print("static func _destroy(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong)") } printer.println() printer.printBraceBlock("extension \(type.swiftNominal.name): \(protocolName)") { printer in @@ -1026,32 +924,6 @@ extension JNISwift2JavaGenerator { if method.isStatic { continue } printFunctionDecl(&printer, decl: method, skipMethodBody: false) } - - if type.swiftNominal.kind == .enum { - printer.printBraceBlock("static func _getDiscriminator(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jint") { printer in - let selfPointer = self.printSelfJLongToUnsafeMutablePointer( - &printer, - swiftParentName: "Self", - JavaParameter(name: "selfPointer", type: .long) - ) - printer.printBraceBlock("switch (\(selfPointer).pointee)") { printer in - for (idx, enumCase) in type.cases.enumerated() { - printer.print("case .\(enumCase.name): return \(idx)") - } - } - } - } - - printer.printBraceBlock("static func _destroy(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong)") { printer in - printer.print(#"assert(selfPointer != 0, "selfPointer memory address was null")"#) - printer.print("let selfPointerBits$ = Int(Int64(fromJNI: selfPointer, in: environment))") - printer.print("let selfPointer$ = UnsafeMutablePointer(bitPattern: selfPointerBits$)") - printer.printBraceBlock("guard let selfPointer$ else") { printer in - printer.print("fatalError(\"selfPointer memory address was null in call to \\(#function)!\")") - } - printer.print("selfPointer$.deinitialize(count: 1)") - printer.print("selfPointer$.deallocate()") - } } } diff --git a/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift b/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift index b96e50f5..44e9c66f 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift +++ b/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift @@ -85,8 +85,6 @@ final class Swift2JavaVisitor { for memberItem in node.memberBlock.members { self.visit(decl: memberItem.decl, in: importedNominalType, sourceFilePath: sourceFilePath) } - - self.synthesizeToStringMethods(in: importedNominalType) } func visit( @@ -422,46 +420,6 @@ final class Swift2JavaVisitor { } } } - - private func synthesizeToStringMethods(in imported: ImportedNominalType) { - switch imported.swiftNominal.kind { - case .actor, .class, .enum, .struct: - break - case .protocol: - return - } - - let knownTypes = SwiftKnownTypes(symbolTable: translator.symbolTable) - let toStringFunctionSignature = SwiftFunctionSignature( - selfParameter: .instance(convention: .byValue, swiftType: imported.swiftType), - parameters: [], - result: SwiftResult(convention: .direct, type: knownTypes.string), - effectSpecifiers: [], - genericParameters: [], - genericRequirements: [] - ) - - func makeToStringFunc(name: String, kind: SwiftAPIKind) -> ImportedFunc { - ImportedFunc( - module: translator.swiftModuleName, - swiftDecl: DeclSyntax("func \(raw: name)() -> String"), - name: name, - apiKind: kind, - functionSignature: toStringFunctionSignature - ) - } - - if !imported.methods.contains(where: { - $0.name == "toString" && $0.functionSignature == toStringFunctionSignature - }) { - imported.methods.append(makeToStringFunc(name: "toString", kind: .synthesizedFunction(.toString))) - } - if !imported.methods.contains(where: { - $0.name == "toDebugString" && $0.functionSignature == toStringFunctionSignature - }) { - imported.methods.append(makeToStringFunc(name: "toDebugString", kind: .synthesizedFunction(.toDebugString))) - } - } } extension DeclSyntaxProtocol where Self: WithModifiersSyntax & WithAttributesSyntax { diff --git a/Sources/SwiftJava/SwiftObjects.swift b/Sources/SwiftJava/SwiftObjects.swift new file mode 100644 index 00000000..14933a5b --- /dev/null +++ b/Sources/SwiftJava/SwiftObjects.swift @@ -0,0 +1,92 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 +// +//===----------------------------------------------------------------------===// + +public protocol _RawDiscriminatorRepresentable { + var _rawDiscriminator: Int32 { get } +} + +@JavaClass("org.swift.swiftkit.core.SwiftObjects") +open class SwiftObjects: JavaObject { +} + +@JavaImplementation("org.swift.swiftkit.core.SwiftObjects") +extension SwiftObjects { + @JavaMethod + public static func getRawDiscriminator(environment: UnsafeMutablePointer!, selfPointer: Int, selfTypePointer: Int) -> Int32 { + guard let selfType$ = UnsafeRawPointer(bitPattern: selfTypePointer) else { + fatalError("selfType metadata address was null") + } + let typeMetadata = unsafeBitCast(selfType$, to: Any.Type.self) + guard let typeMetadata = typeMetadata as? (any _RawDiscriminatorRepresentable.Type) else { + fatalError("_RawDiscriminatorRepresentable conformance did not found in \(typeMetadata)") + } + + func perform(as type: T.Type) -> Int32 { + guard let self$ = UnsafeMutablePointer(bitPattern: selfPointer) else { + fatalError("self memory address was null") + } + return self$.pointee._rawDiscriminator + } + return perform(as: typeMetadata) + } + + @JavaMethod + public static func toString(environment: UnsafeMutablePointer!, selfPointer: Int, selfTypePointer: Int) -> String { + guard let selfType$ = UnsafeRawPointer(bitPattern: selfTypePointer) else { + fatalError("selfType metadata address was null") + } + let typeMetadata = unsafeBitCast(selfType$, to: Any.Type.self) + + func perform(as type: T.Type) -> String { + guard let self$ = UnsafeMutablePointer(bitPattern: selfPointer) else { + fatalError("self memory address was null") + } + return String(describing: self$.pointee) + } + return perform(as: typeMetadata) + } + + @JavaMethod + public static func toDebugString(environment: UnsafeMutablePointer!, selfPointer: Int, selfTypePointer: Int) -> String { + guard let selfType$ = UnsafeRawPointer(bitPattern: selfTypePointer) else { + fatalError("selfType metadata address was null") + } + let typeMetadata = unsafeBitCast(selfType$, to: Any.Type.self) + + func perform(as type: T.Type) -> String { + guard let self$ = UnsafeMutablePointer(bitPattern: selfPointer) else { + fatalError("self memory address was null") + } + return String(reflecting: self$.pointee) + } + return perform(as: typeMetadata) + } + + @JavaMethod + public static func destroy(environment: UnsafeMutablePointer!, selfPointer: Int, selfTypePointer: Int) { + guard let selfType$ = UnsafeRawPointer(bitPattern: selfTypePointer) else { + fatalError("selfType metadata address was null") + } + let typeMetadata = unsafeBitCast(selfType$, to: Any.Type.self) + + func perform(as type: T.Type) { + guard let self$ = UnsafeMutablePointer(bitPattern: selfPointer) else { + fatalError("self memory address was null") + } + self$.deinitialize(count: 1) + self$.deallocate() + } + return perform(as: typeMetadata) + } +} diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftObjects.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftObjects.java index c508e90e..3674198e 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftObjects.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftObjects.java @@ -23,4 +23,9 @@ public static void requireNonZero(long number, String name) { throw new IllegalArgumentException(String.format("'%s' must not be zero!", name)); } } + + public static native int getRawDiscriminator(long selfPointer, long selfTypePointer); + public static native String toString(long selfPointer, long selfTypePointer); + public static native String toDebugString(long selfPointer, long selfTypePointer); + public static native void destroy(long selfPointer, long selfTypePointer); } diff --git a/Tests/JExtractSwiftTests/JNI/JNIClassTests.swift b/Tests/JExtractSwiftTests/JNI/JNIClassTests.swift index 61271356..164ca649 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIClassTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIClassTests.swift @@ -96,16 +96,6 @@ struct JNIClassTests { """, ] ) - try assertOutput( - input: source, - .jni, - .java, - expectedChunks: [ - """ - private static native void $destroy(long selfPointer); - """ - ] - ) try assertOutput( input: source, .jni, @@ -115,6 +105,7 @@ struct JNIClassTests { @Override public Runnable $createDestroyFunction() { long self$ = this.$memoryAddress(); + long selfType$ = this.$typeMetadataAddress(); if (CallTraces.TRACE_DOWNCALLS) { CallTraces.traceDowncall("MyClass.$createDestroyFunction", "this", this, @@ -126,7 +117,7 @@ struct JNIClassTests { if (CallTraces.TRACE_DOWNCALLS) { CallTraces.traceDowncall("MyClass.$destroy", "self", self$); } - MyClass.$destroy(self$); + SwiftObjects.destroy(self$, selfType$); } }; """ @@ -246,33 +237,6 @@ struct JNIClassTests { ) } - @Test - func destroyFunction_swiftThunks() throws { - try assertOutput( - input: source, - .jni, - .swift, - detectChunkByInitialLines: 1, - expectedChunks: [ - """ - @_cdecl("Java_com_example_swift_MyClass__00024destroy__J") - public func Java_com_example_swift_MyClass__00024destroy__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) { - guard let env$ = environment else { - fatalError("Missing JNIEnv in downcall to \\(#function)") - } - assert(selfPointer != 0, "selfPointer memory address was null") - let selfPointerBits$ = Int(Int64(fromJNI: selfPointer, in: env$)) - guard let selfPointer$ = UnsafeMutablePointer(bitPattern: selfPointerBits$) else { - fatalError("selfPointer memory address was null in call to \\(#function)!") - } - selfPointer$.deinitialize(count: 1) - selfPointer$.deallocate() - } - """ - ] - ) - } - @Test func memberMethod_javaBindings() throws { try assertOutput( diff --git a/Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift b/Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift index a077a98a..65cacd90 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift @@ -79,26 +79,25 @@ struct JNIEnumTests { } """, """ - private static native void $destroy(long selfPointer); - """, - """ @Override public Runnable $createDestroyFunction() { - long self$ = this.$memoryAddress(); - if (CallTraces.TRACE_DOWNCALLS) { - CallTraces.traceDowncall("MyEnum.$createDestroyFunction", - "this", this, - "self", self$); - } - return new Runnable() { - @Override - public void run() { - if (CallTraces.TRACE_DOWNCALLS) { - CallTraces.traceDowncall("MyEnum.$destroy", "self", self$); - } - MyEnum.$destroy(self$); + long self$ = this.$memoryAddress(); + long selfType$ = this.$typeMetadataAddress(); + if (CallTraces.TRACE_DOWNCALLS) { + CallTraces.traceDowncall("MyEnum.$createDestroyFunction", + "this", this, + "self", self$); } - }; + return new Runnable() { + @Override + public void run() { + if (CallTraces.TRACE_DOWNCALLS) { + CallTraces.traceDowncall("MyEnum.$destroy", "self", self$); + } + SwiftObjects.destroy(self$, selfType$); + } + }; + } """, ] ) @@ -110,7 +109,7 @@ struct JNIEnumTests { input: source, .jni, .java, - detectChunkByInitialLines: 1, + detectChunkByInitialLines: 2, expectedChunks: [ """ public enum Discriminator { @@ -121,12 +120,10 @@ struct JNIEnumTests { """, """ public Discriminator getDiscriminator() { - return Discriminator.values()[$getDiscriminator(this.$memoryAddress())]; + var raw = SwiftObjects.getRawDiscriminator(this.$memoryAddress(), this.$typeMetadataAddress()); + return Discriminator.values()[raw]; } """, - """ - private static native int $getDiscriminator(long selfPointer); - """, ] ) } @@ -140,13 +137,13 @@ struct JNIEnumTests { detectChunkByInitialLines: 1, expectedChunks: [ """ - @_cdecl("Java_com_example_swift_MyEnum__00024getDiscriminator__J") - public func Java_com_example_swift_MyEnum__00024getDiscriminator__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jint { - ... - switch (selfPointer$.pointee) { - case .first: return 0 - case .second: return 1 - case .third: return 2 + extension MyEnum: _RawDiscriminatorRepresentable { + public var _rawDiscriminator: Int32 { + switch self { + case .first: return 0 + case .second: return 1 + case .third: return 2 + } } } """ diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift index b468c5d0..b8ac9d84 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift @@ -73,7 +73,6 @@ struct JNIGenericTypeTests { } """, """ - private static native void $destroy(long selfPointer, long selfTypePointer); @Override public Runnable $createDestroyFunction() { long self$ = this.$memoryAddress(); @@ -90,7 +89,7 @@ struct JNIGenericTypeTests { if (CallTraces.TRACE_DOWNCALLS) { CallTraces.traceDowncall("MyID.$destroy", "self", self$, "selfType", selfType$); } - MyID.$destroy(self$, selfType$); + SwiftObjects.destroy(self$, selfType$); } }; } diff --git a/Tests/JExtractSwiftTests/JNI/JNINestedTypesTests.swift b/Tests/JExtractSwiftTests/JNI/JNINestedTypesTests.swift index 01499209..159e496e 100644 --- a/Tests/JExtractSwiftTests/JNI/JNINestedTypesTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNINestedTypesTests.swift @@ -75,20 +75,20 @@ struct JNINestedTypesTests { detectChunkByInitialLines: 1, expectedChunks: [ """ - @_cdecl("Java_com_example_swift_A__00024destroy__J") - public func Java_com_example_swift_A__00024destroy__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) { + @_cdecl("Java_com_example_swift_A__00024typeMetadataAddressDowncall__") + public func Java_com_example_swift_A__00024typeMetadataAddressDowncall__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { ... } """, """ - @_cdecl("Java_com_example_swift_A_00024B__00024destroy__J") - public func Java_com_example_swift_A_00024B__00024destroy__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) { + @_cdecl("Java_com_example_swift_A_00024B__00024typeMetadataAddressDowncall__") + public func Java_com_example_swift_A_00024B__00024typeMetadataAddressDowncall__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { ... } """, """ - @_cdecl("Java_com_example_swift_A_00024B__00024destroy__J") - public func Java_com_example_swift_A_00024B__00024destroy__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) { + @_cdecl("Java_com_example_swift_A_00024B_00024C__00024typeMetadataAddressDowncall__") + public func Java_com_example_swift_A_00024B_00024C__00024typeMetadataAddressDowncall__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { ... } """, diff --git a/Tests/JExtractSwiftTests/JNI/JNIStructTests.swift b/Tests/JExtractSwiftTests/JNI/JNIStructTests.swift index db14095b..1c8d7367 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIStructTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIStructTests.swift @@ -82,16 +82,6 @@ struct JNIStructTests { """, ] ) - try assertOutput( - input: source, - .jni, - .java, - expectedChunks: [ - """ - private static native void $destroy(long selfPointer); - """ - ] - ) try assertOutput( input: source, .jni, @@ -101,6 +91,7 @@ struct JNIStructTests { @Override public Runnable $createDestroyFunction() { long self$ = this.$memoryAddress(); + long selfType$ = this.$typeMetadataAddress(); if (CallTraces.TRACE_DOWNCALLS) { CallTraces.traceDowncall("MyStruct.$createDestroyFunction", "this", this, @@ -112,7 +103,7 @@ struct JNIStructTests { if (CallTraces.TRACE_DOWNCALLS) { CallTraces.traceDowncall("MyStruct.$destroy", "self", self$); } - MyStruct.$destroy(self$); + SwiftObjects.destroy(self$, selfType$); } }; } @@ -167,32 +158,6 @@ struct JNIStructTests { ) } - @Test - func destroyFunction_swiftThunks() throws { - try assertOutput( - input: source, - .jni, - .swift, - expectedChunks: [ - """ - @_cdecl("Java_com_example_swift_MyStruct__00024destroy__J") - public func Java_com_example_swift_MyStruct__00024destroy__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) { - guard let env$ = environment else { - fatalError("Missing JNIEnv in downcall to \\(#function)") - } - assert(selfPointer != 0, "selfPointer memory address was null") - let selfPointerBits$ = Int(Int64(fromJNI: selfPointer, in: env$)) - guard let selfPointer$ = UnsafeMutablePointer(bitPattern: selfPointerBits$) else { - fatalError("selfPointer memory address was null in call to \\(#function)!") - } - selfPointer$.deinitialize(count: 1) - selfPointer$.deallocate() - } - """ - ] - ) - } - @Test func memberMethod_javaBindings() throws { try assertOutput( diff --git a/Tests/JExtractSwiftTests/JNI/JNISubscriptsTests.swift b/Tests/JExtractSwiftTests/JNI/JNISubscriptsTests.swift index 05ff7eab..eebdb35d 100644 --- a/Tests/JExtractSwiftTests/JNI/JNISubscriptsTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNISubscriptsTests.swift @@ -62,16 +62,6 @@ struct JNISubscriptsTests { """, ] ) - try assertOutput( - input: noParamsSubscriptSource, - .jni, - .java, - expectedChunks: [ - """ - private static native void $destroy(long selfPointer); - """ - ] - ) } @Test("Test generation of JavaClass for subscript with parameters") diff --git a/Tests/JExtractSwiftTests/JNI/JNIToStringTests.swift b/Tests/JExtractSwiftTests/JNI/JNIToStringTests.swift index 45b67aca..4b0437b9 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIToStringTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIToStringTests.swift @@ -32,28 +32,8 @@ struct JNIToStringTests { detectChunkByInitialLines: 1, expectedChunks: [ """ - public java.lang.String toString() { - return MyType.$toString(this.$memoryAddress()); - } - private static native java.lang.String $toString(long selfPointer); - """ - ] - ) - } - - @Test("JNI toString (Swift)") - func toString_swift() throws { - try assertOutput( - input: source, - .jni, - .swift, - detectChunkByInitialLines: 1, - expectedChunks: [ - """ - @_cdecl("Java_com_example_swift_MyType__00024toString__J") - public func Java_com_example_swift_MyType__00024toString__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jstring? { - ... - return String(describing: selfPointer$.pointee).getJNIValue(in: environment) + public String toString() { + return SwiftObjects.toString(this.$memoryAddress(), this.$typeMetadataAddress()); } """ ] @@ -69,28 +49,8 @@ struct JNIToStringTests { detectChunkByInitialLines: 1, expectedChunks: [ """ - public java.lang.String toDebugString() { - return MyType.$toDebugString(this.$memoryAddress()); - } - private static native java.lang.String $toDebugString(long selfPointer); - """ - ] - ) - } - - @Test("JNI toDebugString (Swift)") - func toDebugString_swift() throws { - try assertOutput( - input: source, - .jni, - .swift, - detectChunkByInitialLines: 1, - expectedChunks: [ - """ - @_cdecl("Java_com_example_swift_MyType__00024toDebugString__J") - public func Java_com_example_swift_MyType__00024toDebugString__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jstring? { - ... - return String(reflecting: selfPointer$.pointee).getJNIValue(in: environment) + public String toDebugString() { + return SwiftObjects.toDebugString(this.$memoryAddress(), this.$typeMetadataAddress()); } """ ]