Skip to content
Open
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
19 changes: 19 additions & 0 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,13 @@ jobs:
perl -pi -e "s#(download_and_extract_toolchain \".ANDROID_SDK_TAG\"\))#\1\n PATH=\\\$(dirname \\\$SWIFT_EXECUTABLE_FOR_ANDROID_SDK):\\\$PATH\n echo \"path is now \\\$PATH\"#" install-and-build-with-sdk.sh
chmod 500 install-and-build-with-sdk.sh
./install-and-build-with-sdk.sh --android --build-command="swift build" --android-sdk-triple="${{ matrix.sdk_triple }}" --android-ndk-version="${{ matrix.ndk_version }}" ${{ matrix.swift_version }}
- name: Verify Android JNI consumer with C++ interoperability
if: matrix.swift_version == '6.3' && matrix.sdk_triple == 'aarch64-unknown-linux-android28' && matrix.ndk_version == 'r27d'
working-directory: Samples/SwiftJavaExtractJNISampleApp
env:
CXX_INTEROP: '1'
SWIFT_JAVA_VERBOSE: true
run: ./install-and-build-with-sdk.sh --android --build-command="swift build" --android-sdk-triple="${{ matrix.sdk_triple }}" --android-ndk-version="${{ matrix.ndk_version }}" ${{ matrix.swift_version }}

verify-samples:
name: Sample ${{ matrix.sample_app }} (${{ matrix.os_version }} swift:${{ matrix.swift_version }} jdk:${{matrix.jdk_vendor}})
Expand Down Expand Up @@ -296,6 +303,12 @@ jobs:
${{ runner.os }}-swift${{ matrix.swift_version }}-sample-
- name: "Verify sample: ${{ matrix.sample_app }}"
run: .github/scripts/validate_sample.sh Samples/${{ matrix.sample_app }}
- name: Verify JNI consumer with C++ interoperability
if: matrix.swift_version == '6.3' && (matrix.sample_app == 'JavaKitSampleApp' || matrix.sample_app == 'SwiftJavaExtractJNISampleApp')
env:
CXX_INTEROP: '1'
SWIFT_JAVA_VERBOSE: true
run: .github/scripts/validate_sample.sh Samples/${{ matrix.sample_app }}


verify-samples-macos:
Expand Down Expand Up @@ -335,6 +348,12 @@ jobs:
${{ runner.os }}-swift${{ matrix.swift_version }}-sample-
- name: "Verify sample ${{ matrix.sample_app }}"
run: .github/scripts/validate_sample.sh Samples/${{ matrix.sample_app }}
- name: Verify JNI consumer with C++ interoperability
if: matrix.swift_version == '6.3' && (matrix.sample_app == 'JavaKitSampleApp' || matrix.sample_app == 'SwiftJavaExtractJNISampleApp')
env:
CXX_INTEROP: '1'
SWIFT_JAVA_VERBOSE: true
run: .github/scripts/validate_sample.sh Samples/${{ matrix.sample_app }}

linkage-test:
name: Linkage test (noble swift:6.3)
Expand Down
13 changes: 7 additions & 6 deletions Samples/JavaKitSampleApp/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
import CompilerPluginSupport
import PackageDescription

// Enable C++ interoperability only in the consumer targets, not their dependencies.
let sampleSwiftSettings: [SwiftSetting] =
[.swiftLanguageMode(.v5)]
+ (Context.environment["CXX_INTEROP"] == "1" ? [.interoperabilityMode(.Cxx)] : [])

let package = Package(
name: "JavaKitSampleApp",
platforms: [
Expand Down Expand Up @@ -35,9 +40,7 @@ let package = Package(
.product(name: "JavaUtilJar", package: "swift-java"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
],
swiftSettings: [
.swiftLanguageMode(.v5)
],
swiftSettings: sampleSwiftSettings,
plugins: [
.plugin(name: "JavaCompilerPlugin", package: "swift-java"),
.plugin(name: "SwiftJavaPlugin", package: "swift-java"),
Expand All @@ -49,9 +52,7 @@ let package = Package(
dependencies: [
"JavaKitExample"
],
swiftSettings: [
.swiftLanguageMode(.v5)
]
swiftSettings: sampleSwiftSettings
),
]
)
13 changes: 7 additions & 6 deletions Samples/SwiftJavaExtractJNISampleApp/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
import CompilerPluginSupport
import PackageDescription

// Enable C++ interoperability only in the consumer targets, not their dependencies.
let sampleSwiftSettings: [SwiftSetting] =
[.swiftLanguageMode(.v5)]
+ (Context.environment["CXX_INTEROP"] == "1" ? [.interoperabilityMode(.Cxx)] : [])

let package = Package(
name: "JExtractJNISampleApp",
platforms: [
Expand Down Expand Up @@ -34,9 +39,7 @@ let package = Package(
exclude: [
"swift-java.config"
],
swiftSettings: [
.swiftLanguageMode(.v5)
],
swiftSettings: sampleSwiftSettings,
plugins: [
.plugin(name: "JExtractSwiftPlugin", package: "swift-java")
]
Expand All @@ -50,9 +53,7 @@ let package = Package(
exclude: [
"swift-java.config"
],
swiftSettings: [
.swiftLanguageMode(.v5)
],
swiftSettings: sampleSwiftSettings,
plugins: [
.plugin(name: "JExtractSwiftPlugin", package: "swift-java")
]
Expand Down
7 changes: 5 additions & 2 deletions Samples/SwiftJavaExtractJNISampleApp/ci-validate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,8 @@ fi
# FIXME: disable prebuilts until prebuilt swift-syntax isn't broken on 6.2 anymore: https://github.com/swiftlang/swift-java/issues/418
swift build $DISABLE_EXPERIMENTAL_PREBUILTS --disable-sandbox

./gradlew run
./gradlew test
# A reused daemon can resolve `swift` using the PATH from an older toolchain.
./gradlew --no-daemon run
# Gradle does not track the Swift library as a test input. Rerun tests when
# switching C++ interoperability so the second configuration is exercised too.
./gradlew --no-daemon test --rerun-tasks
Original file line number Diff line number Diff line change
Expand Up @@ -1515,8 +1515,8 @@ extension JNISwift2JavaGenerator {
let typeMetadataVariableName = "\(inner)typeMetadata$"
printer.print(
"""
let \(pointerVariableName) = environment.interface.CallLongMethodA(environment, \(inner), _JNIMethodIDCache.JNISwiftInstance.memoryAddress, [])
let \(typeMetadataVariableName) = environment.interface.CallLongMethodA(environment, \(inner), _JNIMethodIDCache.JNISwiftInstance.typeMetadataAddress, [])
let \(pointerVariableName) = environment.swiftInterface.CallLongMethodA(environment, \(inner), _JNIMethodIDCache.JNISwiftInstance.memoryAddress, [])
let \(typeMetadataVariableName) = environment.swiftInterface.CallLongMethodA(environment, \(inner), _JNIMethodIDCache.JNISwiftInstance.typeMetadataAddress, [])
"""
)
let existentialName = NativeSwiftConversionStep.extractSwiftProtocolValue(
Expand All @@ -1533,7 +1533,7 @@ extension JNISwift2JavaGenerator {
// or if its a custom class implementing the interface.
if allowsJavaImplementations {
printer.printBraceBlock(
"if environment.interface.IsInstanceOf(environment, \(inner), _JNIMethodIDCache.JNISwiftInstance.class) != 0"
"if environment.swiftInterface.IsInstanceOf(environment, \(inner), _JNIMethodIDCache.JNISwiftInstance.class) != 0"
) { printer in
printStandardJExtractBlock(&printer)
}
Expand Down Expand Up @@ -1702,15 +1702,15 @@ extension JNISwift2JavaGenerator {

printer.print(
"""
let class$ = environment.interface.GetObjectClass(environment, \(placeholder))
let methodID$ = environment.interface.GetMethodID(environment, class$, "\(methodName)", "\(methodSignature.mangledName)")!
environment.interface.DeleteLocalRef(environment, class$)
let arguments$: [jvalue] = [\(arguments.joined(separator: .comma))]
let class$ = environment.swiftInterface.GetObjectClass(environment, \(placeholder))
let methodID$ = environment.swiftInterface.GetMethodID(environment, class$, "\(methodName)", "\(methodSignature.mangledName)")!
environment.swiftInterface.DeleteLocalRef(environment, class$)
let arguments$: [JNITypes.jvalue] = [\(arguments.joined(separator: .comma))]
"""
)

let upcall =
"environment.interface.\(nativeResult.javaType.jniCallMethodAName)(environment, \(placeholder), methodID$, arguments$)"
"environment.swiftInterface.\(nativeResult.javaType.jniCallMethodAName)(environment, \(placeholder), methodID$, arguments$)"
let result = nativeResult.conversion.render(&printer, upcall)

if nativeResult.javaType.isVoid {
Expand Down Expand Up @@ -1818,7 +1818,7 @@ extension JNISwift2JavaGenerator {
let discriminatorParameterName
):
if !returnType.isVoid {
printer.print("let \(resultName): \(returnType.jniTypeName)")
printer.print("let \(resultName): \(returnType.swiftJNITypeName)")
}
printer.printIfBlock("let innerResult$ = \(placeholder)") { printer in
let inner = inner.render(&printer, "innerResult$")
Expand All @@ -1828,7 +1828,7 @@ extension JNISwift2JavaGenerator {
printer.print(
"""
var flag$ = Int8(1)
environment.interface.SetByteArrayRegion(environment, \(discriminatorParameterName), 0, 1, &flag$)
environment.swiftInterface.SetByteArrayRegion(environment, \(discriminatorParameterName), 0, 1, &flag$)
"""
)
}
Expand All @@ -1839,7 +1839,7 @@ extension JNISwift2JavaGenerator {
printer.print(
"""
var flag$ = Int8(0)
environment.interface.SetByteArrayRegion(environment, \(discriminatorParameterName), 0, 1, &flag$)
environment.swiftInterface.SetByteArrayRegion(environment, \(discriminatorParameterName), 0, 1, &flag$)
"""
)
}
Expand All @@ -1854,10 +1854,10 @@ extension JNISwift2JavaGenerator {
printer.printBraceBlock("do") { printer in
printer.print(
"""
environment.interface.SetLongField(environment, \(outArgumentName), _JNIMethodIDCache._OutSwiftGenericInstance.selfPointer, \(inner))
environment.swiftInterface.SetLongField(environment, \(outArgumentName), _JNIMethodIDCache._OutSwiftGenericInstance.selfPointer, \(inner))
let metadataPointer = unsafeBitCast(\(swiftFunctionResultType).self, to: UnsafeRawPointer.self)
let metadataPointerBits$ = Int64(Int(bitPattern: metadataPointer))
environment.interface.SetLongField(environment, \(outArgumentName), _JNIMethodIDCache._OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment))
environment.swiftInterface.SetLongField(environment, \(outArgumentName), _JNIMethodIDCache._OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment))
"""
)
}
Expand All @@ -1869,8 +1869,8 @@ extension JNISwift2JavaGenerator {
printer.print(
"""
let (selfPointerBits$, selfTypePointerBits$) = \(boxed)
environment.interface.SetLongField(environment, \(outArgumentName), _JNIMethodIDCache._OutSwiftGenericInstance.selfPointer, selfPointerBits$.getJNIValue(in: environment))
environment.interface.SetLongField(environment, \(outArgumentName), _JNIMethodIDCache._OutSwiftGenericInstance.selfTypePointer, selfTypePointerBits$.getJNIValue(in: environment))
environment.swiftInterface.SetLongField(environment, \(outArgumentName), _JNIMethodIDCache._OutSwiftGenericInstance.selfPointer, selfPointerBits$.getJNIValue(in: environment))
environment.swiftInterface.SetLongField(environment, \(outArgumentName), _JNIMethodIDCache._OutSwiftGenericInstance.selfTypePointer, selfTypePointerBits$.getJNIValue(in: environment))
"""
)
}
Expand Down Expand Up @@ -1941,20 +1941,20 @@ extension JNISwift2JavaGenerator {
// Global ref all indirect returns
for outParameter in nativeFunctionSignature.result.outParameters {
printer.print(
"nonisolated(unsafe) let \(outParameter.name) = environment.interface.NewGlobalRef(environment, \(outParameter.name))"
"nonisolated(unsafe) let \(outParameter.name) = environment.swiftInterface.NewGlobalRef(environment, \(outParameter.name))"
)
globalRefs.append(outParameter.name)
}

// We also need to global ref any objects passed in
for parameter in nativeFunctionSignature.parameters.flatMap(\.parameters) where !parameter.type.isPrimitive {
printer.print("nonisolated(unsafe) let \(parameter.name) = environment.interface.NewGlobalRef(environment, \(parameter.name))")
printer.print("nonisolated(unsafe) let \(parameter.name) = environment.swiftInterface.NewGlobalRef(environment, \(parameter.name))")
globalRefs.append(parameter.name)
}

printer.print(
"""
nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future)
nonisolated(unsafe) let globalFuture = environment.swiftInterface.NewGlobalRef(environment, result_future)
"""
)

Expand Down Expand Up @@ -1993,7 +1993,7 @@ extension JNISwift2JavaGenerator {
printer.print("\(tryAwaitString) \(placeholderWithoutTry)")
printer.print("environment = try! JavaVirtualMachine.shared().environment()")
printer.print(
"_ = environment.interface.CallBooleanMethodA(environment, globalFuture, \(completeMethodID), [jvalue(l: nil)])"
"_ = environment.swiftInterface.CallBooleanMethodA(environment, globalFuture, \(completeMethodID), [JNITypes.objectValue(nil)])"
)
} else {
printer.print("let swiftResult$ = \(tryAwaitString) \(placeholderWithoutTry)")
Expand All @@ -2013,7 +2013,7 @@ extension JNISwift2JavaGenerator {
}

printer.print(
"_ = environment.interface.CallBooleanMethodA(environment, globalFuture, \(completeMethodID), [jvalue(l: \(result))])"
"_ = environment.swiftInterface.CallBooleanMethodA(environment, globalFuture, \(completeMethodID), [JNITypes.objectValue(\(result))])"
)
}
}
Expand All @@ -2026,7 +2026,7 @@ extension JNISwift2JavaGenerator {
// Defer might on any thread, so we need to attach environment.
printer.print("let deferEnvironment = try! JavaVirtualMachine.shared().environment()")
for globalRef in globalRefs {
printer.print("deferEnvironment.interface.DeleteGlobalRef(deferEnvironment, \(globalRef))")
printer.print("deferEnvironment.swiftInterface.DeleteGlobalRef(deferEnvironment, \(globalRef))")
}
}
if isThrowing {
Expand All @@ -2039,7 +2039,7 @@ extension JNISwift2JavaGenerator {
"""
let catchEnvironment = try! JavaVirtualMachine.shared().environment()
let exception = catchEnvironment.makeJavaThrowable(for: error)
_ = catchEnvironment.interface.CallBooleanMethodA(catchEnvironment, globalFuture, \(completeExceptionallyMethodID), [jvalue(l: exception)])
_ = catchEnvironment.swiftInterface.CallBooleanMethodA(catchEnvironment, globalFuture, \(completeExceptionallyMethodID), [JNITypes.objectValue(exception)])
"""
)
}
Expand Down Expand Up @@ -2107,9 +2107,9 @@ extension JNISwift2JavaGenerator {
let releaseMode = mutable ? "0" : "jint(JNI_ABORT)"
printer.print(
"""
let \(countVar) = Int(environment.interface.GetArrayLength(environment, \(inner)))
let \(ptrVar) = environment.interface.GetByteArrayElements(environment, \(inner), nil)!
defer { environment.interface.ReleaseByteArrayElements(environment, \(inner), \(ptrVar), \(releaseMode)) }
let \(countVar) = Int(environment.swiftInterface.GetArrayLength(environment, \(inner)))
let \(ptrVar) = environment.swiftInterface.GetByteArrayElements(environment, \(inner), nil)!
defer { environment.swiftInterface.ReleaseByteArrayElements(environment, \(inner), \(ptrVar), \(releaseMode)) }
let \(rbpVar) = \(bufferPointerType)(start: \(ptrVar), count: \(countVar))
"""
)
Expand Down Expand Up @@ -2138,12 +2138,12 @@ extension JNISwift2JavaGenerator {
let setMethodName = element.javaType.jniSetArrayRegionMethodName
printer.print("var element_\(element.index)_jni$ = \(converted)")
printer.print(
"environment.interface.\(setMethodName)(environment, \(element.outParamName), 0, 1, &element_\(element.index)_jni$)"
"environment.swiftInterface.\(setMethodName)(environment, \(element.outParamName), 0, 1, &element_\(element.index)_jni$)"
)
case .class, .array:
printer.print("let element_\(element.index)_jni$ = \(converted)")
printer.print(
"environment.interface.SetObjectArrayElement(environment, \(element.outParamName), 0, element_\(element.index)_jni$)"
"environment.swiftInterface.SetObjectArrayElement(environment, \(element.outParamName), 0, element_\(element.index)_jni$)"
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -892,15 +892,17 @@ extension JNISwift2JavaGenerator {
self.generatedCDeclSymbolNames.append(cName)

let translatedParameters = parameters.map {
"\($0.name): \($0.type.jniTypeName)"
"\($0.name): \($0.type.swiftJNITypeName)"
}

// Use the Swift alias defined in the runtime's C import context. Importing
// Android's JNIEnv directly in a C++-enabled client makes it a C++ struct.
let thunkParameters =
[
"environment: UnsafeMutablePointer<JNIEnv?>!",
"thisClass: jclass",
"environment: JNIEnvironment!",
"thisClass: JNITypes.jclass",
] + translatedParameters
let thunkReturnType = resultType != .void ? " -> \(resultType.jniTypeName)" : ""
let thunkReturnType = resultType != .void ? " -> \(resultType.swiftJNITypeName)" : ""

// TODO: Think about function overloads
printer.printBraceBlock(
Expand Down Expand Up @@ -944,7 +946,7 @@ extension JNISwift2JavaGenerator {
methods: [wrapMemoryAddressUnsafeMethod]
)

static var javaClass: jclass {
static var javaClass: JNITypes.jclass {
cache.javaClass
}

Expand All @@ -960,7 +962,7 @@ extension JNISwift2JavaGenerator {
private func printThrowableErrorConformance(_ printer: inout SwiftPrinter, _ type: ExtractedNominalType) {
let bridgeName = JNICaching.bridgeName(for: type)
printer.printBraceBlock("extension \(type.effectiveSwiftTypeName): _JNIThrowableError") { printer in
printer.printBraceBlock("public func _makeJavaThrowable(in environment: JNIEnvironment) -> jthrowable?") { printer in
printer.printBraceBlock("public func _makeJavaThrowable(in environment: JNIEnvironment) -> JNITypes.jthrowable?") { printer in
printer.print("\(bridgeName).toJavaObject(self, in: environment)")
}
}
Expand Down Expand Up @@ -995,7 +997,7 @@ extension JNISwift2JavaGenerator {
printer.printBraceBlock(bridgeDeclaration) { printer in
printer.print("typealias SwiftType = \(bridgedSwiftType)")
printer.println()
printer.printBraceBlock("static var javaClass: jclass") { printer in
printer.printBraceBlock("static var javaClass: JNITypes.jclass") { printer in
printer.print("\(cacheName).javaClass")
}
printer.println()
Expand Down Expand Up @@ -1167,15 +1169,15 @@ extension JNISwift2JavaGenerator {
let resultType = nativeSignature.result.javaType

let translatedParameters = parameters.map {
"\($0.name): \($0.type.jniTypeName)"
"\($0.name): \($0.type.swiftJNITypeName)"
}

let thunkParameters =
[
"environment: UnsafeMutablePointer<JNIEnv?>!",
"thisClass: jclass",
"environment: JNIEnvironment!",
"thisClass: JNITypes.jclass",
] + translatedParameters
let thunkReturnType = resultType != .void ? " -> \(resultType.jniTypeName)" : ""
let thunkReturnType = resultType != .void ? " -> \(resultType.swiftJNITypeName)" : ""

let signature = #"static func \#(decl.openerMethodName)(\#(thunkParameters.joined(separator: .comma)))\#(thunkReturnType)"#
if !skipMethodBody {
Expand Down
Loading
Loading