Skip to content

Commit 2d2318a

Browse files
authored
Merge branch 'main' into swiftkit-selfpointer
2 parents e7612f0 + 19a6785 commit 2d2318a

File tree

20 files changed

+846
-20
lines changed

20 files changed

+846
-20
lines changed

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ let package = Package(
226226
exclude: ["swift-java.config"],
227227
swiftSettings: [
228228
.swiftLanguageMode(.v5),
229-
.unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"])
229+
.unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"], .when(platforms: [.macOS, .linux, .windows]))
230230
],
231231
linkerSettings: [
232232
.unsafeFlags(
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
public struct MySwiftStruct {
16+
private var cap: Int64
17+
public var len: Int64
18+
19+
public init(cap: Int64, len: Int64) {
20+
self.cap = cap
21+
self.len = len
22+
}
23+
24+
public func getCapacity() -> Int64 {
25+
self.cap
26+
}
27+
28+
public mutating func increaseCap(by value: Int64) -> Int64 {
29+
precondition(value > 0)
30+
self.cap += value
31+
return self.cap
32+
}
33+
}

Samples/JExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2SwiftJNI.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ static void examples() {
5252
} catch (Exception e) {
5353
System.out.println("Caught exception: " + e.getMessage());
5454
}
55+
56+
MySwiftStruct myStruct = MySwiftStruct.init(12, 34, arena);
57+
System.out.println("myStruct.cap: " + myStruct.getCapacity());
58+
System.out.println("myStruct.len: " + myStruct.getLen());
59+
myStruct.increaseCap(10);
60+
System.out.println("myStruct.cap after increase: " + myStruct.getCapacity());
5561
}
5662

5763
System.out.println("DONE.");
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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+
package com.example.swift;
16+
17+
import org.junit.jupiter.api.Test;
18+
import org.swift.swiftkit.core.ConfinedSwiftMemorySession;
19+
20+
import static org.junit.jupiter.api.Assertions.*;
21+
22+
public class MySwiftStructTest {
23+
@Test
24+
void init() {
25+
try (var arena = new ConfinedSwiftMemorySession()) {
26+
MySwiftStruct s = MySwiftStruct.init(1337, 42, arena);
27+
assertEquals(1337, s.getCapacity());
28+
assertEquals(42, s.getLen());
29+
}
30+
}
31+
32+
@Test
33+
void getAndSetLen() {
34+
try (var arena = new ConfinedSwiftMemorySession()) {
35+
MySwiftStruct s = MySwiftStruct.init(1337, 42, arena);
36+
s.setLen(100);
37+
assertEquals(100, s.getLen());
38+
}
39+
}
40+
41+
@Test
42+
void increaseCap() {
43+
try (var arena = new ConfinedSwiftMemorySession()) {
44+
MySwiftStruct s = MySwiftStruct.init(1337, 42, arena);
45+
long newCap = s.increaseCap(10);
46+
assertEquals(1347, newCap);
47+
assertEquals(1347, s.getCapacity());
48+
}
49+
}
50+
}

Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,23 @@ public func globalReceiveSomeDataProtocol(data: some DataProtocol) -> Int {
6868
return data.count
6969
}
7070

71+
public func globalReceiveOptional(o1: Int?, o2: (some DataProtocol)?) -> Int {
72+
switch (o1, o2) {
73+
case (nil, nil):
74+
p("<nil>, <nil>")
75+
return 0
76+
case (let v1?, nil):
77+
p("\(v1), <nil>")
78+
return 1
79+
case (nil, let v2?):
80+
p("<nil>, \(v2)")
81+
return 2
82+
case (let v1?, let v2?):
83+
p("\(v1), \(v2)")
84+
return 3
85+
}
86+
}
87+
7188
// ==== Internal helpers
7289

7390
func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) {

Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
import org.swift.swiftkit.ffm.AllocatingSwiftArena;
2424
import org.swift.swiftkit.ffm.SwiftRuntime;
2525

26+
import java.util.Optional;
27+
import java.util.OptionalLong;
28+
2629
public class HelloJava2Swift {
2730

2831
public static void main(String[] args) {
@@ -96,6 +99,7 @@ static void examples() {
9699
var bytes = arena.allocateFrom("hello");
97100
var dat = Data.init(bytes, bytes.byteSize(), arena);
98101
MySwiftLibrary.globalReceiveSomeDataProtocol(dat);
102+
MySwiftLibrary.globalReceiveOptional(OptionalLong.of(12), Optional.of(dat));
99103
}
100104

101105

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 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+
package com.example.swift;
16+
17+
import org.junit.jupiter.api.Test;
18+
import org.swift.swiftkit.ffm.AllocatingSwiftArena;
19+
20+
import java.util.Optional;
21+
import java.util.OptionalLong;
22+
23+
import static org.junit.jupiter.api.Assertions.*;
24+
25+
public class OptionalImportTest {
26+
@Test
27+
void test_Optional_receive() {
28+
try (var arena = AllocatingSwiftArena.ofConfined()) {
29+
var origBytes = arena.allocateFrom("foobar");
30+
var data = Data.init(origBytes, origBytes.byteSize(), arena);
31+
assertEquals(0, MySwiftLibrary.globalReceiveOptional(OptionalLong.empty(), Optional.empty()));
32+
assertEquals(3, MySwiftLibrary.globalReceiveOptional(OptionalLong.of(12), Optional.of(data)));
33+
}
34+
}
35+
}

Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ extension SwiftKnownTypeDeclKind {
125125
.qualified(const: true, volatile: false, type: .void)
126126
)
127127
case .void: .void
128-
case .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, .unsafeBufferPointer, .unsafeMutableBufferPointer, .string, .data, .dataProtocol:
128+
case .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, .unsafeBufferPointer, .unsafeMutableBufferPointer, .string, .data, .dataProtocol, .optional:
129129
nil
130130
}
131131
}

Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ struct CdeclLowering {
194194
)
195195

196196
case .unsafeBufferPointer, .unsafeMutableBufferPointer:
197-
guard let genericArgs = type.asNominalType?.genericArguments, genericArgs.count == 1 else {
197+
guard let genericArgs = nominal.genericArguments, genericArgs.count == 1 else {
198198
throw LoweringError.unhandledType(type)
199199
}
200200
// Typed pointers are lowered to (raw-pointer, count) pair.
@@ -253,6 +253,12 @@ struct CdeclLowering {
253253
]
254254
))
255255

256+
case .optional:
257+
guard let genericArgs = nominal.genericArguments, genericArgs.count == 1 else {
258+
throw LoweringError.unhandledType(type)
259+
}
260+
return try lowerOptionalParameter(genericArgs[0], convention: convention, parameterName: parameterName)
261+
256262
case .string:
257263
// 'String' is passed in by C string. i.e. 'UnsafePointer<Int8>' ('const uint8_t *')
258264
if knownType == .string {
@@ -336,8 +342,79 @@ struct CdeclLowering {
336342
}
337343
throw LoweringError.unhandledType(type)
338344

339-
case .optional:
340-
throw LoweringError.unhandledType(type)
345+
case .optional(let wrapped):
346+
return try lowerOptionalParameter(wrapped, convention: convention, parameterName: parameterName)
347+
}
348+
}
349+
350+
/// Lower a Swift Optional to cdecl function type.
351+
///
352+
/// - Parameters:
353+
/// - fn: the Swift function type to lower.
354+
func lowerOptionalParameter(
355+
_ wrappedType: SwiftType,
356+
convention: SwiftParameterConvention,
357+
parameterName: String
358+
) throws -> LoweredParameter {
359+
// If there is a 1:1 mapping between this Swift type and a C type, lower it to 'UnsafePointer<T>?'
360+
if let _ = try? CType(cdeclType: wrappedType) {
361+
return LoweredParameter(
362+
cdeclParameters: [
363+
SwiftParameter(convention: .byValue, parameterName: parameterName, type: .optional(knownTypes.unsafePointer(wrappedType)))
364+
],
365+
conversion: .pointee(.optionalChain(.placeholder))
366+
)
367+
}
368+
369+
switch wrappedType {
370+
case .nominal(let nominal):
371+
if let knownType = nominal.nominalTypeDecl.knownTypeKind {
372+
switch knownType {
373+
case .data:
374+
break
375+
case .unsafeRawPointer, .unsafeMutableRawPointer:
376+
throw LoweringError.unhandledType(.optional(wrappedType))
377+
case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer:
378+
throw LoweringError.unhandledType(.optional(wrappedType))
379+
case .unsafePointer, .unsafeMutablePointer:
380+
throw LoweringError.unhandledType(.optional(wrappedType))
381+
case .unsafeBufferPointer, .unsafeMutableBufferPointer:
382+
throw LoweringError.unhandledType(.optional(wrappedType))
383+
case .void, .string:
384+
throw LoweringError.unhandledType(.optional(wrappedType))
385+
case .dataProtocol:
386+
throw LoweringError.unhandledType(.optional(wrappedType))
387+
default:
388+
// Unreachable? Should be handled by `CType(cdeclType:)` lowering above.
389+
throw LoweringError.unhandledType(.optional(wrappedType))
390+
}
391+
}
392+
393+
// Lower arbitrary nominal to `UnsafeRawPointer?`
394+
return LoweredParameter(
395+
cdeclParameters: [
396+
SwiftParameter(convention: .byValue, parameterName: parameterName, type: .optional(knownTypes.unsafeRawPointer))
397+
],
398+
conversion: .pointee(.typedPointer(.optionalChain(.placeholder), swiftType: wrappedType))
399+
)
400+
401+
case .existential(let proto), .opaque(let proto):
402+
if
403+
let knownProtocol = proto.asNominalTypeDeclaration?.knownTypeKind,
404+
let concreteTy = knownTypes.representativeType(of: knownProtocol)
405+
{
406+
return try lowerOptionalParameter(concreteTy, convention: convention, parameterName: parameterName)
407+
}
408+
throw LoweringError.unhandledType(.optional(wrappedType))
409+
410+
case .tuple(let tuple):
411+
if tuple.count == 1 {
412+
return try lowerOptionalParameter(tuple[0], convention: convention, parameterName: parameterName)
413+
}
414+
throw LoweringError.unhandledType(.optional(wrappedType))
415+
416+
case .function, .metatype, .optional:
417+
throw LoweringError.unhandledType(.optional(wrappedType))
341418
}
342419
}
343420

@@ -527,13 +604,13 @@ struct CdeclLowering {
527604
case .void:
528605
return LoweredResult(cdeclResultType: .void, cdeclOutParameters: [], conversion: .placeholder)
529606

530-
case .string:
531-
// Returning string is not supported at this point.
532-
throw LoweringError.unhandledType(type)
533-
534607
case .data:
535608
break
536609

610+
case .string, .optional:
611+
// Not supported at this point.
612+
throw LoweringError.unhandledType(type)
613+
537614
default:
538615
// Unreachable? Should be handled by `CType(cdeclType:)` lowering above.
539616
throw LoweringError.unhandledType(type)

Sources/JExtractSwiftLib/FFM/ConversionStep.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ enum ConversionStep: Equatable {
6262

6363
indirect case member(ConversionStep, member: String)
6464

65+
indirect case optionalChain(ConversionStep)
66+
6567
/// Count the number of times that the placeholder occurs within this
6668
/// conversion step.
6769
var placeholderCount: Int {
@@ -71,7 +73,7 @@ enum ConversionStep: Equatable {
7173
.typedPointer(let inner, swiftType: _),
7274
.unsafeCastPointer(let inner, swiftType: _),
7375
.populatePointer(name: _, assumingType: _, to: let inner),
74-
.member(let inner, member: _):
76+
.member(let inner, member: _), .optionalChain(let inner):
7577
inner.placeholderCount
7678
case .initialize(_, arguments: let arguments):
7779
arguments.reduce(0) { $0 + $1.argument.placeholderCount }
@@ -175,6 +177,10 @@ enum ConversionStep: Equatable {
175177
}
176178
return nil
177179

180+
case .optionalChain(let step):
181+
let inner = step.asExprSyntax(placeholder: placeholder, bodyItems: &bodyItems)
182+
return ExprSyntax(OptionalChainingExprSyntax(expression: inner!))
183+
178184
case .closureLowering(let parameterSteps, let resultStep):
179185
var body: [CodeBlockItemSyntax] = []
180186

0 commit comments

Comments
 (0)