Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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,59 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 20245Apple 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.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.swift.swiftkit.core.ClosableSwiftArena;
import org.swift.swiftkit.core.ConfinedSwiftMemorySession;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(value = 3, jvmArgsAppend = { "--enable-native-access=ALL-UNNAMED" })
public class EnumBenchmark {

@State(Scope.Benchmark)
public static class BenchmarkState {
ClosableSwiftArena arena;
Vehicle vehicle;

@Setup(Level.Trial)
public void beforeAll() {
arena = new ConfinedSwiftMemorySession();
vehicle = Vehicle.motorbike("Yamaha", 900, arena);
}

@TearDown(Level.Trial)
public void afterAll() {
arena.close();
}
}

@Benchmark
public Vehicle.Motorbike java_copy(BenchmarkState state, Blackhole bh) {
Vehicle.Motorbike motorbike = state.vehicle.getAsMotorbike().orElseThrow();
bh.consume(motorbike.arg0());
bh.consume(motorbike.horsePower());

return motorbike;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,46 @@ void upgrade() {
assertEquals("motorbike", vehicle.getName());
}
}

@Test
void getAsBicycle() {
try (var arena = new ConfinedSwiftMemorySession()) {
Vehicle vehicle = Vehicle.bicycle(arena);
Vehicle.Bicycle bicycle = vehicle.getAsBicycle().orElseThrow();
assertNotNull(bicycle);
}
}

@Test
void getAsCar() {
try (var arena = new ConfinedSwiftMemorySession()) {
Vehicle vehicle = Vehicle.car("BMW", arena);
Vehicle.Car car = vehicle.getAsCar().orElseThrow();
assertEquals("BMW", car.arg0());
}
}

@Test
void getAsMotorbike() {
try (var arena = new ConfinedSwiftMemorySession()) {
Vehicle vehicle = Vehicle.motorbike("Yamaha", 750, arena);
Vehicle.Motorbike motorbike = vehicle.getAsMotorbike().orElseThrow();
assertEquals("Yamaha", motorbike.arg0());
assertEquals(750, motorbike.horsePower());
}
}

@Test
void associatedValuesAreCopied() {
try (var arena = new ConfinedSwiftMemorySession()) {
Vehicle vehicle = Vehicle.car("BMW", arena);
Vehicle.Car car = vehicle.getAsCar().orElseThrow();
assertEquals("BMW", car.arg0());
vehicle.upgrade();
Vehicle.Motorbike motorbike = vehicle.getAsMotorbike().orElseThrow();
assertNotNull(motorbike);
// Motorbike should still remain
assertEquals("BMW", car.arg0());
}
}
}
25 changes: 24 additions & 1 deletion Sources/JExtractSwiftLib/ImportedDecls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,24 @@ public final class ImportedEnumCase: ImportedDecl, CustomStringConvertible {
/// The enum parameters
var parameters: [SwiftEnumCaseParameter]

var swiftDecl: any DeclSyntaxProtocol

var enumType: SwiftNominalType

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

init(name: String, parameters: [SwiftEnumCaseParameter], caseFunction: ImportedFunc) {
init(
name: String,
parameters: [SwiftEnumCaseParameter],
swiftDecl: any DeclSyntaxProtocol,
enumType: SwiftNominalType,
caseFunction: ImportedFunc
) {
self.name = name
self.parameters = parameters
self.swiftDecl = swiftDecl
self.enumType = enumType
self.caseFunction = caseFunction
}

Expand All @@ -69,12 +81,23 @@ public final class ImportedEnumCase: ImportedDecl, CustomStringConvertible {
ImportedEnumCase {
name: \(name),
parameters: \(parameters),
swiftDecl: \(swiftDecl),
enumType: \(enumType),
caseFunction: \(caseFunction)
}
"""
}
}

extension ImportedEnumCase: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self))
}
public static func == (lhs: ImportedEnumCase, rhs: ImportedEnumCase) -> Bool {
return lhs === rhs
}
}

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
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,6 @@ 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)
Expand All @@ -212,7 +211,8 @@ extension JNISwift2JavaGenerator {
}

private func printEnumCaseInterface(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
// printer.print("public sealed interface Case {}")
printer.print("public sealed interface Case {}")
// TODO: Print `getCase()` method to allow for easy pattern matching.
}

private func printEnumStaticInitializers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
Expand All @@ -222,11 +222,54 @@ extension JNISwift2JavaGenerator {
}

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
//
// }
// }
for enumCase in decl.cases {
guard let translatedCase = self.translatedEnumCase(for: enumCase) else {
return
}

let members = translatedCase.translatedValues.map {
$0.parameter.renderParameter()
}

let caseName = enumCase.name.firstCharacterUppercased
let hasParameters = !enumCase.parameters.isEmpty

// Print record
printer.printBraceBlock("public record \(caseName)(\(members.joined(separator: ", "))) implements Case") { printer in
if hasParameters {
let nativeResults = zip(translatedCase.translatedValues, translatedCase.conversions).map { value, conversion in
"\(conversion.native.javaType) \(value.parameter.name)"
}
printer.print(#"@SuppressWarnings("unused")"#)
printer.printBraceBlock("static \(caseName) fromJNI(\(nativeResults.joined(separator: ", ")))") { printer in
let memberValues = zip(translatedCase.translatedValues, translatedCase.conversions).map { (value, conversion) in
let result = conversion.translated.conversion.render(&printer, value.parameter.name)
return result
}
printer.print("return new \(caseName)(\(memberValues.joined(separator: ", ")));")
}
}
}

// TODO: Optimize when all values can just be passed directly, instead of going through "middle type"?

// Print method to get enum as case
printer.printBraceBlock("public Optional<\(caseName)> getAs\(caseName)()") { printer in
// TODO: Check that discriminator is OK
if hasParameters {
printer.print(
"""
return Optional.of($getAs\(caseName)(this.$memoryAddress()));
"""
)
} else {
printer.print("return Optional.of(new \(caseName)());")
}
}
printer.print("private static native \(caseName) $getAs\(caseName)(long self);")

printer.println()
}
}

private func printFunctionDowncallMethods(
Expand Down
Loading
Loading