Skip to content
Draft
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
1 change: 0 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ let package = Package(
// ==== SwiftJava (i.e. calling Java directly Swift utilities)
.library(
name: "SwiftJava",
type: .dynamic,
targets: ["SwiftJava"]
),

Expand Down
4 changes: 1 addition & 3 deletions Samples/SwiftAndJavaJarSampleLib/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ let package = Package(
.target(
name: "MySwiftLibrary",
dependencies: [
.product(name: "SwiftJava", package: "swift-java"),
.product(name: "CSwiftJavaJNI", package: "swift-java"),
.product(name: "SwiftRuntimeFunctions", package: "swift-java")
.product(name: "SwiftRuntimeFunctions", package: "swift-java"),
],
exclude: [
"swift-java.config",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ public class ConcreteProtocolAB: ProtocolA, ProtocolB {
public let constantB: Int64
public var mutable: Int64 = 0

public init(constantA: Int64, constantB: Int64) {
self.constantA = constantA
self.constantB = constantB
}

public func name() -> String {
return "ConcreteProtocolAB"
}

public func makeClass() -> MySwiftClass {
return MySwiftClass(x: 10, y: 50)
}

public init(constantA: Int64, constantB: Int64) {
self.constantA = constantA
self.constantB = constantB
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ void protocolMethod() {
void protocolClassMethod() {
try (var arena = SwiftArena.ofConfined()) {
ProtocolA proto1 = ConcreteProtocolAB.init(10, 5, arena);
assertEquals(10, proto1.makeClass(arena).getX());
assertEquals(10, proto1.makeClass().getX());
}
}

Expand Down
2 changes: 0 additions & 2 deletions Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,6 @@ extension FFMSwift2JavaGenerator {
private static final boolean INITIALIZED_LIBS = initializeLibs();
static boolean initializeLibs() {
System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_CORE);
System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA);
System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_RUNTIME_FUNCTIONS);
System.loadLibrary(LIB_NAME);
return true;
Expand Down Expand Up @@ -346,7 +345,6 @@ extension FFMSwift2JavaGenerator {
private static SymbolLookup getSymbolLookup() {
if (SwiftLibraries.AUTO_LOAD_LIBS) {
System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_CORE);
System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA);
System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_RUNTIME_FUNCTIONS);
System.loadLibrary(LIB_NAME);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ extension JNISwift2JavaGenerator {
let protocolType: SwiftNominalType
let functions: [Function]
let variables: [Variable]
let importedType: ImportedNominalType

var wrapperName: String {
protocolType.nominalTypeDecl.javaInterfaceSwiftProtocolWrapperName
Expand Down Expand Up @@ -100,8 +99,7 @@ extension JNISwift2JavaGenerator {
return JavaInterfaceSwiftWrapper(
protocolType: SwiftNominalType(nominalTypeDecl: type.swiftNominal),
functions: functions,
variables: variables,
importedType: type
variables: variables
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ extension JNISwift2JavaGenerator {
static final String LIB_NAME = "\(swiftModuleName)";

static {
System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA);
System.loadLibrary(LIB_NAME);
}
"""
Expand Down Expand Up @@ -170,7 +169,6 @@ extension JNISwift2JavaGenerator {
@SuppressWarnings("unused")
private static final boolean INITIALIZED_LIBS = initializeLibs();
static boolean initializeLibs() {
System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA);
System.loadLibrary(LIB_NAME);
return true;
}
Expand Down Expand Up @@ -216,7 +214,7 @@ extension JNISwift2JavaGenerator {
}

public static \(decl.swiftNominal.name) wrapMemoryAddressUnsafe(long selfPointer) {
return new \(decl.swiftNominal.name)(selfPointer, SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA);
return new \(decl.swiftNominal.name)(selfPointer, SwiftMemoryManagement.GLOBAL_SWIFT_JAVA_ARENA);
}
"""
)
Expand Down Expand Up @@ -533,9 +531,17 @@ extension JNISwift2JavaGenerator {
// If we have enabled javaCallbacks we must emit default
// arena methods for protocols, as this is what
// Swift will call into, when you call a interface from Swift.
let shouldGenerateGlobalArenaVariation = config.effectiveMemoryManagementMode.requiresGlobalArena && translatedSignature.requiresSwiftArena
let shouldGenerateGlobalArenaVariation: Bool
let isParentProtocol = importedFunc?.parentType?.asNominalType?.isProtocol ?? false

if config.effectiveMemoryManagementMode.requiresGlobalArena && translatedSignature.requiresSwiftArena {
shouldGenerateGlobalArenaVariation = true
} else if isParentProtocol, translatedSignature.requiresSwiftArena, config.effectiveEnableJavaCallbacks {
shouldGenerateGlobalArenaVariation = true
} else {
shouldGenerateGlobalArenaVariation = false
}

if shouldGenerateGlobalArenaVariation {
if let importedFunc {
printDeclDocumentation(&printer, importedFunc)
Expand All @@ -548,7 +554,7 @@ extension JNISwift2JavaGenerator {
}

printer.printBraceBlock("\(annotationsStr)\(modifiers.joined(separator: " ")) \(resultType) \(translatedDecl.name)(\(parametersStr))\(throwsClause)") { printer in
let globalArenaName = "SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA"
let globalArenaName = "SwiftMemoryManagement.GLOBAL_SWIFT_JAVA_ARENA"
let arguments = translatedDecl.translatedFunctionSignature.parameters.map(\.parameter.name) + [globalArenaName]
let call = "\(translatedDecl.name)(\(arguments.joined(separator: ", ")))"
if translatedDecl.translatedFunctionSignature.resultType.javaType.isVoid {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1317,9 +1317,5 @@ extension JNISwift2JavaGenerator {

// FIXME: Remove once we support protocol variables
case protocolVariablesNotSupported

/// We cannot generate interface wrappers for
/// protocols that we unable to be jextracted.
case protocolWasNotExtracted
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,9 @@ extension JNISwift2JavaGenerator {
printer.print("var \(translatedWrapper.javaInterfaceVariableName): \(translatedWrapper.javaInterfaceName) { get }")
}
printer.println()
try printer.printBraceBlock("extension \(translatedWrapper.wrapperName)") { printer in
printer.printBraceBlock("extension \(translatedWrapper.wrapperName)") { printer in
for function in translatedWrapper.functions {
try printInterfaceWrapperFunctionImpl(&printer, function, inside: translatedWrapper)
printInterfaceWrapperFunctionImpl(&printer, function, inside: translatedWrapper)
printer.println()
}

Expand All @@ -142,29 +142,16 @@ extension JNISwift2JavaGenerator {
_ printer: inout CodePrinter,
_ function: JavaInterfaceSwiftWrapper.Function,
inside wrapper: JavaInterfaceSwiftWrapper
) throws {
guard let protocolMethod = wrapper.importedType.methods.first(where: { $0.functionSignature == function.originalFunctionSignature }) else {
fatalError("Failed to find protocol method")
}
guard let translatedDecl = self.translatedDecl(for: protocolMethod) else {
throw JavaTranslationError.protocolWasNotExtracted
}

) {
printer.printBraceBlock(function.swiftDecl.signatureString) { printer in
var upcallArguments = zip(
let upcallArguments = zip(
function.originalFunctionSignature.parameters,
function.parameterConversions
).map { param, conversion in
// Wrap-java does not extract parameter names, so no labels
conversion.render(&printer, param.parameterName!)
}

// If the underlying translated method requires
// a SwiftArena, we pass in the global arena
if translatedDecl.translatedFunctionSignature.requiresSwiftArena {
upcallArguments.append("JavaSwiftArena.defaultAutoArena")
}

let tryClause = function.originalFunctionSignature.isThrowing ? "try " : ""
let javaUpcall = "\(tryClause)\(wrapper.javaInterfaceVariableName).\(function.swiftFunctionName)(\(upcallArguments.joined(separator: ", ")))"

Expand Down Expand Up @@ -201,6 +188,8 @@ extension JNISwift2JavaGenerator {
private func printGlobalSwiftThunkSources(_ printer: inout CodePrinter) throws {
printHeader(&printer)

printJNIOnLoad(&printer)

for decl in analysis.importedGlobalFuncs {
printSwiftFunctionThunk(&printer, decl)
printer.println()
Expand All @@ -212,6 +201,18 @@ extension JNISwift2JavaGenerator {
}
}

private func printJNIOnLoad(_ printer: inout CodePrinter) {
printer.print(
"""
@_cdecl("JNI_OnLoad")
func JNI_OnLoad(javaVM: JavaVMPointer, reserved: UnsafeMutableRawPointer) -> jint {
SwiftJavaRuntimeSupport._JNI_OnLoad(javaVM, reserved)
return JNI_VERSION_1_6
}
"""
)
}

private func printNominalTypeThunks(_ printer: inout CodePrinter, _ type: ImportedNominalType) throws {
printHeader(&printer)

Expand Down
56 changes: 17 additions & 39 deletions Sources/SwiftJava/AnyJavaObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,6 @@ extension AnyJavaObject {
return String(seq)
}

/// The mangled name for this java class
public static var mangledName: String {
"L\(fullJavaClassNameWithSlashes);"
}

/// Initialize a Java object from its instance.
public init(javaThis: jobject, environment: JNIEnvironment) {
self.init(javaHolder: JavaObjectHolder(object: javaThis, environment: environment))
Expand All @@ -102,41 +97,23 @@ extension AnyJavaObject {
in environment: JNIEnvironment,
_ body: (jclass) throws -> Result
) throws -> Result {
do {
let resolvedClass = try environment.translatingJNIExceptions {
environment.interface.FindClass(
environment,
fullJavaClassNameWithSlashes
)
}!
return try body(resolvedClass)
} catch {
// If we are in a Java environment where we have loaded
// SwiftJava dynamically, we have access to the application class loader
// so lets try that as as a fallback
if let applicationClassLoader = JNI.shared?.applicationClassLoader {
return try _withJNIClassFromCustomClassLoader(
applicationClassLoader,
in: environment
) { applicationLoadedClass in
return try body(applicationLoadedClass)
}
} else {
throw error
}
}
let resolvedClass = try environment.translatingJNIExceptions {
environment.interface.FindClass(
environment,
fullJavaClassNameWithSlashes
)
}!
return try body(resolvedClass)
}

/// Retrieve the Java class for this type using a specific class loader.
private static func _withJNIClassFromCustomClassLoader<Result>(
_ classLoader: JavaClassLoader,
in environment: JNIEnvironment,
_ body: (jclass) throws -> Result
_ body: (jclass?) throws -> Result
) throws -> Result {
let resolvedClass = try classLoader.findClass(fullJavaClassName)
// OK to force unwrap, as classLoader will throw ClassNotFoundException
// if the class cannot be found.
return try body(resolvedClass!.javaThis)
let resolvedClass = try? classLoader.findClass(fullJavaClassName)
return try body(resolvedClass?.javaThis)
}

/// Retrieve the Java class for this type and execute body().
Expand All @@ -147,15 +124,16 @@ extension AnyJavaObject {
) throws -> Result {
if let AnyJavaObjectWithCustomClassLoader = self as? AnyJavaObjectWithCustomClassLoader.Type,
let customClassLoader = try AnyJavaObjectWithCustomClassLoader.getJavaClassLoader(in: environment) {
do {
return try _withJNIClassFromCustomClassLoader(customClassLoader, in: environment) { clazz in
return try body(clazz)
try _withJNIClassFromCustomClassLoader(customClassLoader, in: environment) { clazz in
guard let clazz else {
// If the custom class loader did not find the class
// let's look in the default class loader.
return try _withJNIClassFromDefaultClassLoader(in: environment, body)
}
} catch {
return try _withJNIClassFromDefaultClassLoader(in: environment, body)
return try body(clazz)
}
} else {
return try _withJNIClassFromDefaultClassLoader(in: environment, body)
try _withJNIClassFromDefaultClassLoader(in: environment, body)
}
}
}
47 changes: 0 additions & 47 deletions Sources/SwiftJava/JNI.swift

This file was deleted.

2 changes: 1 addition & 1 deletion Sources/SwiftJava/JVM/JavaVirtualMachine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ extension JavaVirtualMachine {
/// TODO: If the use of the lock itself ends up being slow, we could
/// use an atomic here instead because our access pattern is fairly
/// simple.
static let sharedJVM: LockedState<JavaVirtualMachine?> = .init(initialState: nil)
private static let sharedJVM: LockedState<JavaVirtualMachine?> = .init(initialState: nil)

/// Access the shared Java Virtual Machine instance.
///
Expand Down
31 changes: 31 additions & 0 deletions Sources/SwiftJavaRuntimeSupport/JNI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//===----------------------------------------------------------------------===//
//
// 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 SwiftJava
import CSwiftJavaJNI

final class JNI {
static var shared: JNI!

let applicationClassLoader: JavaClassLoader

init(fromVM javaVM: JavaVirtualMachine) {
self.applicationClassLoader = try! JavaClass<JavaThread>(environment: javaVM.environment()).currentThread().getContextClassLoader()
}
}

// Called by generated code, and not automatically by Java.
public func _JNI_OnLoad(_ javaVM: JavaVMPointer, _ reserved: UnsafeMutableRawPointer) {
JNI.shared = JNI(fromVM: JavaVirtualMachine(adoptingJVM: javaVM))
}
Loading
Loading