Skip to content
Merged
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 @@ -1046,15 +1046,23 @@ extension LoweredFunctionSignature {
fatalError("Enum cases are not supported with FFM.")

case .subscriptGetter:
let parameters = paramExprs.map { $0.description }.joined(separator: ", ")
let parameters = paramExprs.enumerated()
.map { (i, argument) -> String in
LabeledExprSyntax(label: original.parameters[i].argumentLabel, expression: argument).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: ", ")
let parameters = argumentsWithoutNewValue.enumerated()
.map { (i, argument) -> String in
LabeledExprSyntax(label: original.parameters[i].argumentLabel, expression: argument).description
}
.joined(separator: ", ")
resultExpr = "\(callee)[\(raw: parameters)] = \(newValueArgument)"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -660,17 +660,30 @@ extension JNISwift2JavaGenerator {

result = "\(callee).\(decl.name) = \(newValueArgument)"
case .subscriptGetter:
let parameters = arguments.joined(separator: ", ")
let parameters = zip(
decl.functionSignature.parameters,
arguments,
).map { originalParam, argument in
let label = originalParam.argumentLabel.map { "\($0): " } ?? ""
return "\(label)\(argument)"
}
.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()
// Drop the trailing newValue parameter — it's the right-hand-side of
// the assignment, not part of the bracketed index expression
let indexParams = decl.functionSignature.parameters.dropLast()
let indexArgs = arguments.dropLast()

let parameters = argumentsWithoutNewValue.joined(separator: ", ")
let parameters = zip(indexParams, indexArgs).map { originalParam, argument in
let label = originalParam.argumentLabel.map { "\($0): " } ?? ""
return "\(label)\(argument)"
}
.joined(separator: ", ")
result = "\(callee)[\(parameters)] = \(newValueArgument)"
}

Expand Down
15 changes: 13 additions & 2 deletions Sources/SwiftExtract/SwiftTypes/SwiftFunctionSignature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -358,8 +358,19 @@ extension SwiftFunctionSignature {
)

let valueType: SwiftType = try SwiftType(subscriptNode.returnClause.type, lookupContext: lookupContext)
var nodeParameters = try subscriptNode.parameterClause.parameters.map { param in
try SwiftParameter(param, lookupContext: lookupContext)
var nodeParameters = try subscriptNode.parameterClause.parameters.map { param -> SwiftParameter in
var p = try SwiftParameter(param, lookupContext: lookupContext)
// Subscript parameters have no external argument label by default. A
// single-identifier subscript parameter `subscript(index: Int)` is
// called as `obj[5]`, not `obj[index: 5]` — only the explicit
// `subscript(label name: Int)` form (i.e. a `secondName` exists)
// surfaces an external label. SwiftParameter.init treats single-
// identifier params like function params (label == name); strip the
// label here for the subscript-specific call shape.
if param.secondName == nil {
p.argumentLabel = nil
}
return p
}

var effectSpecifiers: [SwiftEffectSpecifier]? = nil
Expand Down
79 changes: 78 additions & 1 deletion Tests/JExtractSwiftTests/JNI/JNISubscriptsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ struct JNISubscriptsTests {
}
"""

private let subscriptWithLabelledParamsSource = """
public struct MyStruct {
private var testVariable: [Int32] = []

public subscript(index idx: Int32) -> Int32 {
get { return testVariable[Int(idx)] }
set { testVariable[Int(idx)] = newValue }
}
}
"""

@Test("Test generation of JavaClass for subscript with no parameters")
func generatesJavaClassForNoParams() throws {
try assertOutput(
Expand Down Expand Up @@ -84,7 +95,73 @@ struct JNISubscriptsTests {
MyStruct.$setSubscript(index, newValue, this.$memoryAddress());
""",
"""
private static native void $setSubscript(int index, int newValue, long selfPointer);
private static native void $setSubscript(int index, int newValue, long selfPointer);
""",
]
)

// The subscipt uses no label, just directly: `base[idx]`
try assertOutput(
input: subscriptWithParamsSource,
.jni,
.swift,
detectChunkByInitialLines: 1,
expectedChunks: [
"""
@_cdecl("Java_com_example_swift_MyStruct__00024getSubscript__IJ")
...
return selfPointer$.pointee[Int32(fromJNI: index, in: environment)].getJNILocalRefValue(in: environment)
""",
"""
@_cdecl("Java_com_example_swift_MyStruct__00024setSubscript__IIJ")
...
selfPointer$.pointee[Int32(fromJNI: index, in: environment)] = Int32(fromJNI: newValue, in: environment)
""",
]
)
}

@Test("Test generation of JavaClass for subscript with labelled parameters")
func generatesJavaClassForLabelledParameters() throws {
try assertOutput(
input: subscriptWithLabelledParamsSource,
.jni,
.java,
expectedChunks: [
"""
public int getSubscript(int idx) {
return MyStruct.$getSubscript(idx, this.$memoryAddress());

""",
"""
private static native int $getSubscript(int idx, long selfPointer);
""",
"""
public void setSubscript(int idx, int newValue) {
MyStruct.$setSubscript(idx, newValue, this.$memoryAddress());
""",
"""
private static native void $setSubscript(int idx, int newValue, long selfPointer);
""",
]
)

// The subscipt uses an explicit label: `base[index: idx]`
try assertOutput(
input: subscriptWithLabelledParamsSource,
.jni,
.swift,
detectChunkByInitialLines: 1,
expectedChunks: [
"""
@_cdecl("Java_com_example_swift_MyStruct__00024getSubscript__IJ")
...
return selfPointer$.pointee[index: Int32(fromJNI: idx, in: environment)].getJNILocalRefValue(in: environment)
""",
"""
@_cdecl("Java_com_example_swift_MyStruct__00024setSubscript__IIJ")
...
selfPointer$.pointee[index: Int32(fromJNI: idx, in: environment)] = Int32(fromJNI: newValue, in: environment)
""",
]
)
Expand Down
Loading