Skip to content

Commit acff3ee

Browse files
madsodgaardktoso
andauthored
jextract: pass arena from Swift instead of default interface for callbacks (#489)
Co-authored-by: Konrad `ktoso` Malawski <[email protected]>
1 parent 9a2b40b commit acff3ee

File tree

12 files changed

+90
-47
lines changed

12 files changed

+90
-47
lines changed

Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/ProtocolTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ void protocolMethod() {
7777
void protocolClassMethod() {
7878
try (var arena = SwiftArena.ofConfined()) {
7979
ProtocolA proto1 = ConcreteProtocolAB.init(10, 5, arena);
80-
assertEquals(10, proto1.makeClass().getX());
80+
assertEquals(10, proto1.makeClass(arena).getX());
8181
}
8282
}
8383

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+InterfaceWrapperGeneration.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ extension JNISwift2JavaGenerator {
4242
let protocolType: SwiftNominalType
4343
let functions: [Function]
4444
let variables: [Variable]
45+
let importedType: ImportedNominalType
4546

4647
var wrapperName: String {
4748
protocolType.nominalTypeDecl.javaInterfaceSwiftProtocolWrapperName
@@ -99,7 +100,8 @@ extension JNISwift2JavaGenerator {
99100
return JavaInterfaceSwiftWrapper(
100101
protocolType: SwiftNominalType(nominalTypeDecl: type.swiftNominal),
101102
functions: functions,
102-
variables: variables
103+
variables: variables,
104+
importedType: type
103105
)
104106
}
105107

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ extension JNISwift2JavaGenerator {
214214
}
215215
216216
public static \(decl.swiftNominal.name) wrapMemoryAddressUnsafe(long selfPointer) {
217-
return new \(decl.swiftNominal.name)(selfPointer, SwiftMemoryManagement.GLOBAL_SWIFT_JAVA_ARENA);
217+
return new \(decl.swiftNominal.name)(selfPointer, SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA);
218218
}
219219
"""
220220
)
@@ -531,17 +531,9 @@ extension JNISwift2JavaGenerator {
531531
// If we have enabled javaCallbacks we must emit default
532532
// arena methods for protocols, as this is what
533533
// Swift will call into, when you call a interface from Swift.
534-
let shouldGenerateGlobalArenaVariation: Bool
534+
let shouldGenerateGlobalArenaVariation = config.effectiveMemoryManagementMode.requiresGlobalArena && translatedSignature.requiresSwiftArena
535535
let isParentProtocol = importedFunc?.parentType?.asNominalType?.isProtocol ?? false
536536

537-
if config.effectiveMemoryManagementMode.requiresGlobalArena && translatedSignature.requiresSwiftArena {
538-
shouldGenerateGlobalArenaVariation = true
539-
} else if isParentProtocol, translatedSignature.requiresSwiftArena, config.effectiveEnableJavaCallbacks {
540-
shouldGenerateGlobalArenaVariation = true
541-
} else {
542-
shouldGenerateGlobalArenaVariation = false
543-
}
544-
545537
if shouldGenerateGlobalArenaVariation {
546538
if let importedFunc {
547539
printDeclDocumentation(&printer, importedFunc)
@@ -554,7 +546,7 @@ extension JNISwift2JavaGenerator {
554546
}
555547

556548
printer.printBraceBlock("\(annotationsStr)\(modifiers.joined(separator: " ")) \(resultType) \(translatedDecl.name)(\(parametersStr))\(throwsClause)") { printer in
557-
let globalArenaName = "SwiftMemoryManagement.GLOBAL_SWIFT_JAVA_ARENA"
549+
let globalArenaName = "SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA"
558550
let arguments = translatedDecl.translatedFunctionSignature.parameters.map(\.parameter.name) + [globalArenaName]
559551
let call = "\(translatedDecl.name)(\(arguments.joined(separator: ", ")))"
560552
if translatedDecl.translatedFunctionSignature.resultType.javaType.isVoid {

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,5 +1317,9 @@ extension JNISwift2JavaGenerator {
13171317

13181318
// FIXME: Remove once we support protocol variables
13191319
case protocolVariablesNotSupported
1320+
1321+
/// We cannot generate interface wrappers for
1322+
/// protocols that we unable to be jextracted.
1323+
case protocolWasNotExtracted
13201324
}
13211325
}

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,9 @@ extension JNISwift2JavaGenerator {
124124
printer.print("var \(translatedWrapper.javaInterfaceVariableName): \(translatedWrapper.javaInterfaceName) { get }")
125125
}
126126
printer.println()
127-
printer.printBraceBlock("extension \(translatedWrapper.wrapperName)") { printer in
127+
try printer.printBraceBlock("extension \(translatedWrapper.wrapperName)") { printer in
128128
for function in translatedWrapper.functions {
129-
printInterfaceWrapperFunctionImpl(&printer, function, inside: translatedWrapper)
129+
try printInterfaceWrapperFunctionImpl(&printer, function, inside: translatedWrapper)
130130
printer.println()
131131
}
132132

@@ -142,16 +142,29 @@ extension JNISwift2JavaGenerator {
142142
_ printer: inout CodePrinter,
143143
_ function: JavaInterfaceSwiftWrapper.Function,
144144
inside wrapper: JavaInterfaceSwiftWrapper
145-
) {
145+
) throws {
146+
guard let protocolMethod = wrapper.importedType.methods.first(where: { $0.functionSignature == function.originalFunctionSignature }) else {
147+
fatalError("Failed to find protocol method")
148+
}
149+
guard let translatedDecl = self.translatedDecl(for: protocolMethod) else {
150+
throw JavaTranslationError.protocolWasNotExtracted
151+
}
152+
146153
printer.printBraceBlock(function.swiftDecl.signatureString) { printer in
147-
let upcallArguments = zip(
154+
var upcallArguments = zip(
148155
function.originalFunctionSignature.parameters,
149156
function.parameterConversions
150157
).map { param, conversion in
151158
// Wrap-java does not extract parameter names, so no labels
152159
conversion.render(&printer, param.parameterName!)
153160
}
154161

162+
// If the underlying translated method requires
163+
// a SwiftArena, we pass in the global arena
164+
if translatedDecl.translatedFunctionSignature.requiresSwiftArena {
165+
upcallArguments.append("JNI.shared.defaultAutoArena")
166+
}
167+
155168
let tryClause = function.originalFunctionSignature.isThrowing ? "try " : ""
156169
let javaUpcall = "\(tryClause)\(wrapper.javaInterfaceVariableName).\(function.swiftFunctionName)(\(upcallArguments.joined(separator: ", ")))"
157170

Sources/SwiftJava/AnyJavaObject.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ extension AnyJavaObject {
7777
return String(seq)
7878
}
7979

80+
/// The mangled name for this java class
81+
public static var mangledName: String {
82+
"L\(fullJavaClassNameWithSlashes);"
83+
}
84+
8085
/// Initialize a Java object from its instance.
8186
public init(javaThis: jobject, environment: JNIEnvironment) {
8287
self.init(javaHolder: JavaObjectHolder(object: javaThis, environment: environment))

Sources/SwiftJavaRuntimeSupport/JNI.swift

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,38 @@
1515
import SwiftJava
1616
import CSwiftJavaJNI
1717

18-
final class JNI {
19-
static var shared: JNI!
18+
/// A type that represents the shared JNI environment
19+
/// used to share any global JNI variables.
20+
///
21+
/// This is initialized when the `JNI_OnLoad` is triggered,
22+
/// which happens when you call `System.loadLibrary(...)`
23+
/// from Java.
24+
public final class JNI {
25+
/// The shared JNI object, initialized by `JNI_OnLoad`
26+
public fileprivate(set) static var shared: JNI!
2027

21-
let applicationClassLoader: JavaClassLoader
28+
/// The default application class loader
29+
public let applicationClassLoader: JavaClassLoader
30+
31+
/// The default auto arena of SwiftKitCore
32+
public let defaultAutoArena: JavaSwiftArena
2233

2334
init(fromVM javaVM: JavaVirtualMachine) {
24-
self.applicationClassLoader = try! JavaClass<JavaThread>(environment: javaVM.environment()).currentThread().getContextClassLoader()
35+
let environment = try! javaVM.environment()
36+
37+
self.applicationClassLoader = try! JavaClass<JavaThread>(environment: environment).currentThread().getContextClassLoader()
38+
39+
// Find global arena
40+
let swiftMemoryClass = environment.interface.FindClass(environment, "org/swift/swiftkit/core/SwiftMemoryManagement")!
41+
let arenaFieldID = environment.interface.GetStaticFieldID(
42+
environment,
43+
swiftMemoryClass,
44+
"DEFAULT_SWIFT_JAVA_AUTO_ARENA",
45+
JavaSwiftArena.mangledName
46+
)
47+
let localObject = environment.interface.GetStaticObjectField(environment, swiftMemoryClass, arenaFieldID)!
48+
self.defaultAutoArena = JavaSwiftArena(javaThis: localObject, environment: environment)
49+
environment.interface.DeleteLocalRef(environment, localObject)
2550
}
2651
}
2752

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import SwiftJava
16+
17+
@JavaInterface("org.swift.swiftkit.core.SwiftArena")
18+
public struct JavaSwiftArena {}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"classes" : {
3+
"org.swift.swiftkit.core.JNISwiftInstance" : "JavaJNISwiftInstance",
4+
"org.swift.swiftkit.core.SwiftArena" : "JavaSwiftArena"
5+
}
6+
}

SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftMemoryManagement.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@
1515
package org.swift.swiftkit.core;
1616

1717
public class SwiftMemoryManagement {
18-
public static final SwiftArena GLOBAL_SWIFT_JAVA_ARENA = SwiftArena.ofAuto();
18+
public static final SwiftArena DEFAULT_SWIFT_JAVA_AUTO_ARENA = SwiftArena.ofAuto();
1919
}

0 commit comments

Comments
 (0)