Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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 @@ -61,4 +61,9 @@ public struct MySwiftStruct {
public func makeRandomIntMethod() -> Int {
return Int.random(in: 1..<256)
}

public subscript() -> Int {
get { return len }
set { len = newValue }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,15 @@ void create_struct() {
assertEquals(len, struct.getLength());
}
}

@Test
void testSubscript() {
try (var arena = AllocatingSwiftArena.ofConfined()) {
MySwiftStruct s = MySwiftStruct.init(1337, 42, arena);
long currentValue = s.getSubscript();
s.setSubscript(66);
assertEquals(42, currentValue);
assertEquals(66, s.getLength());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,9 @@ public struct MySwiftStruct {
self.cap += value
return self.cap
}

public subscript() -> Int64 {
get { return len }
set { len = newValue }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,15 @@ void increaseCap() {
assertEquals(1347, s.getCapacity());
}
}

@Test
void testSubscript() {
try (var arena = SwiftArena.ofConfined()) {
MySwiftStruct s = MySwiftStruct.init(1337, 42, arena);
long currentValue = s.getSubscript();
s.setSubscript(66);
assertEquals(42, currentValue);
assertEquals(66, s.getLen());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -812,11 +812,10 @@ extension LoweredFunctionSignature {

// Build callee expression.
let callee: ExprSyntax = if let selfExpr {
if case .initializer = apiKind {
switch apiKind {
// Don't bother to create explicit ${Self}.init expression.
selfExpr
} else {
ExprSyntax(MemberAccessExprSyntax(base: selfExpr, name: .identifier(swiftAPIName)))
case .initializer, .subscriptGetter, .subscriptSetter: selfExpr
default: ExprSyntax(MemberAccessExprSyntax(base: selfExpr, name: .identifier(swiftAPIName)))
}
} else {
ExprSyntax(DeclReferenceExprSyntax(baseName: .identifier(swiftAPIName)))
Expand Down Expand Up @@ -845,6 +844,18 @@ extension LoweredFunctionSignature {
case .enumCase:
// This should not be called, but let's fatalError.
fatalError("Enum cases are not supported with FFM.")

case .subscriptGetter:
let parameters = paramExprs.map { $0.description }.joined(separator: ", ")
resultExpr = "\(callee)[\(raw: parameters)]"
case .subscriptSetter:
assert(paramExprs.count >= 1)

var argumentsWithoutNewValue = paramExprs
let newValueArgument = argumentsWithoutNewValue.removeLast()

let parameters = argumentsWithoutNewValue.map { $0.description }.joined(separator: ", ")
resultExpr = "\(callee)[\(raw: parameters)] = \(newValueArgument)"
}

// Lower the result.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ extension FFMSwift2JavaGenerator {

// Name.
let javaName = switch decl.apiKind {
case .getter: decl.javaGetterName
case .setter: decl.javaSetterName
case .getter, .subscriptGetter: decl.javaGetterName
case .setter, .subscriptSetter: decl.javaSetterName
case .function, .initializer, .enumCase: decl.name
}

Expand Down
4 changes: 4 additions & 0 deletions Sources/JExtractSwiftLib/ImportedDecls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ package enum SwiftAPIKind {
case getter
case setter
case enumCase
case subscriptGetter
case subscriptSetter
}

/// Describes a Swift nominal type (e.g., a class, struct, enum) that has been
Expand Down Expand Up @@ -179,6 +181,8 @@ public final class ImportedFunc: ImportedDecl, CustomStringConvertible {
case .setter: "setter:"
case .enumCase: "case:"
case .function, .initializer: ""
case .subscriptGetter: "subscriptGetter:"
case .subscriptSetter: "subscriptSetter:"
}

let context = if let parentType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@ extension JNISwift2JavaGenerator {

// Name.
let javaName = switch decl.apiKind {
case .getter: decl.javaGetterName
case .setter: decl.javaSetterName
case .getter, .subscriptGetter: decl.javaGetterName
case .setter, .subscriptSetter: decl.javaSetterName
case .function, .initializer, .enumCase: decl.name
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,19 @@ extension JNISwift2JavaGenerator {
}

result = "\(callee).\(decl.name) = \(newValueArgument)"
case .subscriptGetter:
let parameters = arguments.joined(separator: ", ")
result = "\(callee)[\(parameters)]"
case .subscriptSetter:
guard let newValueArgument = arguments.last else {
fatalError("Setter did not contain newValue parameter: \(decl)")
}

var argumentsWithoutNewValue = arguments
argumentsWithoutNewValue.removeLast()

let parameters = argumentsWithoutNewValue.joined(separator: ", ")
result = "\(callee)[\(parameters)] = \(newValueArgument)"
}

// Lower the result.
Expand Down
151 changes: 111 additions & 40 deletions Sources/JExtractSwiftLib/Swift2JavaVisitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
//===----------------------------------------------------------------------===//

import Foundation
import SwiftJavaConfigurationShared
import SwiftParser
import SwiftSyntax
import SwiftJavaConfigurationShared

final class Swift2JavaVisitor {
let translator: Swift2JavaTranslator
Expand Down Expand Up @@ -53,19 +53,18 @@ final class Swift2JavaVisitor {
case .extensionDecl(let node):
self.visit(extensionDecl: node, in: parent, sourceFilePath: sourceFilePath)
case .typeAliasDecl:
break // TODO: Implement; https://github.com/swiftlang/swift-java/issues/338
break // TODO: Implement; https://github.com/swiftlang/swift-java/issues/338
case .associatedTypeDecl:
break // TODO: Implement associated types
break // TODO: Implement associated types

case .initializerDecl(let node):
self.visit(initializerDecl: node, in: parent)
case .functionDecl(let node):
self.visit(functionDecl: node, in: parent, sourceFilePath: sourceFilePath)
case .variableDecl(let node):
self.visit(variableDecl: node, in: parent, sourceFilePath: sourceFilePath)
case .subscriptDecl:
// TODO: Implement subscripts
break
case .subscriptDecl(let node):
self.visit(subscriptDecl: node, in: parent)
case .enumCaseDecl(let node):
self.visit(enumCaseDecl: node, in: parent)

Expand All @@ -75,7 +74,8 @@ final class Swift2JavaVisitor {
}

func visit(
nominalDecl node: some DeclSyntaxProtocol & DeclGroupSyntax & NamedDeclSyntax & WithAttributesSyntax & WithModifiersSyntax,
nominalDecl node: some DeclSyntaxProtocol & DeclGroupSyntax & NamedDeclSyntax
& WithAttributesSyntax & WithModifiersSyntax,
in parent: ImportedNominalType?,
sourceFilePath: String
) {
Expand Down Expand Up @@ -115,7 +115,7 @@ final class Swift2JavaVisitor {
}

func visit(
functionDecl node: FunctionDeclSyntax,
functionDecl node: FunctionDeclSyntax,
in typeContext: ImportedNominalType?,
sourceFilePath: String
) {
Expand Down Expand Up @@ -154,7 +154,7 @@ final class Swift2JavaVisitor {
}

func visit(
enumCaseDecl node: EnumCaseDeclSyntax,
enumCaseDecl node: EnumCaseDeclSyntax,
in typeContext: ImportedNominalType?
) {
guard let typeContext else {
Expand Down Expand Up @@ -200,7 +200,7 @@ final class Swift2JavaVisitor {
}

func visit(
variableDecl node: VariableDeclSyntax,
variableDecl node: VariableDeclSyntax,
in typeContext: ImportedNominalType?,
sourceFilePath: String
) {
Expand All @@ -216,37 +216,21 @@ final class Swift2JavaVisitor {

self.log.debug("Import variable: \(node.kind) '\(node.qualifiedNameForDebug)'")

func importAccessor(kind: SwiftAPIKind) throws {
let signature = try SwiftFunctionSignature(
node,
isSet: kind == .setter,
enclosingType: typeContext?.swiftType,
lookupContext: translator.lookupContext
)

let imported = ImportedFunc(
module: translator.swiftModuleName,
swiftDecl: node,
name: varName,
apiKind: kind,
functionSignature: signature
)

log.debug("Record imported variable accessor \(kind == .getter ? "getter" : "setter"):\(node.qualifiedNameForDebug)")
if let typeContext {
typeContext.variables.append(imported)
} else {
translator.importedGlobalVariables.append(imported)
}
}

do {
let supportedAccessors = node.supportedAccessorKinds(binding: binding)
if supportedAccessors.contains(.get) {
try importAccessor(kind: .getter)
try importAccessor(
from: DeclSyntax(node),
in: typeContext,
kind: .getter,
name: varName)
}
if supportedAccessors.contains(.set) {
try importAccessor(kind: .setter)
try importAccessor(
from: DeclSyntax(node),
in: typeContext,
kind: .setter,
name: varName)
}
} catch {
self.log.debug("Failed to import: \(node.qualifiedNameForDebug); \(error)")
Expand Down Expand Up @@ -289,10 +273,89 @@ final class Swift2JavaVisitor {
typeContext.initializers.append(imported)
}

private func visit(
subscriptDecl node: SubscriptDeclSyntax,
in typeContext: ImportedNominalType?,
) {
guard node.shouldExtract(config: config, log: log, in: typeContext) else {
return
}

guard let accessorBlock = node.accessorBlock else {
return
}

let name = "subscript"
let accessors = accessorBlock.supportedAccessorKinds()

do {
if accessors.contains(.get) {
try importAccessor(
from: DeclSyntax(node),
in: typeContext,
kind: .subscriptGetter,
name: name)
}
if accessors.contains(.set) {
try importAccessor(
from: DeclSyntax(node),
in: typeContext,
kind: .subscriptSetter,
name: name)
}
} catch {
self.log.debug("Failed to import: \(node.qualifiedNameForDebug); \(error)")
}
}

private func importAccessor(
from node: DeclSyntax,
in typeContext: ImportedNominalType?,
kind: SwiftAPIKind,
name: String
) throws {
let signature: SwiftFunctionSignature

switch node.as(DeclSyntaxEnum.self) {
case .variableDecl(let varNode):
signature = try SwiftFunctionSignature(
varNode,
isSet: kind == .setter,
enclosingType: typeContext?.swiftType,
lookupContext: translator.lookupContext)
case .subscriptDecl(let subscriptNode):
signature = try SwiftFunctionSignature(
subscriptNode,
isSet: kind == .subscriptSetter,
enclosingType: typeContext?.swiftType,
lookupContext: translator.lookupContext)
default:
log.warning("Not supported declaration type \(node.kind) while calling importAccessor!")
return
}

let imported = ImportedFunc(
module: translator.swiftModuleName,
swiftDecl: node,
name: name,
apiKind: kind,
functionSignature: signature
)

log.debug(
"Record imported variable accessor \(kind == .getter || kind == .subscriptGetter ? "getter" : "setter"):\(node.qualifiedNameForDebug)"
)
if let typeContext {
typeContext.variables.append(imported)
} else {
translator.importedGlobalVariables.append(imported)
}
}

private func synthesizeRawRepresentableConformance(
enumDecl node: EnumDeclSyntax,
in parent: ImportedNominalType?
) {
) {
guard let imported = translator.importedNominalType(node, parent: parent) else {
return
}
Expand All @@ -304,15 +367,21 @@ final class Swift2JavaVisitor {
),
inheritanceType.isRawTypeCompatible
{
if !imported.variables.contains(where: { $0.name == "rawValue" && $0.functionSignature.result.type != inheritanceType }) {
if !imported.variables.contains(where: {
$0.name == "rawValue" && $0.functionSignature.result.type != inheritanceType
}) {
let decl: DeclSyntax = "public var rawValue: \(raw: inheritanceType.description) { get }"
self.visit(decl: decl, in: imported, sourceFilePath: imported.sourceFilePath)
}

// FIXME: why is this un-used
imported.variables.first?.signatureString

if !imported.initializers.contains(where: { $0.functionSignature.parameters.count == 1 && $0.functionSignature.parameters.first?.parameterName == "rawValue" && $0.functionSignature.parameters.first?.type == inheritanceType }) {
if !imported.initializers.contains(where: {
$0.functionSignature.parameters.count == 1
&& $0.functionSignature.parameters.first?.parameterName == "rawValue"
&& $0.functionSignature.parameters.first?.type == inheritanceType
}) {
let decl: DeclSyntax = "public init?(rawValue: \(raw: inheritanceType))"
self.visit(decl: decl, in: imported, sourceFilePath: imported.sourceFilePath)
}
Expand All @@ -330,7 +399,9 @@ extension DeclSyntaxProtocol where Self: WithModifiersSyntax & WithAttributesSyn
}

guard meetsRequiredAccessLevel else {
log.debug("Skip import '\(self.qualifiedNameForDebug)': not at least \(config.effectiveMinimumInputAccessLevelMode)")
log.debug(
"Skip import '\(self.qualifiedNameForDebug)': not at least \(config.effectiveMinimumInputAccessLevelMode)"
)
return false
}
guard !attributes.contains(where: { $0.isJava }) else {
Expand Down
Loading