Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
@@ -0,0 +1,18 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2025 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
//
//===----------------------------------------------------------------------===//

public enum Alignment: String {
case horizontal
case vertical
}
54 changes: 54 additions & 0 deletions Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/Vehicle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2025 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
//
//===----------------------------------------------------------------------===//

public enum Vehicle {
case bicycle
case car(String)
case motorbike(String, horsePower: Int64)

public init?(name: String) {
switch name {
case "bicycle": self = .bicycle
case "car": self = .car("Unknown")
case "motorbike": self = .motorbike("Unknown", horsePower: 0)
default: return nil
}
}

public var name: String {
switch self {
case .bicycle: "bicycle"
case .car: "car"
case .motorbike: "motorbike"
}
}

public func isFasterThan(other: Vehicle) -> Bool {
switch (self, other) {
case (.bicycle, .bicycle), (.bicycle, .car), (.bicycle, .motorbike): false
case (.car, .bicycle): true
case (.car, .motorbike), (.car, .car): false
case (.motorbike, .bicycle), (.motorbike, .car): true
case (.motorbike, .motorbike): false
}
}

public mutating func upgrade() {
switch self {
case .bicycle: self = .car("Unknown")
case .car: self = .motorbike("Unknown", horsePower: 0)
case .motorbike: break
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"javaPackage": "com.example.swift",
"mode": "jni",
"logLevel": ["debug"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 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 com.example.swift;

import org.junit.jupiter.api.Test;
import org.swift.swiftkit.core.ConfinedSwiftMemorySession;

import java.util.Optional;

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

public class AlignmentTest {
@Test
void rawValue() {
try (var arena = new ConfinedSwiftMemorySession()) {
Optional<Alignment> invalid = Alignment.init("invalid", arena);
assertFalse(invalid.isPresent());

Optional<Alignment> horizontal = Alignment.init("horizontal", arena);
assertTrue(horizontal.isPresent());
assertEquals("horizontal", horizontal.get().getRawValue());

Optional<Alignment> vertical = Alignment.init("vertical", arena);
assertTrue(vertical.isPresent());
assertEquals("vertical", vertical.get().getRawValue());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 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 com.example.swift;

import org.junit.jupiter.api.Test;
import org.swift.swiftkit.core.ConfinedSwiftMemorySession;

import java.util.Optional;

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

public class VehicleTest {
@Test
void bicycle() {
try (var arena = new ConfinedSwiftMemorySession()) {
Vehicle vehicle = Vehicle.bicycle(arena);
assertNotNull(vehicle);
}
}

@Test
void car() {
try (var arena = new ConfinedSwiftMemorySession()) {
Vehicle vehicle = Vehicle.car("Porsche 911", arena);
assertNotNull(vehicle);
}
}

@Test
void motorbike() {
try (var arena = new ConfinedSwiftMemorySession()) {
Vehicle vehicle = Vehicle.motorbike("Yamaha", 750, arena);
assertNotNull(vehicle);
}
}

@Test
void initName() {
try (var arena = new ConfinedSwiftMemorySession()) {
assertFalse(Vehicle.init("bus", arena).isPresent());
Optional<Vehicle> vehicle = Vehicle.init("car", arena);
assertTrue(vehicle.isPresent());
assertNotNull(vehicle.get());
}
}

@Test
void nameProperty() {
try (var arena = new ConfinedSwiftMemorySession()) {
Vehicle vehicle = Vehicle.bicycle(arena);
assertEquals("bicycle", vehicle.getName());
}
}

@Test
void isFasterThan() {
try (var arena = new ConfinedSwiftMemorySession()) {
Vehicle bicycle = Vehicle.bicycle(arena);
Vehicle car = Vehicle.car("Porsche 911", arena);
assertFalse(bicycle.isFasterThan(car));
assertTrue(car.isFasterThan(bicycle));
}
}

@Test
void upgrade() {
try (var arena = new ConfinedSwiftMemorySession()) {
Vehicle vehicle = Vehicle.bicycle(arena);
assertEquals("bicycle", vehicle.getName());
vehicle.upgrade();
assertEquals("car", vehicle.getName());
vehicle.upgrade();
assertEquals("motorbike", vehicle.getName());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@ extension DeclSyntaxProtocol {
}
))
.triviaSanitizedDescription
case .enumCaseDecl(let node):
node.triviaSanitizedDescription
default:
fatalError("unimplemented \(self.kind)")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,10 @@ extension LoweredFunctionSignature {
case .setter:
assert(paramExprs.count == 1)
resultExpr = "\(callee) = \(paramExprs[0])"

case .enumCase:
// This should not be called, but let's fatalError.
fatalError("Enum cases are not supported with FFM.")
}

// Lower the result.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ extension FFMSwift2JavaGenerator {
let javaName = switch decl.apiKind {
case .getter: decl.javaGetterName
case .setter: decl.javaSetterName
case .function, .initializer: decl.name
case .function, .initializer, .enumCase: decl.name
}

// Signature.
Expand Down
30 changes: 30 additions & 0 deletions Sources/JExtractSwiftLib/ImportedDecls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package enum SwiftAPIKind {
case initializer
case getter
case setter
case enumCase
}

/// Describes a Swift nominal type (e.g., a class, struct, enum) that has been
Expand All @@ -32,6 +33,7 @@ package class ImportedNominalType: ImportedDecl {
package var initializers: [ImportedFunc] = []
package var methods: [ImportedFunc] = []
package var variables: [ImportedFunc] = []
package var cases: [ImportedEnumCase] = []

init(swiftNominal: SwiftNominalTypeDeclaration) {
self.swiftNominal = swiftNominal
Expand All @@ -46,6 +48,33 @@ package class ImportedNominalType: ImportedDecl {
}
}

public final class ImportedEnumCase: ImportedDecl, CustomStringConvertible {
/// The case name
public var name: String

/// The enum parameters
var parameters: [SwiftEnumCaseParameter]

/// A function that represents the Swift static "initializer" for cases
var caseFunction: ImportedFunc

init(name: String, parameters: [SwiftEnumCaseParameter], caseFunction: ImportedFunc) {
self.name = name
self.parameters = parameters
self.caseFunction = caseFunction
}

public var description: String {
"""
ImportedEnumCase {
name: \(name),
parameters: \(parameters),
caseFunction: \(caseFunction)
}
"""
}
}

public final class ImportedFunc: ImportedDecl, CustomStringConvertible {
/// Swift module name (e.g. the target name where a type or function was declared)
public var module: String
Expand Down Expand Up @@ -113,6 +142,7 @@ public final class ImportedFunc: ImportedDecl, CustomStringConvertible {
let prefix = switch self.apiKind {
case .getter: "getter:"
case .setter: "setter:"
case .enumCase: "case:"
case .function, .initializer: ""
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ extension JNISwift2JavaGenerator {

printer.println()

if decl.swiftNominal.kind == .enum {
printEnumHelpers(&printer, decl)
printer.println()
}

for initializer in decl.initializers {
printFunctionDowncallMethods(&printer, initializer)
printer.println()
Expand Down Expand Up @@ -187,6 +192,43 @@ extension JNISwift2JavaGenerator {
}
}

private func printEnumHelpers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
// TODO: Move this to seperate file +Enum?
printEnumDiscriminator(&printer, decl)
printer.println()
printEnumCaseInterface(&printer, decl)
printer.println()
printEnumStaticInitializers(&printer, decl)
printer.println()
printEnumCases(&printer, decl)
}

private func printEnumDiscriminator(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
printer.printBraceBlock("public enum Discriminator") { printer in
printer.print(
decl.cases.map { $0.name.uppercased() }.joined(separator: ",\n")
)
}
}

private func printEnumCaseInterface(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
// printer.print("public sealed interface Case {}")
}

private func printEnumStaticInitializers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
for enumCase in decl.cases {
printFunctionDowncallMethods(&printer, enumCase.caseFunction)
}
}

private func printEnumCases(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
// for enumCase in decl.cases {
// printer.printBraceBlock("public static final \(enumCase.name.firstCharacterUppercased) implements Case") { printer in
//
// }
// }
}

private func printFunctionDowncallMethods(
_ printer: inout CodePrinter,
_ decl: ImportedFunc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ extension JNISwift2JavaGenerator {
let javaName = switch decl.apiKind {
case .getter: decl.javaGetterName
case .setter: decl.javaSetterName
case .function, .initializer: decl.name
case .function, .initializer, .enumCase: decl.name
}

// Swift -> Java
Expand Down Expand Up @@ -137,7 +137,7 @@ extension JNISwift2JavaGenerator {
parentName: String
) throws -> TranslatedFunctionSignature {
let parameters = try functionSignature.parameters.enumerated().map { idx, param in
let parameterName = param.parameterName ?? "arg\(idx))"
let parameterName = param.parameterName ?? "arg\(idx)"
return try translateParameter(swiftType: param.type, parameterName: parameterName, methodName: methodName, parentName: parentName)
}

Expand Down
Loading
Loading