Skip to content

Commit

Permalink
Merge pull request #32 from skiptools/bridgetuple
Browse files Browse the repository at this point in the history
Support bridging tuples
  • Loading branch information
aabewhite authored Dec 5, 2024
2 parents 2a0d466 + 1d21ddf commit 4a9086e
Show file tree
Hide file tree
Showing 8 changed files with 211 additions and 0 deletions.
172 changes: 172 additions & 0 deletions Sources/SkipBridge/Tuples.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// Copyright 2024 Skip
//
// This is free software: you can redistribute and/or modify it
// under the terms of the GNU Lesser General Public License 3.0
// as published by the Free Software Foundation https://fsf.org

/// Tuple conversions.
public final class SwiftTuple {
public static func javaObject<T0, T1>(for tuple: (T0, T1)?, options: JConvertibleOptions) -> JavaObjectPointer? {
guard let tuple else {
return nil
}
let t0_java = (tuple.0 as! JConvertible).toJavaObject(options: options).toJavaParameter(options: options)
let t1_java = (tuple.1 as! JConvertible).toJavaObject(options: options).toJavaParameter(options: options)
if options.contains(.kotlincompat) {
return try! Java_Pair.create(ctor: Java_Pair_constructor_methodID, args: [t0_java, t1_java])
} else {
return try! Java_Tuple2.create(ctor: Java_Tuple2_constructor_methodID, args: [t0_java, t1_java])
}
}

public static func tuple<T0, T1>(forJavaObject tuple: JavaObjectPointer?, options: JConvertibleOptions) -> (T0, T1)? {
guard let tuple else {
return nil
}
let t0MethodID: JavaMethodID
let t1MethodID: JavaMethodID
if options.contains(.kotlincompat) {
t0MethodID = Java_Pair_first_methodID
t1MethodID = Java_Pair_second_methodID
} else {
t0MethodID = Java_Tuple2_e0_methodID
t1MethodID = Java_Tuple2_e1_methodID
}
let t0_java = try! JavaObjectPointer?.call(t0MethodID, on: tuple, options: options, args: [])
let t0_swift = (T0.self as! JConvertible.Type).fromJavaObject(t0_java, options: options)
let t1_java = try! JavaObjectPointer?.call(t1MethodID, on: tuple, options: options, args: [])
let t1_swift = (T1.self as! JConvertible.Type).fromJavaObject(t1_java, options: options)
return (t0_swift as! T0, t1_swift as! T1)
}

public static func javaObject<T0, T1, T2>(for tuple: (T0, T1, T2)?, options: JConvertibleOptions) -> JavaObjectPointer? {
guard let tuple else {
return nil
}
let t0_java = (tuple.0 as! JConvertible).toJavaObject(options: options).toJavaParameter(options: options)
let t1_java = (tuple.1 as! JConvertible).toJavaObject(options: options).toJavaParameter(options: options)
let t2_java = (tuple.2 as! JConvertible).toJavaObject(options: options).toJavaParameter(options: options)
if options.contains(.kotlincompat) {
return try! Java_Triple.create(ctor: Java_Triple_constructor_methodID, args: [t0_java, t1_java, t2_java])
} else {
return try! Java_Tuple3.create(ctor: Java_Tuple3_constructor_methodID, args: [t0_java, t1_java, t2_java])
}
}

public static func tuple<T0, T1, T2>(forJavaObject tuple: JavaObjectPointer?, options: JConvertibleOptions) -> (T0, T1, T2)? {
guard let tuple else {
return nil
}
let t0MethodID: JavaMethodID
let t1MethodID: JavaMethodID
let t2MethodID: JavaMethodID
if options.contains(.kotlincompat) {
t0MethodID = Java_Triple_first_methodID
t1MethodID = Java_Triple_second_methodID
t2MethodID = Java_Triple_third_methodID
} else {
t0MethodID = Java_Tuple3_e0_methodID
t1MethodID = Java_Tuple3_e1_methodID
t2MethodID = Java_Tuple3_e2_methodID
}
let t0_java = try! JavaObjectPointer?.call(t0MethodID, on: tuple, options: options, args: [])
let t0_swift = (T0.self as! JConvertible.Type).fromJavaObject(t0_java, options: options)
let t1_java = try! JavaObjectPointer?.call(t1MethodID, on: tuple, options: options, args: [])
let t1_swift = (T1.self as! JConvertible.Type).fromJavaObject(t1_java, options: options)
let t2_java = try! JavaObjectPointer?.call(t2MethodID, on: tuple, options: options, args: [])
let t2_swift = (T2.self as! JConvertible.Type).fromJavaObject(t2_java, options: options)
return (t0_swift as! T0, t1_swift as! T1, t2_swift as! T2)
}

public static func javaObject<T0, T1, T2, T3>(for tuple: (T0, T1, T2, T3)?, options: JConvertibleOptions) -> JavaObjectPointer? {
guard let tuple else {
return nil
}
let t0_java = (tuple.0 as! JConvertible).toJavaObject(options: options).toJavaParameter(options: options)
let t1_java = (tuple.1 as! JConvertible).toJavaObject(options: options).toJavaParameter(options: options)
let t2_java = (tuple.2 as! JConvertible).toJavaObject(options: options).toJavaParameter(options: options)
let t3_java = (tuple.3 as! JConvertible).toJavaObject(options: options).toJavaParameter(options: options)
return try! Java_Tuple4.create(ctor: Java_Tuple4_constructor_methodID, args: [t0_java, t1_java, t2_java, t3_java])
}

public static func tuple<T0, T1, T2, T3>(forJavaObject tuple: JavaObjectPointer?, options: JConvertibleOptions) -> (T0, T1, T2, T3)? {
guard let tuple else {
return nil
}
let t0_java = try! JavaObjectPointer?.call(Java_Tuple4_e0_methodID, on: tuple, options: options, args: [])
let t0_swift = (T0.self as! JConvertible.Type).fromJavaObject(t0_java, options: options)
let t1_java = try! JavaObjectPointer?.call(Java_Tuple4_e1_methodID, on: tuple, options: options, args: [])
let t1_swift = (T1.self as! JConvertible.Type).fromJavaObject(t1_java, options: options)
let t2_java = try! JavaObjectPointer?.call(Java_Tuple4_e2_methodID, on: tuple, options: options, args: [])
let t2_swift = (T2.self as! JConvertible.Type).fromJavaObject(t2_java, options: options)
let t3_java = try! JavaObjectPointer?.call(Java_Tuple4_e3_methodID, on: tuple, options: options, args: [])
let t3_swift = (T3.self as! JConvertible.Type).fromJavaObject(t3_java, options: options)
return (t0_swift as! T0, t1_swift as! T1, t2_swift as! T2, t3_swift as! T3)
}

public static func javaObject<T0, T1, T2, T3, T4>(for tuple: (T0, T1, T2, T3, T4)?, options: JConvertibleOptions) -> JavaObjectPointer? {
guard let tuple else {
return nil
}
let t0_java = (tuple.0 as! JConvertible).toJavaObject(options: options).toJavaParameter(options: options)
let t1_java = (tuple.1 as! JConvertible).toJavaObject(options: options).toJavaParameter(options: options)
let t2_java = (tuple.2 as! JConvertible).toJavaObject(options: options).toJavaParameter(options: options)
let t3_java = (tuple.3 as! JConvertible).toJavaObject(options: options).toJavaParameter(options: options)
let t4_java = (tuple.4 as! JConvertible).toJavaObject(options: options).toJavaParameter(options: options)
return try! Java_Tuple4.create(ctor: Java_Tuple5_constructor_methodID, args: [t0_java, t1_java, t2_java, t3_java, t4_java])
}

public static func tuple<T0, T1, T2, T3, T4>(forJavaObject tuple: JavaObjectPointer?, options: JConvertibleOptions) -> (T0, T1, T2, T3, T4)? {
guard let tuple else {
return nil
}
let t0_java = try! JavaObjectPointer?.call(Java_Tuple5_e0_methodID, on: tuple, options: options, args: [])
let t0_swift = (T0.self as! JConvertible.Type).fromJavaObject(t0_java, options: options)
let t1_java = try! JavaObjectPointer?.call(Java_Tuple5_e1_methodID, on: tuple, options: options, args: [])
let t1_swift = (T1.self as! JConvertible.Type).fromJavaObject(t1_java, options: options)
let t2_java = try! JavaObjectPointer?.call(Java_Tuple5_e2_methodID, on: tuple, options: options, args: [])
let t2_swift = (T2.self as! JConvertible.Type).fromJavaObject(t2_java, options: options)
let t3_java = try! JavaObjectPointer?.call(Java_Tuple5_e3_methodID, on: tuple, options: options, args: [])
let t3_swift = (T3.self as! JConvertible.Type).fromJavaObject(t3_java, options: options)
let t4_java = try! JavaObjectPointer?.call(Java_Tuple5_e4_methodID, on: tuple, options: options, args: [])
let t4_swift = (T4.self as! JConvertible.Type).fromJavaObject(t4_java, options: options)
return (t0_swift as! T0, t1_swift as! T1, t2_swift as! T2, t3_swift as! T3, t4_swift as! T4)
}
}

private let Java_Tuple2 = try! JClass(name: "skip/lib/Tuple2")
private let Java_Tuple2_constructor_methodID = Java_Tuple2.getMethodID(name: "<init>", sig: "(Ljava/lang/Object;Ljava/lang/Object;)V")!
private let Java_Tuple2_e0_methodID = Java_Tuple2.getMethodID(name: "get_e0", sig: "()Ljava/lang/Object;")!
private let Java_Tuple2_e1_methodID = Java_Tuple2.getMethodID(name: "get_e1", sig: "()Ljava/lang/Object;")!

private let Java_Pair = try! JClass(name: "kotlin/Pair")
private let Java_Pair_constructor_methodID = Java_Pair.getMethodID(name: "<init>", sig: "(Ljava/lang/Object;Ljava/lang/Object;)V")!
private let Java_Pair_first_methodID = Java_Pair.getMethodID(name: "getFirst", sig: "()Ljava/lang/Object;")!
private let Java_Pair_second_methodID = Java_Pair.getMethodID(name: "getSecond", sig: "()Ljava/lang/Object;")!

private let Java_Tuple3 = try! JClass(name: "skip/lib/Tuple3")
private let Java_Tuple3_constructor_methodID = Java_Tuple3.getMethodID(name: "<init>", sig: "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V")!
private let Java_Tuple3_e0_methodID = Java_Tuple3.getMethodID(name: "get_e0", sig: "()Ljava/lang/Object;")!
private let Java_Tuple3_e1_methodID = Java_Tuple3.getMethodID(name: "get_e1", sig: "()Ljava/lang/Object;")!
private let Java_Tuple3_e2_methodID = Java_Tuple3.getMethodID(name: "get_e2", sig: "()Ljava/lang/Object;")!

private let Java_Triple = try! JClass(name: "kotlin/Triple")
private let Java_Triple_constructor_methodID = Java_Triple.getMethodID(name: "<init>", sig: "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V")!
private let Java_Triple_first_methodID = Java_Triple.getMethodID(name: "getFirst", sig: "()Ljava/lang/Object;")!
private let Java_Triple_second_methodID = Java_Triple.getMethodID(name: "getSecond", sig: "()Ljava/lang/Object;")!
private let Java_Triple_third_methodID = Java_Triple.getMethodID(name: "getThird", sig: "()Ljava/lang/Object;")!

private let Java_Tuple4 = try! JClass(name: "skip/lib/Tuple4")
private let Java_Tuple4_constructor_methodID = Java_Tuple4.getMethodID(name: "<init>", sig: "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V")!
private let Java_Tuple4_e0_methodID = Java_Tuple4.getMethodID(name: "get_e0", sig: "()Ljava/lang/Object;")!
private let Java_Tuple4_e1_methodID = Java_Tuple4.getMethodID(name: "get_e1", sig: "()Ljava/lang/Object;")!
private let Java_Tuple4_e2_methodID = Java_Tuple4.getMethodID(name: "get_e2", sig: "()Ljava/lang/Object;")!
private let Java_Tuple4_e3_methodID = Java_Tuple4.getMethodID(name: "get_e3", sig: "()Ljava/lang/Object;")!

private let Java_Tuple5 = try! JClass(name: "skip/lib/Tuple5")
private let Java_Tuple5_constructor_methodID = Java_Tuple5.getMethodID(name: "<init>", sig: "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V")!
private let Java_Tuple5_e0_methodID = Java_Tuple5.getMethodID(name: "get_e0", sig: "()Ljava/lang/Object;")!
private let Java_Tuple5_e1_methodID = Java_Tuple5.getMethodID(name: "get_e1", sig: "()Ljava/lang/Object;")!
private let Java_Tuple5_e2_methodID = Java_Tuple5.getMethodID(name: "get_e2", sig: "()Ljava/lang/Object;")!
private let Java_Tuple5_e3_methodID = Java_Tuple5.getMethodID(name: "get_e3", sig: "()Ljava/lang/Object;")!
private let Java_Tuple5_e4_methodID = Java_Tuple5.getMethodID(name: "get_e4", sig: "()Ljava/lang/Object;")!
1 change: 1 addition & 0 deletions Sources/SkipBridgeToKotlinCompatSamples/Samples.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Foundation
public class Compat {
public let id: UUID
public var urls: [URL] = []
public var attempts: (Int, URL)?

public init(id: UUID) {
self.id = id
Expand Down
1 change: 1 addition & 0 deletions Sources/SkipBridgeToKotlinSamples/Samples.swift
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ public var swiftClosure1OptionalsVar: (String?) -> Int? = { s in s?.count }
public var swiftIntArrayVar = [1, 2, 3]
public var swiftStringSetVar: Set<String> = ["a", "b", "c"]
public var swiftIntStringDictionaryVar = [1: "a", 2: "b", 3: "c"]
public var swiftIntStringTuple = (1, "s")

// MARK: Functions

Expand Down
1 change: 1 addition & 0 deletions Sources/SkipBridgeToSwiftSamples/Samples.swift
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ public var kotlinClosure1OptionalsVar: (String?) -> Int? = { s in s?.count }
public var kotlinIntArrayVar = [1, 2, 3]
public var kotlinStringSetVar: Set<String> = ["a", "b", "c"]
public var kotlinIntStringDictionaryVar = [1: "a", 2: "b", 3: "c"]
public var kotlinIntStringTupleVar = (1, "s")

// MARK: Functions

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,10 @@ public func testSupport_kotlinClosure1OptionalsVar(value: String?) -> Int? {
return i1 == i2 ? i1 : (i1 ?? 0) * 10000 + (i2 ?? 0)
}

public func testSupport_kotlinClosure1Parameter(value: (Int) -> String) {
// We're only testing that using a closure as a parameter compiles cleanly
}

public func testSupport_kotlinIntArrayVar(value: [Int]) -> [Int] {
kotlinIntArrayVar = value
return kotlinIntArrayVar
Expand All @@ -499,6 +503,11 @@ public func testSupport_kotlinIntStringDictionaryVar(value: [Int: String]) -> [I
return kotlinIntStringDictionaryVar
}

public func testSupport_kotlinIntStringTupleVar(value: (Int, String)) -> (Int, String) {
kotlinIntStringTupleVar = value
return kotlinIntStringTupleVar
}

public func testSupport_callKotlinThrowingFunction(shouldThrow: Bool) throws -> Int {
return try kotlinThrowingFunction(shouldThrow: shouldThrow)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,17 @@ final class BridgeToKotlinCompatTests: XCTestCase {
XCTAssertEqual(list2[0], url)
#endif
}

func testCompatTuplePair() {
#if SKIP
let uuid = java.util.UUID.randomUUID()
let compat = Compat(id: uuid)
XCTAssertEqual(uuid, compat.id)

let url = java.net.URI("https://skip.tools")
compat.attempts = Pair(2, url)
XCTAssertEqual(2, compat.attempts?.first)
XCTAssertEqual(url, compat.attempts?.second)
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,14 @@ final class BridgeToKotlinTests: XCTestCase {
XCTAssertEqual(swiftIntStringDictionaryVar, Dictionary<Int, String>())
}

func testTuples() {
XCTAssertEqual(swiftIntStringTuple.0, 1)
XCTAssertEqual(swiftIntStringTuple.1, "s")
swiftIntStringTuple = (2, "a")
XCTAssertEqual(swiftIntStringTuple.0, 2)
XCTAssertEqual(swiftIntStringTuple.1, "a")
}

func testThrowsFunctions() throws {
do {
try swiftThrowingVoidFunction(shouldThrow: true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,12 @@ final class BridgeToSwiftTests: XCTestCase {
XCTAssertEqual(roundtripped[5], "e")
}

func testTuples() {
let roundtripped = testSupport_kotlinIntStringTupleVar(value: (2, "a"))
XCTAssertEqual(roundtripped.0, 2)
XCTAssertEqual(roundtripped.1, "a")
}

func testThrowingFunctions() throws {
do {
try testSupport_callKotlinThrowingVoidFunction(shouldThrow: true)
Expand Down

0 comments on commit 4a9086e

Please sign in to comment.