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
Original file line number Diff line number Diff line change
Expand Up @@ -398,11 +398,19 @@ extension FFMSwift2JavaGenerator {
}
switch knownType {
case .unsafePointer, .unsafeMutablePointer:
// FIXME: Implement
throw JavaTranslationError.unhandledType(swiftType)
case .unsafeBufferPointer, .unsafeMutableBufferPointer:
// FIXME: Implement
throw JavaTranslationError.unhandledType(swiftType)
return TranslatedParameter(
parameter: JavaParameter(name: parameterName, type: .javaForeignMemorySegment),
conversion: .placeholder
)
case .unsafeBufferPointer(let element), .unsafeMutableBufferPointer(let element):
let elementLayout = try CType(cdeclType: element).foreignValueLayout
return TranslatedParameter(
parameter: JavaParameter(name: parameterName, type: .javaForeignMemorySegment),
conversion: .commaSeparated([
.placeholder,
.constant("\(parameterName).byteSize() / \(elementLayout.description).byteSize()"),
])
)

case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer:
return TranslatedParameter(
Expand Down Expand Up @@ -739,11 +747,32 @@ extension FFMSwift2JavaGenerator {
break // Implemented as wrapper

case .unsafePointer, .unsafeMutablePointer:
// FIXME: Implement
throw JavaTranslationError.unhandledType(swiftType)
case .unsafeBufferPointer, .unsafeMutableBufferPointer:
// FIXME: Implement
throw JavaTranslationError.unhandledType(swiftType)

return TranslatedResult(
javaResultType: .javaForeignMemorySegment,
annotations: resultAnnotations,
outParameters: [],
conversion: .placeholder
)
case .unsafeBufferPointer(let element), .unsafeMutableBufferPointer(let element):
let elementLayout = try CType(cdeclType: element).foreignValueLayout
return TranslatedResult(
javaResultType: .javaForeignMemorySegment,
annotations: resultAnnotations,
outParameters: [
JavaParameter(name: "pointer", type: .javaForeignMemorySegment),
JavaParameter(name: "count", type: .long),
],
conversion: .method(
.readMemorySegment(.explodedName(component: "pointer"), as: .javaForeignMemorySegment),
methodName: "reinterpret",
arguments: [
.constant("result$_count.get(SwiftValueLayout.SWIFT_INT64, 0) * \(elementLayout.description).byteSize()")
],
withArena: false
)
)

case .string:
return TranslatedResult(
javaResultType: .javaLangString,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,23 @@ extension JNISwift2JavaGenerator {

case .foundationDate, .essentialsDate, .foundationData, .essentialsData, .foundationURL, .essentialsURL:
break // Handled as wrapped struct
case .unsafePointer, .unsafeMutablePointer:
return TranslatedParameter(
parameter: JavaParameter(
name: parameterName,
type: .long
),
conversion: .placeholder
)
case .unsafeBufferPointer, .unsafeMutableBufferPointer:
return TranslatedParameter(
parameter: JavaParameter(name: parameterName, type: .swiftBufferPointer),
conversion: .commaSeparated([
.member(.placeholder, field: "baseAddress"),
.member(.placeholder, field: "count"),
])
)


case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer:
return TranslatedParameter(
Expand Down Expand Up @@ -935,6 +952,25 @@ extension JNISwift2JavaGenerator {
),
)

case .unsafePointer, .unsafeMutablePointer:
return TranslatedResult(
javaType: .long,
nativeJavaType: .long,
annotations: resultAnnotations,
outParameters: [],
conversion: .placeholder,
)

case .unsafeBufferPointer, .unsafeMutableBufferPointer:
return TranslatedResult(
javaType: .swiftBufferPointer,
nativeJavaType: .void,
annotations: resultAnnotations,
outParameters: [.init(name: resultName, type: .swiftBufferPointer, allocation: .new)],
conversion: .constant(resultName),
)
//taenda todo: add support for UnsafeRawBufferPointer and UnsafeMutableRawBufferPointer

default:
guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType.kind, config: self.config) else {
throw JavaTranslationError.unsupportedSwiftType(swiftType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,35 @@ extension JNISwift2JavaGenerator {
elementType: element,
parameterName: parameterName
)
case .unsafePointer(let pointee), .unsafeMutablePointer(let pointee):
return NativeParameter(
parameters: [
JavaParameter(name: parameterName, type: .long)
],
conversion: .extractSwiftValue(.placeholder, swiftType: pointee),
indirectConversion: nil,
conversionCheck: nil
)

case .unsafeBufferPointer(let element), .unsafeMutableBufferPointer(let element):
let isMutable = knownType.kind == .unsafeMutableBufferPointer
let countParameterName = "\(parameterName)_count"
return NativeParameter(
parameters: [
JavaParameter(name: parameterName, type: .long),
JavaParameter(name: countParameterName, type: .long),
],
conversion: .constructUnsafeBufferPointer(
base: .extractSwiftValue(.placeholder, swiftType: element, allowNil: true),
count: .labelessInitializer(
.initFromJNI(.constant(countParameterName), swiftType: self.knownTypes.int64),
swiftType: self.knownTypes.int
),
mutable: isMutable
),
indirectConversion: nil,
conversionCheck: nil
)

case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer:
let isMutable = knownType.kind == .unsafeMutableRawBufferPointer
Expand Down Expand Up @@ -829,6 +858,25 @@ extension JNISwift2JavaGenerator {
outParameters: []
)

case .unsafePointer, .unsafeMutablePointer:
return NativeResult(
javaType: .long,
conversion: .getJNIValue(.convertToBitPattern(.placeholder)),
outParameters: []
)

case .unsafeBufferPointer, .unsafeMutableBufferPointer:
return NativeResult(
javaType: .void,
conversion: .bufferPointerIndirectReturn(.placeholder, outArgumentName: resultName + "Out"),
outParameters: [JavaParameter(name: resultName + "Out", type: .swiftBufferPointer)]
)
case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer:
return NativeResult(
javaType: .array(.byte),
conversion: .getJNIValue(.placeholder),
outParameters: []
)
default:
guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType.kind, config: self.config),
javaType.implementsJavaValue
Expand Down Expand Up @@ -1364,6 +1412,10 @@ extension JNISwift2JavaGenerator {
/// of the `Unsafe(Mutable)Pointer` types in Swift.
indirect case pointee(NativeSwiftConversionStep)

indirect case convertToBitPattern(NativeSwiftConversionStep)

indirect case constructUnsafeBufferPointer(base: NativeSwiftConversionStep, count: NativeSwiftConversionStep, mutable: Bool)

indirect case closureLowering(parameters: [NativeParameter], result: NativeResult)

/// Escaping closure lowering using the protocol infrastructure.
Expand Down Expand Up @@ -1408,6 +1460,11 @@ extension JNISwift2JavaGenerator {
outArgumentName: String
)

indirect case bufferPointerIndirectReturn(
NativeSwiftConversionStep,
outArgumentName: String
)

indirect case constructor(
_ swiftType: SwiftType,
arguments: [(String?, NativeSwiftConversionStep)] = []
Expand Down Expand Up @@ -1629,6 +1686,10 @@ extension JNISwift2JavaGenerator {
)
return bitsName

case .convertToBitPattern(let inner):
let inner = inner.render(&printer, placeholder)
return "Int64(Int(bitPattern: \(inner)))"

case .allocateExistentialValue(let inner, let name, let protocolTypes):
let inner = inner.render(&printer, placeholder)
let existentialType = SwiftKitPrinting.renderExistentialType(protocolTypes)
Expand Down Expand Up @@ -1667,6 +1728,12 @@ extension JNISwift2JavaGenerator {
let inner = inner.render(&printer, placeholder)
return "\(inner).pointee"

case .constructUnsafeBufferPointer(let base, let count, let mutable):
let base = base.render(&printer, placeholder)
let count = count.render(&printer, placeholder)
let bufferTypeName: String = mutable ? "UnsafeMutableBufferPointer" : "UnsafeBufferPointer"
return "\(bufferTypeName)(start: \(base), count: \(count))"

case .closureLowering(let parameters, let nativeResult):
var printer = SwiftPrinter()

Expand Down Expand Up @@ -1876,6 +1943,20 @@ extension JNISwift2JavaGenerator {
}
return ""

case .bufferPointerIndirectReturn(let inner, let outArgumentName):
let inner = inner.render(&printer, placeholder)
printer.printBraceBlock("do") { printer in
printer.print(
"""
let baseAddressBits$ = Int64(Int(bitPattern: \(inner).baseAddress))
environment.interface.SetLongField(environment, \(outArgumentName), _JNIMethodIDCache.SwiftBufferPointer.baseAddress, baseAddressBits$.getJNIValue(in: environment))
let countBits$ = Int64(\(inner).count)
environment.interface.SetLongField(environment, \(outArgumentName), _JNIMethodIDCache.SwiftBufferPointer.count, countBits$.getJNIValue(in: environment))
"""
)
}
return ""

case .constructor(let swiftType, let arguments):
let args = arguments.map { name, value in
let value = value.render(&printer, placeholder)
Expand Down
6 changes: 6 additions & 0 deletions Sources/JExtractSwiftLib/JavaTypes/JavaType+SwiftKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
//
//===----------------------------------------------------------------------===//


import SwiftJavaJNICore

extension JavaType {
Expand All @@ -21,4 +22,9 @@ extension JavaType {
.class(package: "org.swift.swiftkit.core", name: "_OutSwiftGenericInstance")
}

/// A base address and element count pair
static var swiftBufferPointer: JavaType {
.class(package: "org.swift.swiftkit.core", name: "SwiftBufferPointer")
}

}
29 changes: 29 additions & 0 deletions Sources/SwiftJavaRuntimeSupport/JNIMethodIDCaches.swift
Original file line number Diff line number Diff line change
Expand Up @@ -240,4 +240,33 @@ extension _JNIMethodIDCache {
cache.fields[selfTypePointerField]!
}
}

public enum SwiftBufferPointer {
private static let baseAddressField = Field(
name: "baseAddress",
signature: "J"
)

private static let countField = Field(
name: "count",
signature: "J"
)

private static let cache = _JNIMethodIDCache(
className: "org/swift/swiftkit/core/SwiftBufferPointer",
fields: [baseAddressField, countField]
)

public static var `class`: jclass {
cache.javaClass
}

public static var baseAddress: jfieldID {
cache.fields[baseAddressField]!
}

public static var count: jfieldID {
cache.fields[countField]!
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//===----------------------------------------------------------------------===//
//
// 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 org.swift.swiftkit.core;

/**
* Corresponds to Swift's {@code UnsafeBufferPointer<T>} and
* {@code UnsafeMutableBufferPointer<T>}.
*/
public final class SwiftBufferPointer {
/** Address of the first element, as a raw pointer bit pattern. */
private long baseAddress;

/** Number of elements in the buffer, not a byte count. */
private long count;

public SwiftBufferPointer() {
this(0, 0);
}

/**
* @param baseAddress address of the first element, as a raw pointer bit pattern
* @param count number of elements in the buffer
*/
public SwiftBufferPointer(long baseAddress, long count) {
this.baseAddress = baseAddress;
this.count = count;
}

@Override
public boolean equals(Object other) {
if (this == other) return true;
if (!(other instanceof SwiftBufferPointer)) return false;
SwiftBufferPointer o = (SwiftBufferPointer) other;
return this.baseAddress == o.baseAddress && this.count == o.count;
}

@Override
public int hashCode() {
return java.util.Objects.hash(baseAddress, count);
}

@Override
public String toString() {
return "SwiftBufferPointer(baseAddress=0x" + Long.toHexString(baseAddress) + ", count=" + count + ")";
}
}
Loading