Skip to content

Commit 6edcde5

Browse files
authored
Merge pull request #41 from ktoso/wip-properties
2 parents 301adca + 9a561cf commit 6edcde5

File tree

14 files changed

+840
-95
lines changed

14 files changed

+840
-95
lines changed

Package.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ let package = Package(
122122
dependencies: [
123123
.package(url: "https://github.com/swiftlang/swift-syntax.git", branch: "main"),
124124
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.5.0"),
125-
.package(url: "https://github.com/apple/swift-system", from: "1.0.0"),
125+
.package(url: "https://github.com/apple/swift-system", from: "1.0.0"), // TODO: remove, we should not need 'nm' or process callouts
126+
.package(url: "https://github.com/apple/swift-collections.git", .upToNextMinor(from: "1.1.0")),
126127
],
127128
targets: [
128129
.macro(
@@ -282,6 +283,7 @@ let package = Package(
282283
.product(name: "SwiftSyntax", package: "swift-syntax"),
283284
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
284285
.product(name: "ArgumentParser", package: "swift-argument-parser"),
286+
.product(name: "Collections", package: "swift-collections"),
285287
"_Subprocess",
286288
"JavaTypes",
287289
],

Samples/SwiftKitSampleApp/build.gradle

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,11 @@ application {
9595
task jextract(type: Exec) {
9696
description = "Extracts Java accessor sources using jextract"
9797
outputs.dir(layout.buildDirectory.dir("generated"))
98-
inputs.dir("$rootDir/Sources/ExampleSwiftLibrary")
98+
inputs.dir("$rootDir/Sources/ExampleSwiftLibrary") // monitored library
99+
100+
// any changes in the source generator sources also mean the resulting output might change
101+
inputs.dir("$rootDir/Sources/JExtractSwift")
102+
inputs.dir("$rootDir/Sources/JExtractSwiftTool")
99103

100104
workingDir = rootDir
101105
commandLine "make"

Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/MySwiftClassTest.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
package com.example.swift.generated;
1616

1717
import org.junit.jupiter.api.BeforeAll;
18+
import org.junit.jupiter.api.Test;
19+
import org.junit.jupiter.api.condition.DisabledOnOs;
20+
import org.junit.jupiter.api.condition.OS;
1821

1922
import static org.junit.jupiter.api.Assertions.assertEquals;
2023
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -31,6 +34,27 @@ static void beforeAll() {
3134
System.setProperty("jextract.trace.downcalls", "true");
3235
}
3336

34-
// TODO: test member methods on MySwiftClass
37+
@Test
38+
@DisabledOnOs(OS.LINUX) // FIXME: enable on Linux when we get new compiler with mangled names in swift interfaces
39+
void test_MySwiftClass_voidMethod() {
40+
MySwiftClass o = new MySwiftClass(12, 42);
41+
o.voidMethod();
42+
}
43+
44+
@Test
45+
@DisabledOnOs(OS.LINUX) // FIXME: enable on Linux when we get new compiler with mangled names in swift interfaces
46+
void test_MySwiftClass_makeIntMethod() {
47+
MySwiftClass o = new MySwiftClass(12, 42);
48+
var got = o.makeIntMethod();
49+
assertEquals(12, got);
50+
}
51+
52+
@Test
53+
@DisabledOnOs(OS.LINUX) // FIXME: enable on Linux when we get new compiler with mangled names in swift interfaces
54+
void test_MySwiftClass_property_len() {
55+
MySwiftClass o = new MySwiftClass(12, 42);
56+
var got = o.makeIntMethod();
57+
assertEquals(12, got);
58+
}
3559

3660
}

Sources/ExampleSwiftLibrary/MySwiftLibrary.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ public class MySwiftClass {
5555
p("Deinit, self = 0x\(String(addr, radix: 16, uppercase: true))")
5656
}
5757

58+
public var counter: Int32 = 0
59+
5860
public func voidMethod() {
5961
p("")
6062
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
extension String {
16+
17+
// TODO: naive implementation good enough for our simple case `methodMethodSomething` -> `MethodSomething`
18+
var toCamelCase: String {
19+
guard let f = first else {
20+
return self
21+
}
22+
23+
return "\(f.uppercased())\(String(dropFirst()))"
24+
}
25+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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 Foundation
16+
import JavaTypes
17+
import SwiftSyntax
18+
import OrderedCollections
19+
20+
extension ImportedFunc {
21+
/// Render a `@{@snippet ... }` comment section that can be put inside a JavaDoc comment
22+
/// when referring to the original declaration a printed method refers to.
23+
var renderCommentSnippet: String? {
24+
if let syntax {
25+
"""
26+
* {@snippet lang=swift :
27+
* \(syntax)
28+
* }
29+
"""
30+
} else {
31+
nil
32+
}
33+
}
34+
}
35+
36+
extension VariableAccessorKind {
37+
38+
public var fieldSuffix: String {
39+
switch self {
40+
case .get: "_GET"
41+
case .set: "_SET"
42+
}
43+
}
44+
45+
public var renderDescFieldName: String {
46+
switch self {
47+
case .get: "DESC_GET"
48+
case .set: "DESC_SET"
49+
}
50+
}
51+
52+
public var renderAddrFieldName: String {
53+
switch self {
54+
case .get: "ADDR_GET"
55+
case .set: "ADDR_SET"
56+
}
57+
}
58+
59+
public var renderHandleFieldName: String {
60+
switch self {
61+
case .get: "HANDLE_GET"
62+
case .set: "HANDLE_SET"
63+
}
64+
}
65+
66+
/// Renders a "$get" part that can be used in a method signature representing this accessor.
67+
public var renderMethodNameSegment: String {
68+
switch self {
69+
case .get: "$get"
70+
case .set: "$set"
71+
}
72+
}
73+
74+
func renderMethodName(_ decl: ImportedFunc) -> String? {
75+
switch self {
76+
case .get: "get\(decl.identifier.toCamelCase)"
77+
case .set: "set\(decl.identifier.toCamelCase)"
78+
}
79+
}
80+
}
81+
82+
extension Optional where Wrapped == VariableAccessorKind {
83+
public var renderDescFieldName: String {
84+
self?.renderDescFieldName ?? "DESC"
85+
}
86+
87+
public var renderAddrFieldName: String {
88+
self?.renderAddrFieldName ?? "ADDR"
89+
}
90+
91+
public var renderHandleFieldName: String {
92+
self?.renderHandleFieldName ?? "HANDLE"
93+
}
94+
95+
public var renderMethodNameSegment: String {
96+
self?.renderMethodNameSegment ?? ""
97+
}
98+
99+
func renderMethodName(_ decl: ImportedFunc) -> String {
100+
self?.renderMethodName(decl) ?? decl.baseIdentifier
101+
}
102+
}

Sources/JExtractSwift/ImportedDecls.swift

Lines changed: 151 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
import Foundation
1616
import JavaTypes
1717
import SwiftSyntax
18+
import OrderedCollections
1819

20+
/// Any imported (Swift) declaration
1921
protocol ImportedDecl {
2022

2123
}
@@ -32,6 +34,7 @@ public struct ImportedNominalType: ImportedDecl {
3234

3335
public var initializers: [ImportedFunc] = []
3436
public var methods: [ImportedFunc] = []
37+
public var variables: [ImportedVariable] = []
3538

3639
public init(swiftTypeName: String, javaType: JavaType, swiftMangledName: String? = nil, kind: NominalTypeKind) {
3740
self.swiftTypeName = swiftTypeName
@@ -195,7 +198,7 @@ public struct ImportedFunc: ImportedDecl, CustomStringConvertible {
195198

196199
public var swiftMangledName: String = ""
197200

198-
public var swiftDeclRaw: String? = nil
201+
public var syntax: String? = nil
199202

200203
public var isInit: Bool = false
201204

@@ -221,7 +224,153 @@ public struct ImportedFunc: ImportedDecl, CustomStringConvertible {
221224
222225
Swift mangled name:
223226
Imported from:
224-
\(swiftDeclRaw ?? "<no swift source>")
227+
\(syntax?.description ?? "<no swift source>")
228+
}
229+
"""
230+
}
231+
}
232+
233+
public enum VariableAccessorKind {
234+
case get
235+
case set
236+
}
237+
238+
public struct ImportedVariable: ImportedDecl, CustomStringConvertible {
239+
/// If this function/method is member of a class/struct/protocol,
240+
/// this will contain that declaration's imported name.
241+
///
242+
/// This is necessary when rendering accessor Java code we need the type that "self" is expecting to have.
243+
public var parentName: TranslatedType?
244+
public var hasParent: Bool { parentName != nil }
245+
246+
/// This is a full name such as "counter".
247+
public var identifier: String
248+
249+
/// Which accessors are we able to expose.
250+
///
251+
/// Usually this will be all the accessors the variable declares,
252+
/// however if the getter is async or throwing we may not be able to import it
253+
/// (yet), and therefore would skip it from the supported set.
254+
public var supportedAccessorKinds: OrderedSet<VariableAccessorKind> = [.get, .set]
255+
256+
/// This is the base identifier for the function, e.g., "init" for an
257+
/// initializer or "f" for "f(a:b:)".
258+
public var baseIdentifier: String {
259+
guard let idx = identifier.firstIndex(of: "(") else {
260+
return identifier
261+
}
262+
return String(identifier[..<idx])
263+
}
264+
265+
/// A display name to use to refer to the Swift declaration with its
266+
/// enclosing type, if there is one.
267+
public var displayName: String {
268+
if let parentName {
269+
return "\(parentName.swiftTypeName).\(identifier)"
270+
}
271+
272+
return identifier
273+
}
274+
275+
public var returnType: TranslatedType
276+
277+
/// Synthetic signature of an accessor function of the given kind of this property
278+
public func accessorFunc(kind: VariableAccessorKind) -> ImportedFunc? {
279+
guard self.supportedAccessorKinds.contains(kind) else {
280+
return nil
281+
}
282+
283+
switch kind {
284+
case .set:
285+
let newValueParam: FunctionParameterSyntax = "_ newValue: \(self.returnType.cCompatibleSwiftType)"
286+
var funcDecl = ImportedFunc(
287+
parentName: self.parentName,
288+
identifier: self.identifier,
289+
returnType: TranslatedType.void,
290+
parameters: [.init(param: newValueParam, type: self.returnType)])
291+
funcDecl.swiftMangledName = self.swiftMangledName + "s" // form mangled name of the getter by adding the suffix
292+
return funcDecl
293+
294+
case .get:
295+
var funcDecl = ImportedFunc(
296+
parentName: self.parentName,
297+
identifier: self.identifier,
298+
returnType: self.returnType,
299+
parameters: [])
300+
funcDecl.swiftMangledName = self.swiftMangledName + "g" // form mangled name of the getter by adding the suffix
301+
return funcDecl
302+
}
303+
}
304+
305+
public func effectiveAccessorParameters(_ kind: VariableAccessorKind, selfVariant: SelfParameterVariant?) -> [ImportedParam] {
306+
var params: [ImportedParam] = []
307+
308+
if kind == .set {
309+
let newValueParam: FunctionParameterSyntax = "_ newValue: \(raw: self.returnType.swiftTypeName)"
310+
params.append(
311+
ImportedParam(
312+
param: newValueParam,
313+
type: self.returnType)
314+
)
315+
}
316+
317+
if let parentName {
318+
// Add `self: Self` for method calls on a member
319+
//
320+
// allocating initializer takes a Self.Type instead, but it's also a pointer
321+
switch selfVariant {
322+
case nil, .wrapper:
323+
break
324+
325+
case .pointer:
326+
let selfParam: FunctionParameterSyntax = "self$: $swift_pointer"
327+
params.append(
328+
ImportedParam(
329+
param: selfParam,
330+
type: parentName
331+
)
332+
)
333+
334+
case .memorySegment:
335+
let selfParam: FunctionParameterSyntax = "self$: $java_lang_foreign_MemorySegment"
336+
var parentForSelf = parentName
337+
parentForSelf.javaType = .javaForeignMemorySegment
338+
params.append(
339+
ImportedParam(
340+
param: selfParam,
341+
type: parentForSelf
342+
)
343+
)
344+
}
345+
}
346+
347+
return params
348+
}
349+
350+
public var swiftMangledName: String = ""
351+
352+
public var syntax: VariableDeclSyntax? = nil
353+
354+
public init(
355+
parentName: TranslatedType?,
356+
identifier: String,
357+
returnType: TranslatedType
358+
) {
359+
self.parentName = parentName
360+
self.identifier = identifier
361+
self.returnType = returnType
362+
}
363+
364+
public var description: String {
365+
"""
366+
ImportedFunc {
367+
mangledName: \(swiftMangledName)
368+
identifier: \(identifier)
369+
returnType: \(returnType)
370+
371+
Swift mangled name:
372+
Imported from:
373+
\(syntax?.description ?? "<no swift source>")
225374
}
226375
"""
227376
}

0 commit comments

Comments
 (0)