Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Expand Up @@ -63,6 +63,10 @@ public func withBuffer(body: (UnsafeRawBufferPointer) -> Void) {
body(globalBuffer)
}

public func globalReceiveSomeDataProtocol(data: some DataProtocol) {
p(Array(data).description)
}

// ==== Internal helpers

func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@
import org.swift.swiftkit.ffm.AllocatingSwiftArena;
import org.swift.swiftkit.ffm.SwiftRuntime;

import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;

public class HelloJava2Swift {

public static void main(String[] args) {
Expand Down Expand Up @@ -95,6 +91,12 @@ static void examples() {
});
}

try (var arena = AllocatingSwiftArena.ofConfined()) {
var bytes = arena.allocateFrom("hello");
var dat = Data.init(bytes, bytes.byteSize(), arena);
MySwiftLibrary.globalReceiveSomeDataProtocol(dat);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ktoso I'd be happy to make this and Data thing test cases, but where exactly I should write? Any PR testing runs Samples:SwiftKitSampleApp:test?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So every example has a ci-validate.sh and you can add a ./gradle test in there in Samples/SwiftKitSampleApp/ci-validate.sh and add a test in Samples/SwiftKitSampleApp/src/test/java/com/example/swift :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh boy I just realized we have tests there but we've not been running them huh. so yeah please add a gradle test there :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

}


System.out.println("DONE.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ extension CType {
init(cdeclType: SwiftType) throws {
switch cdeclType {
case .nominal(let nominalType):
if let knownType = nominalType.nominalTypeDecl.knownStandardLibraryType {
if let knownType = nominalType.nominalTypeDecl.knownTypeKind {
if let primitiveCType = knownType.primitiveCType {
self = primitiveCType
return
Expand Down Expand Up @@ -68,7 +68,7 @@ extension CType {
case .optional(let wrapped) where wrapped.isPointer:
try self.init(cdeclType: wrapped)

case .metatype, .optional, .tuple:
case .metatype, .optional, .tuple, .opaque, .existential:
throw CDeclToCLoweringError.invalidCDeclType(cdeclType)
}
}
Expand Down Expand Up @@ -125,7 +125,7 @@ extension SwiftKnownTypeDeclKind {
.qualified(const: true, volatile: false, type: .void)
)
case .void: .void
case .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, .unsafeBufferPointer, .unsafeMutableBufferPointer, .string, .data:
case .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, .unsafeBufferPointer, .unsafeMutableBufferPointer, .string, .data, .dataProtocol:
nil
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ extension FFMSwift2JavaGenerator {
struct CdeclLowering {
var knownTypes: SwiftKnownTypes

init(knownTypes: SwiftKnownTypes) {
self.knownTypes = knownTypes
}

init(symbolTable: SwiftSymbolTable) {
self.knownTypes = SwiftKnownTypes(symbolTable: symbolTable)
}
Expand Down Expand Up @@ -165,7 +169,7 @@ struct CdeclLowering {
)

case .nominal(let nominal):
if let knownType = nominal.nominalTypeDecl.knownStandardLibraryType {
if let knownType = nominal.nominalTypeDecl.knownTypeKind {
if convention == .inout {
// FIXME: Support non-trivial 'inout' for builtin types.
throw LoweringError.inoutNotSupported(type)
Expand Down Expand Up @@ -320,6 +324,18 @@ struct CdeclLowering {
conversion: conversion
)

case .opaque(let proto), .existential(let proto):
// If the protocol has a known representative implementation, e.g. `String` for `StringProtocol`
// Translate it as the concrete type.
// NOTE: This is a temporary workaround until we add support for generics.
if
let knownProtocol = proto.asNominalTypeDeclaration?.knownTypeKind,
let concreteTy = knownTypes.representativeType(of: knownProtocol)
{
return try lowerParameter(concreteTy, convention: convention, parameterName: parameterName)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

throw LoweringError.unhandledType(type)

case .optional:
throw LoweringError.unhandledType(type)
}
Expand Down Expand Up @@ -386,7 +402,7 @@ struct CdeclLowering {

switch type {
case .nominal(let nominal):
if let knownType = nominal.nominalTypeDecl.knownStandardLibraryType {
if let knownType = nominal.nominalTypeDecl.knownTypeKind {
switch knownType {
case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer:
// pointer buffers are lowered to (raw-pointer, count) pair.
Expand Down Expand Up @@ -421,7 +437,7 @@ struct CdeclLowering {
// Custom types are not supported yet.
throw LoweringError.unhandledType(type)

case .function, .metatype, .optional, .tuple:
case .function, .metatype, .optional, .tuple, .existential, .opaque:
// TODO: Implement
throw LoweringError.unhandledType(type)
}
Expand Down Expand Up @@ -454,7 +470,7 @@ struct CdeclLowering {

case .nominal(let nominal):
// Types from the Swift standard library that we know about.
if let knownType = nominal.nominalTypeDecl.knownStandardLibraryType {
if let knownType = nominal.nominalTypeDecl.knownTypeKind {
switch knownType {
case .unsafePointer, .unsafeMutablePointer:
// Typed pointers are lowered to corresponding raw forms.
Expand Down Expand Up @@ -575,7 +591,7 @@ struct CdeclLowering {
conversion: .tupleExplode(conversions, name: outParameterName)
)

case .function(_), .optional(_):
case .function, .optional, .existential, .opaque:
throw LoweringError.unhandledType(type)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,16 @@ extension FFMSwift2JavaGenerator {
}

struct JavaTranslation {
var symbolTable: SwiftSymbolTable
var knownTypes: SwiftKnownTypes

init(symbolTable: SwiftSymbolTable) {
self.knownTypes = SwiftKnownTypes(symbolTable: symbolTable)
}

func translate(
_ decl: ImportedFunc
) throws -> TranslatedFunctionDecl {
let lowering = CdeclLowering(symbolTable: symbolTable)
let lowering = CdeclLowering(knownTypes: knownTypes)
let loweredSignature = try lowering.lowerFunctionSignature(decl.functionSignature)

// Name.
Expand Down Expand Up @@ -208,7 +212,7 @@ extension FFMSwift2JavaGenerator {

switch type {
case .nominal(let nominal):
if let knownType = nominal.nominalTypeDecl.knownStandardLibraryType {
if let knownType = nominal.nominalTypeDecl.knownTypeKind {
switch knownType {
case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer:
return TranslatedParameter(
Expand Down Expand Up @@ -248,11 +252,12 @@ extension FFMSwift2JavaGenerator {
// 'self'
let selfParameter: TranslatedParameter?
if case .instance(let swiftSelf) = swiftSignature.selfParameter {
selfParameter = try self.translate(
swiftParam: swiftSelf,
selfParameter = try self.translateParameter(
type: swiftSelf.type,
convention: swiftSelf.convention,
parameterName: swiftSelf.parameterName ?? "self",
loweredParam: loweredFunctionSignature.selfParameter!,
methodName: methodName,
parameterName: swiftSelf.parameterName ?? "self"
methodName: methodName
)
} else {
selfParameter = nil
Expand All @@ -263,11 +268,12 @@ extension FFMSwift2JavaGenerator {
.map { (idx, swiftParam) in
let loweredParam = loweredFunctionSignature.parameters[idx]
let parameterName = swiftParam.parameterName ?? "_\(idx)"
return try self.translate(
swiftParam: swiftParam,
return try self.translateParameter(
type: swiftParam.type,
convention: swiftParam.convention,
parameterName: parameterName,
loweredParam: loweredParam,
methodName: methodName,
parameterName: parameterName
methodName: methodName
)
}

Expand All @@ -285,13 +291,13 @@ extension FFMSwift2JavaGenerator {
}

/// Translate a Swift API parameter to the user-facing Java API parameter.
func translate(
swiftParam: SwiftParameter,
func translateParameter(
type swiftType: SwiftType,
convention: SwiftParameterConvention,
parameterName: String,
loweredParam: LoweredParameter,
methodName: String,
parameterName: String
methodName: String
) throws -> TranslatedParameter {
let swiftType = swiftParam.type

// If there is a 1:1 mapping between this Swift type and a C type, that can
// be expressed as a Java primitive type.
Expand Down Expand Up @@ -319,8 +325,8 @@ extension FFMSwift2JavaGenerator {
)

case .nominal(let swiftNominalType):
if let knownType = swiftNominalType.nominalTypeDecl.knownStandardLibraryType {
if swiftParam.convention == .inout {
if let knownType = swiftNominalType.nominalTypeDecl.knownTypeKind {
if convention == .inout {
// FIXME: Support non-trivial 'inout' for builtin types.
throw JavaTranslationError.inoutNotSupported(swiftType)
}
Expand Down Expand Up @@ -388,6 +394,26 @@ extension FFMSwift2JavaGenerator {
conversion: .call(.placeholder, function: "\(methodName).$toUpcallStub", withArena: true)
)

case .existential(let proto), .opaque(let proto):
// If the protocol has a known representative implementation, e.g. `String` for `StringProtocol`
// Translate it as the concrete type.
// NOTE: This is a temporary workaround until we add support for generics.
if
let knownProtocol = proto.asNominalTypeDeclaration?.knownTypeKind,
let concreteTy = knownTypes.representativeType(of: knownProtocol)
{
return try translateParameter(
type: concreteTy,
convention: convention,
parameterName: parameterName,
loweredParam: loweredParam,
methodName: methodName
)
}

// Otherwise, not supported yet.
throw JavaTranslationError.unhandledType(swiftType)

case .optional:
throw JavaTranslationError.unhandledType(swiftType)
}
Expand Down Expand Up @@ -422,7 +448,7 @@ extension FFMSwift2JavaGenerator {
)

case .nominal(let swiftNominalType):
if let knownType = swiftNominalType.nominalTypeDecl.knownStandardLibraryType {
if let knownType = swiftNominalType.nominalTypeDecl.knownTypeKind {
switch knownType {
case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer:
return TranslatedResult(
Expand Down Expand Up @@ -476,7 +502,7 @@ extension FFMSwift2JavaGenerator {
// TODO: Implement.
throw JavaTranslationError.unhandledType(swiftType)

case .optional, .function:
case .optional, .function, .existential, .opaque:
throw JavaTranslationError.unhandledType(swiftType)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ extension JNISwift2JavaGenerator {
func translate(swiftType: SwiftType) -> JavaType {
switch swiftType {
case .nominal(let nominalType):
if let knownType = nominalType.nominalTypeDecl.knownStandardLibraryType {
if let knownType = nominalType.nominalTypeDecl.knownTypeKind {
guard let javaType = translate(knownType: knownType) else {
fatalError("unsupported known type: \(knownType)")
}
Expand All @@ -78,7 +78,7 @@ extension JNISwift2JavaGenerator {
case .tuple([]):
return .void

case .metatype, .optional, .tuple, .function:
case .metatype, .optional, .tuple, .function, .existential, .opaque:
fatalError("unsupported type: \(self)")
}
}
Expand All @@ -99,10 +99,8 @@ extension JNISwift2JavaGenerator {
.unsafeRawPointer, .unsafeMutableRawPointer,
.unsafePointer, .unsafeMutablePointer,
.unsafeRawBufferPointer, .unsafeMutableRawBufferPointer,
.unsafeBufferPointer, .unsafeMutableBufferPointer:
.unsafeBufferPointer, .unsafeMutableBufferPointer, .data, .dataProtocol:
nil
case .data:
fatalError("unimplemented")
}
}
}
Expand Down
12 changes: 8 additions & 4 deletions Sources/JExtractSwiftLib/Swift2JavaTranslator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ extension Swift2JavaTranslator {
// If any API uses 'Foundation.Data', import 'Data' as if it's declared in
// this module.
if let dataDecl = self.symbolTable[.data] {
if self.isUsing(dataDecl) {
let dataProtocolDecl = self.symbolTable[.dataProtocol]!
if self.isUsing(where: { $0 == dataDecl || $0 == dataProtocolDecl }) {
visitor.visit(nominalDecl: dataDecl.syntax!.asNominal!, in: nil)
}
}
Expand All @@ -117,12 +118,13 @@ extension Swift2JavaTranslator {
)
}

/// Check if any of the imported decls uses the specified nominal declaration.
func isUsing(_ decl: SwiftNominalTypeDeclaration) -> Bool {
/// Check if any of the imported decls uses a nominal declaration that satisfies
/// the given predicate.
func isUsing(where predicate: (SwiftNominalTypeDeclaration) -> Bool) -> Bool {
func check(_ type: SwiftType) -> Bool {
switch type {
case .nominal(let nominal):
return nominal.nominalTypeDecl == decl
return predicate(nominal.nominalTypeDecl)
case .optional(let ty):
return check(ty)
case .tuple(let tuple):
Expand All @@ -131,6 +133,8 @@ extension Swift2JavaTranslator {
return check(fn.resultType) || fn.parameters.contains(where: { check($0.type) })
case .metatype(let ty):
return check(ty)
case .existential(let ty), .opaque(let ty):
return check(ty)
}
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ private let swiftSourceFile: SourceFileSyntax = """
"""

private let foundationSourceFile: SourceFileSyntax = """
public struct Data {
public protocol DataProtocol {}

public struct Data: DataProtocol {
public init(bytes: UnsafeRawPointer, count: Int)
public var count: Int { get }
public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) -> Void)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ enum SwiftKnownTypeDeclKind: String, Hashable {
case void = "Swift.Void"
case string = "Swift.String"

case dataProtocol = "Foundation.DataProtocol"
case data = "Foundation.Data"

var moduleAndName: (module: String, name: String) {
Expand Down
12 changes: 12 additions & 0 deletions Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ struct SwiftKnownTypes {
var unsafeRawPointer: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.unsafeRawPointer])) }
var unsafeMutableRawPointer: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.unsafeMutableRawPointer])) }

var dataProtocol: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.dataProtocol])) }
var data: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.data])) }

func unsafePointer(_ pointeeType: SwiftType) -> SwiftType {
.nominal(
SwiftNominalType(
Expand Down Expand Up @@ -70,4 +73,13 @@ struct SwiftKnownTypes {
)
)
}

/// Returns the known representative concrete type if there is one for the
/// given protocol kind. E.g. `String` for `StringProtocol`
func representativeType(of knownProtocol: SwiftKnownTypeDeclKind) -> SwiftType? {
switch knownProtocol {
case .dataProtocol: return self.data
default: return nil
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ package class SwiftNominalTypeDeclaration {

/// Identify this nominal declaration as one of the known standard library
/// types, like 'Swift.Int[.
lazy var knownStandardLibraryType: SwiftKnownTypeDeclKind? = {
lazy var knownTypeKind: SwiftKnownTypeDeclKind? = {
self.computeKnownStandardLibraryType()
}()

Expand Down
Loading