Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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 }
Copy link
Collaborator

Choose a reason for hiding this comment

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

can it set some other value and not the len perhaps? Just so we don't step on values when we write bigger tests

}
Copy link
Collaborator

Choose a reason for hiding this comment

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

What about a case with parameters?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't notice I lost them in git stash :/ Fixed

}
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);
Copy link
Collaborator

Choose a reason for hiding this comment

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

nice

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
Loading