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
4 changes: 4 additions & 0 deletions .licenseignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,7 @@ Samples/JavaDependencySampleApp/gradle
Sources/_Subprocess/**
Sources/_SubprocessCShims/**
Samples/gradle
Sources/SwiftJavaRuntimeSupport/Foundation/*
Sources/SwiftRuntimeFunctions/Foundation/*
SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/*
SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/foundation/*
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@

package org.swift.swiftkit.ffm;

import com.example.swift.Data;
import com.example.swift.MySwiftLibrary;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.swift.swiftkit.ffm.foundation.Data;

import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
Expand Down Expand Up @@ -79,7 +79,7 @@ public ByteBuffer ffm_data_withUnsafeBytes_asByteBuffer() {
});
return buf.value;
}

@Benchmark
public byte[] ffm_data_withUnsafeBytes_toArray() {
Holder<byte[]> buf = new Holder<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.swift.swiftkit.core.SwiftLibraries;
import org.swift.swiftkit.ffm.AllocatingSwiftArena;
import org.swift.swiftkit.ffm.SwiftRuntime;
import org.swift.swiftkit.ffm.foundation.Data;

import java.util.Optional;
import java.util.OptionalLong;
Expand Down Expand Up @@ -85,7 +86,7 @@ static void examples() {
var origBytes = arena.allocateFrom("foobar");
var origDat = Data.init(origBytes, origBytes.byteSize(), arena);
CallTraces.trace("origDat.count = " + origDat.getCount());

var retDat = MySwiftLibrary.globalReceiveReturnData(origDat, arena);
retDat.withUnsafeBytes((retBytes) -> {
var str = retBytes.getString(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import org.junit.jupiter.api.Test;
import org.swift.swiftkit.ffm.AllocatingSwiftArena;
import org.swift.swiftkit.ffm.foundation.Data;

import java.lang.foreign.ValueLayout;

Expand Down Expand Up @@ -134,7 +135,7 @@ void test_Data_toByteBuffer_emptyData() {
byte[] original = new byte[0];
var data = Data.fromByteArray(original, arena);
var buffer = data.toByteBuffer(arena);

assertEquals(0, buffer.capacity());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import org.junit.jupiter.api.Test;
import org.swift.swiftkit.ffm.AllocatingSwiftArena;
import org.swift.swiftkit.ffm.foundation.Data;

import java.util.Optional;
import java.util.OptionalLong;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.openjdk.jmh.infra.Blackhole;
import org.swift.swiftkit.core.ClosableSwiftArena;
import org.swift.swiftkit.core.SwiftArena;
import org.swift.swiftkit.core.foundation.Data;

import java.util.concurrent.TimeUnit;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import org.junit.jupiter.api.Test;
import org.swift.swiftkit.core.SwiftArena;
import org.swift.swiftkit.core.foundation.Data;

import static org.junit.jupiter.api.Assertions.*;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import org.junit.jupiter.api.Test;
import org.swift.swiftkit.core.SwiftArena;
import org.swift.swiftkit.core.foundation.Date;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
Expand Down Expand Up @@ -59,4 +60,4 @@ void date_timeIntervalSince1970() {
assertEquals(1000, date.getTimeIntervalSince1970());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@

package com.example.swift;

import com.example.swift.MySwiftLibrary;
import org.junit.jupiter.api.Test;
import org.swift.swiftkit.core.SwiftArena;
import org.swift.swiftkit.core.foundation.Data;
import org.swift.swiftkit.core.foundation.Date;

import java.time.Instant;
import java.util.Optional;
Expand Down Expand Up @@ -143,4 +144,4 @@ void optionalThrows() {
assertEquals("swiftError", exception.getMessage());
}
}
}
}
26 changes: 26 additions & 0 deletions Sources/FakeFoundation/Data.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#if canImport(FoundationEssentials)
import FoundationEssentials
#else
import Foundation
#endif

public struct Data: DataProtocol {
public init(bytes: UnsafeRawPointer, count: Int)
public init(_ bytes: [UInt8])
public var count: Int { get }
public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) -> Void)
}
21 changes: 21 additions & 0 deletions Sources/FakeFoundation/DataProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#if canImport(FoundationEssentials)
import FoundationEssentials
#else
import Foundation
#endif

public protocol DataProtocol {}
27 changes: 27 additions & 0 deletions Sources/FakeFoundation/Date.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#if canImport(FoundationEssentials)
import FoundationEssentials
#else
import Foundation
#endif

public struct Date {
/// The interval between the date object and 00:00:00 UTC on 1 January 1970.
public var timeIntervalSince1970: Double { get }

/// Returns a `Date` initialized relative to 00:00:00 UTC on 1 January 1970 by a given number of seconds.
public init(timeIntervalSince1970: Double)
}
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,8 @@ extension FFMSwift2JavaGenerator {
let arena =
if let className = type.className,
analysis.importedTypes[className] != nil
|| type == .swiftkitFFMFoundationData
|| type == .swiftkitFFMFoundationDataProtocol
{
// Use passed-in 'SwiftArena' for 'SwiftValue'.
"swiftArena"
Expand Down Expand Up @@ -619,7 +621,9 @@ extension FFMSwift2JavaGenerator {
func renderMemoryLayoutValue(for javaType: JavaType) -> String {
if let layout = ForeignValueLayout(javaType: javaType) {
return layout.description
} else if case .class(package: _, name: let customClass, _) = javaType {
} else if case .class(.some(let package), name: let customClass, _) = javaType {
return ForeignValueLayout(customType: "\(package).\(customClass)").description
} else if case .class(.none, name: let customClass, _) = javaType {
return ForeignValueLayout(customType: customClass).description
} else {
fatalError("renderMemoryLayoutValue not supported for \(javaType)")
Expand Down Expand Up @@ -776,7 +780,7 @@ extension FFMSwift2JavaGenerator.JavaConversionStep {

case .wrapMemoryAddressUnsafe(let inner, let javaType):
let inner = inner.render(&printer, placeholder, placeholderForDowncall: placeholderForDowncall)
return "\(javaType.className!).wrapMemoryAddressUnsafe(\(inner), swiftArena)"
return "\(javaType).wrapMemoryAddressUnsafe(\(inner), swiftArena)"

case .construct(let inner, let javaType):
let inner = inner.render(&printer, placeholder, placeholderForDowncall: placeholderForDowncall)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,15 @@ extension FFMSwift2JavaGenerator {
)

case .foundationData, .essentialsData:
break
return TranslatedParameter(
javaParameters: [
JavaParameter(
name: parameterName,
type: .swiftkitFFMFoundationData
)
],
conversion: .swiftValueSelfSegment(.placeholder)
)

case .swiftJavaError:
// SwiftJavaError is a class — treat as arbitrary nominal type below
Expand Down Expand Up @@ -635,10 +643,26 @@ extension FFMSwift2JavaGenerator {
case .nominal(let nominal):
if let knownType = nominal.nominalTypeDecl.knownTypeKind {
switch knownType {
case .foundationData, .foundationDataProtocol:
break
case .essentialsData, .essentialsDataProtocol:
break
case .foundationData, .essentialsData:
return TranslatedParameter(
javaParameters: [
JavaParameter(
name: parameterName,
type: .optional(.swiftkitFFMFoundationData)
)
],
conversion: .call(.placeholder, function: "SwiftRuntime.toOptionalSegmentInstance", withArena: false)
)
case .foundationDataProtocol, .essentialsDataProtocol:
return TranslatedParameter(
javaParameters: [
JavaParameter(
name: parameterName,
type: .optional(.swiftkitFFMFoundationDataProtocol)
)
],
conversion: .call(.placeholder, function: "SwiftRuntime.toOptionalSegmentInstance", withArena: false)
)
default:
throw JavaTranslationError.unhandledType(known: .optional(swiftType))
}
Expand All @@ -647,7 +671,7 @@ extension FFMSwift2JavaGenerator {
let translatedTy = try self.translate(swiftType: swiftType)
return TranslatedParameter(
javaParameters: [
JavaParameter(name: parameterName, type: JavaType(className: "Optional<\(translatedTy.description)>"))
JavaParameter(name: parameterName, type: .optional(translatedTy))
],
conversion: .call(.placeholder, function: "SwiftRuntime.toOptionalSegmentInstance", withArena: false)
)
Expand Down Expand Up @@ -750,7 +774,15 @@ extension FFMSwift2JavaGenerator {
)

case .foundationData, .essentialsData:
break // Implemented as wrapper
let javaType: JavaType = .swiftkitFFMFoundationData
return TranslatedResult(
javaResultType: javaType,
annotations: resultAnnotations,
outParameters: [
JavaParameter(name: "", type: javaType)
],
conversion: .wrapMemoryAddressUnsafe(.placeholder, javaType)
)

case .unsafePointer, .unsafeMutablePointer:
// FIXME: Implement
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ extension FFMSwift2JavaGenerator {
// === All types
// We have to write all types to their corresponding output file that matches the file they were declared in,
// because otherwise SwiftPM plugins will not pick up files apropriately -- we expect 1 output +SwiftJava.swift file for every input.

let filteredTypes: [String: ImportedNominalType]
if let singleType = config.singleType {
filteredTypes = self.analysis.importedTypes.filter { $0.key == singleType }
Expand All @@ -92,14 +93,13 @@ extension FFMSwift2JavaGenerator {
let filename = "\(inputFileName)".replacing(".swift", with: "+SwiftJava.swift")

// Print file header before all type thunks
printer.print(
"""
// Generated by swift-java

import SwiftRuntimeFunctions
printer.print("// Generated by swift-java")
printer.println()
if !group.value.allSatisfy({ $0.value.swiftNominal.moduleName == "SwiftRuntimeFunctions" }) {
printer.print("import SwiftRuntimeFunctions")
}
Comment on lines +98 to +100
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A workaround to ensure the generated code doesn't import its own package

printer.println()

"""
)
self.lookupContext.symbolTable.printImportedModules(&printer)

for ty in importedTypesForThisFile {
Expand Down Expand Up @@ -263,16 +263,15 @@ struct SwiftThunkTranslator {

/// Render special thunks for known types like Foundation.Data
func renderSpecificTypeThunks(_ nominal: ImportedNominalType) -> [DeclSyntax] {
guard let knownType = nominal.swiftNominal.knownTypeKind else {
return []
}

switch knownType {
case .foundationData, .essentialsData:
return renderFoundationDataThunks(nominal)
default:
return []
if nominal.swiftNominal.moduleName == "SwiftRuntimeFunctions" {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Foundation types are now imported using the swift-java package name. This is the concern I raised in #684 (comment)

switch nominal.swiftNominal.qualifiedName {
case "Data":
return renderFoundationDataThunks(nominal)
default:
break
}
}
return []
}

/// Render Swift thunks for Foundation.Data helper methods
Expand Down
18 changes: 8 additions & 10 deletions Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ extension FFMSwift2JavaGenerator {

printer.print(
"""
static MemorySegment findOrThrow(String symbol) {
public static MemorySegment findOrThrow(String symbol) {
return SYMBOL_LOOKUP.find(symbol)
.orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: %s".formatted(symbol)));
}
Expand Down Expand Up @@ -557,15 +557,13 @@ extension FFMSwift2JavaGenerator {

/// Print special helper methods for known types like Foundation.Data
func printSpecificTypeHelpers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
guard let knownType = decl.swiftNominal.knownTypeKind else {
return
}

switch knownType {
case .foundationData, .essentialsData:
printFoundationDataHelpers(&printer, decl)
default:
break
if decl.swiftNominal.moduleName == "SwiftRuntimeFunctions" {
switch decl.swiftNominal.qualifiedName {
case "Data":
printFoundationDataHelpers(&printer, decl)
default:
break
}
}
}

Expand Down
Loading
Loading