From 6cf3f5d2bf477830e1fbf7a11c817c160557c761 Mon Sep 17 00:00:00 2001 From: AbdAlRahman Gad Date: Sun, 12 Jul 2026 09:59:52 +0300 Subject: [PATCH 01/10] SwiftExtract: detect operator functions --- .../ExtractedDecls+JavaNaming.swift | 4 +- ...Swift2JavaGenerator+FunctionLowering.swift | 2 +- ...ift2JavaGenerator+SwiftThunkPrinting.swift | 2 +- .../JavaIdentifierFactory.swift | 4 +- Sources/SwiftExtract/ExtractedDecls.swift | 2 + .../SwiftExtract/SwiftAnalysisVisitor.swift | 18 +- .../AnalysisResultTests.swift | 163 ++++++++++++++++++ 7 files changed, 188 insertions(+), 7 deletions(-) diff --git a/Sources/JExtractSwiftLib/ExtractedDecls+JavaNaming.swift b/Sources/JExtractSwiftLib/ExtractedDecls+JavaNaming.swift index 68965ce14..492ff5d26 100644 --- a/Sources/JExtractSwiftLib/ExtractedDecls+JavaNaming.swift +++ b/Sources/JExtractSwiftLib/ExtractedDecls+JavaNaming.swift @@ -57,7 +57,7 @@ extension ExtractedFunc { package var javaGetterName: String? { switch apiKind { case .getter, .subscriptGetter: break - case .setter, .subscriptSetter, .function, .initializer, .enumCase: return nil + case .setter, .subscriptSetter, .function, .initializer, .enumCase, .`operator`: return nil } let returnsBoolean = self.functionSignature.result.type.asNominalTypeDeclaration?.knownTypeKind == .bool @@ -81,7 +81,7 @@ extension ExtractedFunc { package var javaSetterName: String? { switch apiKind { case .setter, .subscriptSetter: break - case .getter, .subscriptGetter, .function, .initializer, .enumCase: return nil + case .getter, .subscriptGetter, .function, .initializer, .enumCase, .`operator`: return nil } let isBooleanSetter = self.functionSignature.parameters.first?.type.asNominalTypeDeclaration?.knownTypeKind == .bool diff --git a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift index f1c50b0ea..9cf26d1d3 100644 --- a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift +++ b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift @@ -1030,7 +1030,7 @@ extension LoweredFunctionSignature { // Build the result. let resultExpr: ExprSyntax switch apiKind { - case .function, .initializer: + case .function, .initializer, .`operator`: let arguments = paramExprs.enumerated() .map { (i, argument) -> String in let argExpr = original.parameters[i].convention == .inout ? "&\(argument)" : argument diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index 14e075b02..b8c84012a 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -627,7 +627,7 @@ extension JNISwift2JavaGenerator { // Build the result let result: String switch decl.apiKind { - case .function, .initializer: + case .function, .initializer, .`operator`: let downcallArguments = zip( decl.functionSignature.parameters, arguments, diff --git a/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift b/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift index 823246305..ebeccc5a0 100644 --- a/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift +++ b/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift @@ -37,7 +37,7 @@ package struct JavaIdentifierFactory { switch method.apiKind { case .getter, .subscriptGetter: method.javaGetterName! case .setter, .subscriptSetter: method.javaSetterName! - case .function, .initializer, .enumCase: method.name + case .function, .initializer, .enumCase, .`operator`: method.name } methodsByBaseName[baseName, default: []].append(method) } @@ -68,7 +68,7 @@ package struct JavaIdentifierFactory { switch decl.apiKind { case .getter, .subscriptGetter: decl.javaGetterName! case .setter, .subscriptSetter: decl.javaSetterName! - case .function, .initializer, .enumCase: decl.name + case .function, .initializer, .enumCase, .`operator`: decl.name } var methodName = baseName + paramsSuffix(decl, baseName: baseName) if Self.javaKeywords.contains(methodName) { diff --git a/Sources/SwiftExtract/ExtractedDecls.swift b/Sources/SwiftExtract/ExtractedDecls.swift index 580bddb47..d5490a2fe 100644 --- a/Sources/SwiftExtract/ExtractedDecls.swift +++ b/Sources/SwiftExtract/ExtractedDecls.swift @@ -25,6 +25,7 @@ public enum SwiftAPIKind: Equatable { case enumCase case subscriptGetter case subscriptSetter + case `operator` } /// Describes a Swift nominal type (e.g., a class, struct, enum) that has been @@ -343,6 +344,7 @@ public final class ExtractedFunc: ExtractedSwiftDecl, CustomStringConvertible { case .function, .initializer: "" case .subscriptGetter: "subscriptGetter:" case .subscriptSetter: "subscriptSetter:" + case .operator: "operator:" } let context = diff --git a/Sources/SwiftExtract/SwiftAnalysisVisitor.swift b/Sources/SwiftExtract/SwiftAnalysisVisitor.swift index 76de54d70..730b5e21b 100644 --- a/Sources/SwiftExtract/SwiftAnalysisVisitor.swift +++ b/Sources/SwiftExtract/SwiftAnalysisVisitor.swift @@ -38,6 +38,15 @@ final class SwiftAnalysisVisitor { } private var deferredConstrainedExtensions: [DeferredConstrainedExtension] = [] + private static func isOperatorFunction(_ node: FunctionDeclSyntax) -> Bool { + switch node.name.tokenKind { + case .binaryOperator, .prefixOperator, .postfixOperator: + return true + default: + return false + } + } + func visit(inputFile: SwiftInputFile) { let node = inputFile.syntax for codeItem in node.statements { @@ -201,11 +210,18 @@ final class SwiftAnalysisVisitor { return } + let apiKind: SwiftAPIKind = + if typeContext != nil && Self.isOperatorFunction(node) { + .operator + } else { + .function + } + let extracted = ExtractedFunc( module: analyzer.swiftModuleName, swiftDecl: node, name: node.name.text.unescapedSwiftName, - apiKind: .function, + apiKind: apiKind, functionSignature: signature, ) diff --git a/Tests/SwiftExtractTests/AnalysisResultTests.swift b/Tests/SwiftExtractTests/AnalysisResultTests.swift index 703e27ef6..081a0bf0b 100644 --- a/Tests/SwiftExtractTests/AnalysisResultTests.swift +++ b/Tests/SwiftExtractTests/AnalysisResultTests.swift @@ -174,6 +174,169 @@ struct AnalysisResultSuite { #expect(kinds == [.getter, .setter]) } + // ==== ----------------------------------------------------------------------- + // MARK: Operators + + @Test func operatortest() throws { + let result = try analyze( + sources: [ + ( + "/fake/Source.swift", + """ + public struct Score { + public var value: Int + + public static func + (left: Score, right: Score) -> Score { + Score(value: left.value + right.value) + } + + public static func - (left: Score, right: Score) -> Score { + Score(value: left.value - right.value) + } + + public static func * (left: Score, right: Score) -> Score { + Score(value: left.value * right.value) + } + + public static func / (left: Score, right: Score) -> Score { + Score(value: left.value / right.value) + } + + public static func % (left: Score, right: Score) -> Score { + Score(value: left.value % right.value) + } + + public static func << (left: Score, right: Int) -> Score { + Score(value: left.value << right) + } + + public static func >> (left: Score, right: Int) -> Score { + Score(value: left.value >> right) + } + + public static func | (left: Score, right: Score) -> Score { + Score(value: left.value | right.value) + } + + public static prefix func ~ (score: Score) -> Score { + Score(value: ~score.value) + } + + public static prefix func - (score: Score) -> Score { + Score(value: -score.value) + } + + public static func == (left: Score, right: Score) -> Bool { + left.value == right.value + } + + public static func != (left: Score, right: Score) -> Bool { + left.value != right.value + } + + public static func < (left: Score, right: Score) -> Bool { + left.value < right.value + } + + public static func <= (left: Score, right: Score) -> Bool { + left.value <= right.value + } + + public static func > (left: Score, right: Score) -> Bool { + left.value > right.value + } + + public static func >= (left: Score, right: Score) -> Bool { + left.value >= right.value + } + + public static func & (left: Score, right: Score) -> Score { + Score(value: left.value & right.value) + } + + public static func ^ (left: Score, right: Score) -> Score { + Score(value: left.value ^ right.value) + } + + public static func ?? (left: Score?, right: Score) -> Score { + left ?? right + } + + public static prefix func ! (score: Score) -> Bool { + score.value == 0 + } + } + """ + ) + ], + moduleName: "Aquarium", + ) + + let score = try #require(result.extractedTypes["Score"]) + let plusOperator = try #require(score.methods.first { $0.name == "+" }) + let minusOperator = try #require(score.methods.first { $0.name == "-" }) + let timesOperator = try #require(score.methods.first { $0.name == "*" }) + let dividedByOperator = try #require(score.methods.first { $0.name == "/" }) + let remainderOperator = try #require(score.methods.first { $0.name == "%" }) + let shiftedLeftOperator = try #require(score.methods.first { $0.name == "<<" }) + let shiftedRightOperator = try #require(score.methods.first { $0.name == ">>" }) + let bitwiseOrOperator = try #require(score.methods.first { $0.name == "|" }) + let bitwiseNotOperator = try #require(score.methods.first { $0.name == "~" }) + let negatedOperator = try #require(score.methods.first { $0.name == "-"}) + let isEqualOperator = try #require(score.methods.first { $0.name == "==" }) + let isNotEqualOperator = try #require(score.methods.first { $0.name == "!=" }) + let lessThanOperator = try #require(score.methods.first { $0.name == "<" }) + let lessThanOrEqualOperator = try #require(score.methods.first { $0.name == "<=" }) + let greaterThanOperator = try #require(score.methods.first { $0.name == ">" }) + let greaterThanOrEqualOperator = try #require(score.methods.first { $0.name == ">=" }) + let bitwiseAndOperator = try #require(score.methods.first { $0.name == "&" }) + let bitwiseXorOperator = try #require(score.methods.first { $0.name == "^" }) + let coalescingNilOperator = try #require(score.methods.first { $0.name == "??" }) + let logicalNotOperator = try #require(score.methods.first { $0.name == "!" }) + + + #expect(plusOperator.name == "+") + #expect(plusOperator.apiKind == .operator) + #expect(minusOperator.name == "-") + #expect(minusOperator.apiKind == .operator) + #expect(timesOperator.name == "*") + #expect(timesOperator.apiKind == .operator) + #expect(dividedByOperator.name == "/") + #expect(dividedByOperator.apiKind == .operator) + #expect(remainderOperator.name == "%") + #expect(remainderOperator.apiKind == .operator) + #expect(shiftedLeftOperator.name == "<<") + #expect(shiftedLeftOperator.apiKind == .operator) + #expect(shiftedRightOperator.name == ">>") + #expect(shiftedRightOperator.apiKind == .operator) + #expect(bitwiseOrOperator.name == "|") + #expect(bitwiseOrOperator.apiKind == .operator) + #expect(bitwiseNotOperator.name == "~") + #expect(bitwiseNotOperator.apiKind == .operator) + #expect(negatedOperator.name == "-") + #expect(negatedOperator.apiKind == .operator) + #expect(isEqualOperator.name == "==") + #expect(isEqualOperator.apiKind == .operator) + #expect(isNotEqualOperator.name == "!=") + #expect(isNotEqualOperator.apiKind == .operator) + #expect(lessThanOperator.name == "<") + #expect(lessThanOperator.apiKind == .operator) + #expect(lessThanOrEqualOperator.name == "<=") + #expect(lessThanOrEqualOperator.apiKind == .operator) + #expect(greaterThanOperator.name == ">") + #expect(greaterThanOperator.apiKind == .operator) + #expect(greaterThanOrEqualOperator.name == ">=") + #expect(greaterThanOrEqualOperator.apiKind == .operator) + #expect(bitwiseAndOperator.name == "&") + #expect(bitwiseAndOperator.apiKind == .operator) + #expect(bitwiseXorOperator.name == "^") + #expect(bitwiseXorOperator.apiKind == .operator) + #expect(coalescingNilOperator.name == "??") + #expect(coalescingNilOperator.apiKind == .operator) + #expect(logicalNotOperator.name == "!") + #expect(logicalNotOperator.apiKind == .operator) + } + // ==== ----------------------------------------------------------------------- // MARK: Effect specifiers (throws / async) From 27481fd607945147ca234ad55b0657a29ec69237 Mon Sep 17 00:00:00 2001 From: AbdAlRahman Gad Date: Mon, 13 Jul 2026 00:29:46 +0300 Subject: [PATCH 02/10] Implement operator translation to valid Java method names. --- .../JExtractSwiftLib/JavaExtractDecider.swift | 2 +- .../JavaIdentifierFactory.swift | 39 ++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/Sources/JExtractSwiftLib/JavaExtractDecider.swift b/Sources/JExtractSwiftLib/JavaExtractDecider.swift index 34e2e164a..a64ef4466 100644 --- a/Sources/JExtractSwiftLib/JavaExtractDecider.swift +++ b/Sources/JExtractSwiftLib/JavaExtractDecider.swift @@ -74,7 +74,7 @@ public struct JavaExtractDecider: ExtractDecider { // Swift operators have no Java mapping if let fn = decl.as(FunctionDeclSyntax.self) { switch fn.name.tokenKind { - case .binaryOperator, .prefixOperator, .postfixOperator: + case .prefixOperator, .postfixOperator: log.trace("Skip '\(decl.qualifiedNameForDebug)': operators are not supported on Java") return false default: diff --git a/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift b/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift index ebeccc5a0..f65816841 100644 --- a/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift +++ b/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift @@ -68,7 +68,8 @@ package struct JavaIdentifierFactory { switch decl.apiKind { case .getter, .subscriptGetter: decl.javaGetterName! case .setter, .subscriptSetter: decl.javaSetterName! - case .function, .initializer, .enumCase, .`operator`: decl.name + case .function, .initializer, .enumCase: decl.name + case .operator: Self.javaOperatorName(decl.name) } var methodName = baseName + paramsSuffix(decl, baseName: baseName) if Self.javaKeywords.contains(methodName) { @@ -94,6 +95,42 @@ package struct JavaIdentifierFactory { } } + private static func javaOperatorName(_ swiftName: String) -> String { + if let knownName = knownOperatorNames[swiftName] { + return knownName + } + + return swiftName.enumerated() + .map { index, character in + let name = knownOperatorNames[String(character)] ?? "operator" + return index == 0 ? name : name.firstCharacterUppercased + } + .joined() + } + + private static let knownOperatorNames: [String: String] = [ + "+": "plus", + "-": "minus", + "*": "times", + "/": "dividedBy", + "%": "remainder", + "<<": "shiftedLeft", + ">>": "shiftedRight", + "|": "bitwiseOr", + "~": "bitwiseNot", + "==": "isEqual", + "!=": "isNotEqual", + "<": "lessThan", + "<=": "lessThanOrEqual", + ">": "greaterThan", + ">=": "greaterThanOrEqual", + "&": "bitwiseAnd", + "^": "bitwiseXor", + "??": "coalescingNil", + "!": "logicalNot", + "=": "equal", + ] + static let javaKeywords: Set = [ /// https://docs.oracle.com/javase/specs/jls/se25/html/jls-3.html#jls-3.9 "abstract", "continue", "for", "new", "switch", From ccd2e0b61cd5f10d21679b0b367e85d0f7c4b6f2 Mon Sep 17 00:00:00 2001 From: AbdAlRahman Gad Date: Mon, 13 Jul 2026 00:30:23 +0300 Subject: [PATCH 03/10] draft: function lowering --- .../Sources/MySwiftLibrary/Operators.swift | 37 +++++++++++++++++++ ...ift2JavaGenerator+SwiftThunkPrinting.swift | 12 +++++- 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Operators.swift diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Operators.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Operators.swift new file mode 100644 index 000000000..3db77615b --- /dev/null +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Operators.swift @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +public struct OperatorScore { + public var value: Int64 + + public init(value: Int64) { + self.value = value + } + + public static func + (left: OperatorScore, right: OperatorScore) -> OperatorScore { + OperatorScore(value: left.value + right.value) + } + + public static func - (left: OperatorScore, right: OperatorScore) -> OperatorScore { + OperatorScore(value: left.value - right.value) + } + + public static func * (left: OperatorScore, right: OperatorScore) -> OperatorScore { + OperatorScore(value: left.value * right.value) + } + + public static func / (left: OperatorScore, right: OperatorScore) -> OperatorScore { + OperatorScore(value: left.value / right.value) + } +} diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index b8c84012a..d5e3b058a 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -627,7 +627,7 @@ extension JNISwift2JavaGenerator { // Build the result let result: String switch decl.apiKind { - case .function, .initializer, .`operator`: + case .function, .initializer: let downcallArguments = zip( decl.functionSignature.parameters, arguments, @@ -637,6 +637,16 @@ extension JNISwift2JavaGenerator { } .joined(separator: ", ") result = "\(tryClause)\(callee).\(decl.name)(\(downcallArguments))" + case .`operator`: + let downcallArguments: String = zip( + decl.functionSignature.parameters, + arguments, + ).map { originalParam, argument in + let label = originalParam.argumentLabel.map { "\($0): " } ?? "" + return "\(label)\(argument)" + } + .joined(separator: ", ") + result = "\(tryClause)\(callee).\(translatedDecl.name)(\(downcallArguments))" case .enumCase: let downcallArguments = zip( From 8accc6b488deebacc5a16b4627cf5dbffe077322 Mon Sep 17 00:00:00 2001 From: AbdAlRahman Gad Date: Wed, 15 Jul 2026 12:42:14 +0300 Subject: [PATCH 04/10] Refactor operator function detection to return SwiftOperatorKind enum values --- Sources/SwiftExtract/ExtractedDecls.swift | 4 +++- Sources/SwiftExtract/SwiftAnalysisVisitor.swift | 16 ++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Sources/SwiftExtract/ExtractedDecls.swift b/Sources/SwiftExtract/ExtractedDecls.swift index d5490a2fe..f74e96005 100644 --- a/Sources/SwiftExtract/ExtractedDecls.swift +++ b/Sources/SwiftExtract/ExtractedDecls.swift @@ -17,6 +17,8 @@ import SwiftSyntax /// Any extracted Swift declaration public protocol ExtractedSwiftDecl: AnyObject {} +public enum SwiftOperatorKind { case prefix; case binary; case postfix } + public enum SwiftAPIKind: Equatable { case function case initializer @@ -25,7 +27,7 @@ public enum SwiftAPIKind: Equatable { case enumCase case subscriptGetter case subscriptSetter - case `operator` + case `operator`(SwiftOperatorKind) } /// Describes a Swift nominal type (e.g., a class, struct, enum) that has been diff --git a/Sources/SwiftExtract/SwiftAnalysisVisitor.swift b/Sources/SwiftExtract/SwiftAnalysisVisitor.swift index 730b5e21b..cd5457d99 100644 --- a/Sources/SwiftExtract/SwiftAnalysisVisitor.swift +++ b/Sources/SwiftExtract/SwiftAnalysisVisitor.swift @@ -38,12 +38,16 @@ final class SwiftAnalysisVisitor { } private var deferredConstrainedExtensions: [DeferredConstrainedExtension] = [] - private static func isOperatorFunction(_ node: FunctionDeclSyntax) -> Bool { + private static func operatorKind(_ node: FunctionDeclSyntax) -> SwiftOperatorKind? { switch node.name.tokenKind { - case .binaryOperator, .prefixOperator, .postfixOperator: - return true + case .binaryOperator: + return .binary + case .prefixOperator: + return .prefix + case .postfixOperator: + return .postfix default: - return false + return nil } } @@ -211,8 +215,8 @@ final class SwiftAnalysisVisitor { } let apiKind: SwiftAPIKind = - if typeContext != nil && Self.isOperatorFunction(node) { - .operator + if typeContext != nil && Self.operatorKind(node) != nil { + .operator(Self.operatorKind(node)!) } else { .function } From 9af28df2cc33b9981457ac90a8be9537f8dc4293 Mon Sep 17 00:00:00 2001 From: AbdAlRahman Gad Date: Wed, 15 Jul 2026 15:07:13 +0300 Subject: [PATCH 05/10] better enum handling --- Sources/SwiftExtract/ExtractedDecls.swift | 6 +++--- Sources/SwiftExtract/SwiftAnalysisVisitor.swift | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/SwiftExtract/ExtractedDecls.swift b/Sources/SwiftExtract/ExtractedDecls.swift index f74e96005..5da573cc7 100644 --- a/Sources/SwiftExtract/ExtractedDecls.swift +++ b/Sources/SwiftExtract/ExtractedDecls.swift @@ -17,8 +17,6 @@ import SwiftSyntax /// Any extracted Swift declaration public protocol ExtractedSwiftDecl: AnyObject {} -public enum SwiftOperatorKind { case prefix; case binary; case postfix } - public enum SwiftAPIKind: Equatable { case function case initializer @@ -27,7 +25,9 @@ public enum SwiftAPIKind: Equatable { case enumCase case subscriptGetter case subscriptSetter - case `operator`(SwiftOperatorKind) + case prefixOperator + case binaryOperator + case postfixOperator } /// Describes a Swift nominal type (e.g., a class, struct, enum) that has been diff --git a/Sources/SwiftExtract/SwiftAnalysisVisitor.swift b/Sources/SwiftExtract/SwiftAnalysisVisitor.swift index cd5457d99..3b079e510 100644 --- a/Sources/SwiftExtract/SwiftAnalysisVisitor.swift +++ b/Sources/SwiftExtract/SwiftAnalysisVisitor.swift @@ -38,14 +38,14 @@ final class SwiftAnalysisVisitor { } private var deferredConstrainedExtensions: [DeferredConstrainedExtension] = [] - private static func operatorKind(_ node: FunctionDeclSyntax) -> SwiftOperatorKind? { + private static func operatorKind(_ node: FunctionDeclSyntax) -> SwiftAPIKind? { switch node.name.tokenKind { case .binaryOperator: - return .binary + return .binaryOperator case .prefixOperator: - return .prefix + return .prefixOperator case .postfixOperator: - return .postfix + return .postfixOperator default: return nil } @@ -216,7 +216,7 @@ final class SwiftAnalysisVisitor { let apiKind: SwiftAPIKind = if typeContext != nil && Self.operatorKind(node) != nil { - .operator(Self.operatorKind(node)!) + Self.operatorKind(node)! } else { .function } From 8822a04d449a837d779d0229e2366920bd26f12d Mon Sep 17 00:00:00 2001 From: AbdAlRahman Gad Date: Wed, 15 Jul 2026 15:12:50 +0300 Subject: [PATCH 06/10] handle binary operators swift Thunk printing --- ...JNISwift2JavaGenerator+SwiftThunkPrinting.swift | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index d5e3b058a..9e2a15740 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -637,17 +637,11 @@ extension JNISwift2JavaGenerator { } .joined(separator: ", ") result = "\(tryClause)\(callee).\(decl.name)(\(downcallArguments))" - case .`operator`: - let downcallArguments: String = zip( - decl.functionSignature.parameters, - arguments, - ).map { originalParam, argument in - let label = originalParam.argumentLabel.map { "\($0): " } ?? "" - return "\(label)\(argument)" + case .binaryOperator: + guard arguments.count == 2 else { + fatalError("Binary operator must have exactly 2 arguments: \(decl)") } - .joined(separator: ", ") - result = "\(tryClause)\(callee).\(translatedDecl.name)(\(downcallArguments))" - + result = "(\(tryClause) ((\(arguments.first!)) \(decl.name) (\(arguments.last!))))" case .enumCase: let downcallArguments = zip( decl.functionSignature.parameters, From 45842c0f329a20b8bafbc7ee8096860e95425fc3 Mon Sep 17 00:00:00 2001 From: AbdAlRahman Gad Date: Thu, 16 Jul 2026 07:14:24 +0300 Subject: [PATCH 07/10] refactor --- .../ExtractedDecls+JavaNaming.swift | 4 +- .../JavaIdentifierFactory.swift | 34 ++++++++---- Sources/SwiftExtract/ExtractedDecls.swift | 4 +- .../AnalysisResultTests.swift | 55 ++++++------------- 4 files changed, 45 insertions(+), 52 deletions(-) diff --git a/Sources/JExtractSwiftLib/ExtractedDecls+JavaNaming.swift b/Sources/JExtractSwiftLib/ExtractedDecls+JavaNaming.swift index 492ff5d26..ad1281d37 100644 --- a/Sources/JExtractSwiftLib/ExtractedDecls+JavaNaming.swift +++ b/Sources/JExtractSwiftLib/ExtractedDecls+JavaNaming.swift @@ -57,7 +57,7 @@ extension ExtractedFunc { package var javaGetterName: String? { switch apiKind { case .getter, .subscriptGetter: break - case .setter, .subscriptSetter, .function, .initializer, .enumCase, .`operator`: return nil + case .setter, .subscriptSetter, .function, .initializer, .enumCase, .binaryOperator, .prefixOperator, .postfixOperator: return nil } let returnsBoolean = self.functionSignature.result.type.asNominalTypeDeclaration?.knownTypeKind == .bool @@ -81,7 +81,7 @@ extension ExtractedFunc { package var javaSetterName: String? { switch apiKind { case .setter, .subscriptSetter: break - case .getter, .subscriptGetter, .function, .initializer, .enumCase, .`operator`: return nil + case .getter, .subscriptGetter, .function, .initializer, .enumCase, .binaryOperator, .prefixOperator, .postfixOperator: return nil } let isBooleanSetter = self.functionSignature.parameters.first?.type.asNominalTypeDeclaration?.knownTypeKind == .bool diff --git a/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift b/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift index f65816841..2a4049485 100644 --- a/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift +++ b/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift @@ -37,7 +37,7 @@ package struct JavaIdentifierFactory { switch method.apiKind { case .getter, .subscriptGetter: method.javaGetterName! case .setter, .subscriptSetter: method.javaSetterName! - case .function, .initializer, .enumCase, .`operator`: method.name + case .function, .initializer, .enumCase, .binaryOperator, .prefixOperator, .postfixOperator: method.name } methodsByBaseName[baseName, default: []].append(method) } @@ -69,7 +69,8 @@ package struct JavaIdentifierFactory { case .getter, .subscriptGetter: decl.javaGetterName! case .setter, .subscriptSetter: decl.javaSetterName! case .function, .initializer, .enumCase: decl.name - case .operator: Self.javaOperatorName(decl.name) + case .binaryOperator, .prefixOperator, .postfixOperator: + Self.javaOperatorName(decl.name, index: decl.name.startIndex) } var methodName = baseName + paramsSuffix(decl, baseName: baseName) if Self.javaKeywords.contains(methodName) { @@ -95,17 +96,28 @@ package struct JavaIdentifierFactory { } } - private static func javaOperatorName(_ swiftName: String) -> String { - if let knownName = knownOperatorNames[swiftName] { - return knownName - } + private static func javaOperatorName(_ swiftName: String, index: String.Index) -> String { + guard index < swiftName.endIndex else { return "" } + + let isFirstToken = index == swiftName.startIndex + let oneCharacterEndIndex = swiftName.index(after: index) + let oneCharacterToken = String(swiftName[index.. Score { - Score(value: ~score.value) - } - - public static prefix func - (score: Score) -> Score { - Score(value: -score.value) - } - public static func == (left: Score, right: Score) -> Bool { left.value == right.value } @@ -261,10 +253,6 @@ struct AnalysisResultSuite { public static func ?? (left: Score?, right: Score) -> Score { left ?? right } - - public static prefix func ! (score: Score) -> Bool { - score.value == 0 - } } """ ) @@ -281,8 +269,6 @@ struct AnalysisResultSuite { let shiftedLeftOperator = try #require(score.methods.first { $0.name == "<<" }) let shiftedRightOperator = try #require(score.methods.first { $0.name == ">>" }) let bitwiseOrOperator = try #require(score.methods.first { $0.name == "|" }) - let bitwiseNotOperator = try #require(score.methods.first { $0.name == "~" }) - let negatedOperator = try #require(score.methods.first { $0.name == "-"}) let isEqualOperator = try #require(score.methods.first { $0.name == "==" }) let isNotEqualOperator = try #require(score.methods.first { $0.name == "!=" }) let lessThanOperator = try #require(score.methods.first { $0.name == "<" }) @@ -292,49 +278,42 @@ struct AnalysisResultSuite { let bitwiseAndOperator = try #require(score.methods.first { $0.name == "&" }) let bitwiseXorOperator = try #require(score.methods.first { $0.name == "^" }) let coalescingNilOperator = try #require(score.methods.first { $0.name == "??" }) - let logicalNotOperator = try #require(score.methods.first { $0.name == "!" }) #expect(plusOperator.name == "+") - #expect(plusOperator.apiKind == .operator) + #expect(plusOperator.apiKind == .binaryOperator) #expect(minusOperator.name == "-") - #expect(minusOperator.apiKind == .operator) + #expect(minusOperator.apiKind == .binaryOperator) #expect(timesOperator.name == "*") - #expect(timesOperator.apiKind == .operator) + #expect(timesOperator.apiKind == .binaryOperator) #expect(dividedByOperator.name == "/") - #expect(dividedByOperator.apiKind == .operator) + #expect(dividedByOperator.apiKind == .binaryOperator) #expect(remainderOperator.name == "%") - #expect(remainderOperator.apiKind == .operator) + #expect(remainderOperator.apiKind == .binaryOperator) #expect(shiftedLeftOperator.name == "<<") - #expect(shiftedLeftOperator.apiKind == .operator) + #expect(shiftedLeftOperator.apiKind == .binaryOperator) #expect(shiftedRightOperator.name == ">>") - #expect(shiftedRightOperator.apiKind == .operator) + #expect(shiftedRightOperator.apiKind == .binaryOperator) #expect(bitwiseOrOperator.name == "|") - #expect(bitwiseOrOperator.apiKind == .operator) - #expect(bitwiseNotOperator.name == "~") - #expect(bitwiseNotOperator.apiKind == .operator) - #expect(negatedOperator.name == "-") - #expect(negatedOperator.apiKind == .operator) + #expect(bitwiseOrOperator.apiKind == .binaryOperator) #expect(isEqualOperator.name == "==") - #expect(isEqualOperator.apiKind == .operator) + #expect(isEqualOperator.apiKind == .binaryOperator) #expect(isNotEqualOperator.name == "!=") - #expect(isNotEqualOperator.apiKind == .operator) + #expect(isNotEqualOperator.apiKind == .binaryOperator) #expect(lessThanOperator.name == "<") - #expect(lessThanOperator.apiKind == .operator) + #expect(lessThanOperator.apiKind == .binaryOperator) #expect(lessThanOrEqualOperator.name == "<=") - #expect(lessThanOrEqualOperator.apiKind == .operator) + #expect(lessThanOrEqualOperator.apiKind == .binaryOperator) #expect(greaterThanOperator.name == ">") - #expect(greaterThanOperator.apiKind == .operator) + #expect(greaterThanOperator.apiKind == .binaryOperator) #expect(greaterThanOrEqualOperator.name == ">=") - #expect(greaterThanOrEqualOperator.apiKind == .operator) + #expect(greaterThanOrEqualOperator.apiKind == .binaryOperator) #expect(bitwiseAndOperator.name == "&") - #expect(bitwiseAndOperator.apiKind == .operator) + #expect(bitwiseAndOperator.apiKind == .binaryOperator) #expect(bitwiseXorOperator.name == "^") - #expect(bitwiseXorOperator.apiKind == .operator) + #expect(bitwiseXorOperator.apiKind == .binaryOperator) #expect(coalescingNilOperator.name == "??") - #expect(coalescingNilOperator.apiKind == .operator) - #expect(logicalNotOperator.name == "!") - #expect(logicalNotOperator.apiKind == .operator) + #expect(coalescingNilOperator.apiKind == .binaryOperator) } // ==== ----------------------------------------------------------------------- From 0c7650a0ae22ce0c186a6447481c98a3e6728b22 Mon Sep 17 00:00:00 2001 From: AbdAlRahman Gad Date: Thu, 16 Jul 2026 07:23:41 +0300 Subject: [PATCH 08/10] Thunking and function lowering --- .../FFMSwift2JavaGenerator+FunctionLowering.swift | 14 +++++++++++++- ...JNISwift2JavaGenerator+SwiftThunkPrinting.swift | 10 ++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift index 9cf26d1d3..2963e47b6 100644 --- a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift +++ b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift @@ -1030,7 +1030,7 @@ extension LoweredFunctionSignature { // Build the result. let resultExpr: ExprSyntax switch apiKind { - case .function, .initializer, .`operator`: + case .function, .initializer: let arguments = paramExprs.enumerated() .map { (i, argument) -> String in let argExpr = original.parameters[i].convention == .inout ? "&\(argument)" : argument @@ -1039,6 +1039,18 @@ extension LoweredFunctionSignature { .joined(separator: ", ") resultExpr = "\(callee)(\(raw: arguments))" + case .binaryOperator: + assert(paramExprs.count == 2) + resultExpr = "\(paramExprs[0]) \(callee) \(paramExprs[1])" + + case .prefixOperator: + assert(paramExprs.count == 1) + resultExpr = "\(callee)\(paramExprs[0])" + + case .postfixOperator: + assert(paramExprs.count == 1) + resultExpr = "\(paramExprs[0])\(callee)" + case .getter: assert(paramExprs.isEmpty) resultExpr = callee diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index 9e2a15740..5b6fd2990 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -642,6 +642,16 @@ extension JNISwift2JavaGenerator { fatalError("Binary operator must have exactly 2 arguments: \(decl)") } result = "(\(tryClause) ((\(arguments.first!)) \(decl.name) (\(arguments.last!))))" + case .prefixOperator: + guard arguments.count == 1 else { + fatalError("Prefix operator must have exactly 1 argument: \(decl)") + } + result = "(\(tryClause) (\(decl.name) (\(arguments.first!))))" + case .postfixOperator: + guard arguments.count == 1 else { + fatalError("Postfix operator must have exactly 1 argument: \(decl)") + } + result = "(\(tryClause) ((\(arguments.first!)) \(decl.name)))" case .enumCase: let downcallArguments = zip( decl.functionSignature.parameters, From 90a22c13454176163198258085385a79c9b04ea9 Mon Sep 17 00:00:00 2001 From: AbdAlRahman Gad Date: Thu, 16 Jul 2026 10:02:14 +0300 Subject: [PATCH 09/10] implement java naming conversion --- .../JavaIdentifierFactory.swift | 44 ++--- .../JNI/JNIOperatorsTests.swift | 152 ++++++++++++++++++ 2 files changed, 176 insertions(+), 20 deletions(-) create mode 100644 Tests/JExtractSwiftTests/JNI/JNIOperatorsTests.swift diff --git a/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift b/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift index 2a4049485..38c7f4ad4 100644 --- a/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift +++ b/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift @@ -70,7 +70,7 @@ package struct JavaIdentifierFactory { case .setter, .subscriptSetter: decl.javaSetterName! case .function, .initializer, .enumCase: decl.name case .binaryOperator, .prefixOperator, .postfixOperator: - Self.javaOperatorName(decl.name, index: decl.name.startIndex) + Self.javaOperatorName(decl.name) } var methodName = baseName + paramsSuffix(decl, baseName: baseName) if Self.javaKeywords.contains(methodName) { @@ -96,28 +96,32 @@ package struct JavaIdentifierFactory { } } - private static func javaOperatorName(_ swiftName: String, index: String.Index) -> String { - guard index < swiftName.endIndex else { return "" } - - let isFirstToken = index == swiftName.startIndex - let oneCharacterEndIndex = swiftName.index(after: index) - let oneCharacterToken = String(swiftName[index.. String { + var index = swiftName.startIndex + var javaName = "" + + while index < swiftName.endIndex { + let isFirstToken = index == swiftName.startIndex + let oneCharacterEndIndex = swiftName.index(after: index) + let oneCharacterToken = String(swiftName[index..=>=++ + infix operator <<>>==+ + + public struct Number { + public static func + (left: Number, right: Number) -> Number { + Number() + } + + public static func ==+ (left: Number, right: Number) -> Number { + Number() + } + + public static func >=>=++ (left: Number, right: Number) -> Number { + Number() + } + + public static func <<>>==+ (left: Number, right: Number) -> Number { + Number() + } + } + """ + + @Test + func plus_javaBindings() throws { + try assertOutput( + input: source, + .jni, + .java, + expectedChunks: [ + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public static func + (left: Number, right: Number) -> Number + * } + */ + public static Number plus(Number left, Number right, SwiftArena swiftArena) { + return Number.wrapMemoryAddressUnsafe(Number.$plus(left.$memoryAddress(), right.$memoryAddress()), swiftArena); + } + """, + """ + private static native long $plus(long left, long right); + """, + ] + ) + } + + @Test + func isEqualPlus_javaBindings() throws { + try assertOutput( + input: source, + .jni, + .java, + detectChunkByInitialLines: 2, + expectedChunks: [ + """ + public static Number isEqualPlus(Number left, Number right, SwiftArena swiftArena) { + return Number.wrapMemoryAddressUnsafe(Number.$isEqualPlus(left.$memoryAddress(), right.$memoryAddress()), swiftArena); + } + """, + """ + private static native long $isEqualPlus(long left, long right); + """, + ] + ) + } + + @Test + func repeatedGreaterThanOrEqual_javaBindings() throws { + try assertOutput( + input: source, + .jni, + .java, + detectChunkByInitialLines: 2, + expectedChunks: [ + """ + public static Number greaterThanOrEqualGreaterThanOrEqualPlusPlus(Number left, Number right, SwiftArena swiftArena) { + return Number.wrapMemoryAddressUnsafe(Number.$greaterThanOrEqualGreaterThanOrEqualPlusPlus(left.$memoryAddress(), right.$memoryAddress()), swiftArena); + } + """, + """ + private static native long $greaterThanOrEqualGreaterThanOrEqualPlusPlus(long left, long right); + """, + ] + ) + } + + @Test + func mixedTwoCharacterTokens_javaBindings() throws { + try assertOutput( + input: source, + .jni, + .java, + detectChunkByInitialLines: 2, + expectedChunks: [ + """ + public static Number shiftedLeftShiftedRightIsEqualPlus(Number left, Number right, SwiftArena swiftArena) { + return Number.wrapMemoryAddressUnsafe(Number.$shiftedLeftShiftedRightIsEqualPlus(left.$memoryAddress(), right.$memoryAddress()), swiftArena); + } + """, + """ + private static native long $shiftedLeftShiftedRightIsEqualPlus(long left, long right); + """, + ] + ) + } + + @Test + func plus_swiftThunks() throws { + try assertOutput( + input: source, + .jni, + .swift, + detectChunkByInitialLines: 1, + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_Number__00024plus__JJ") + public func Java_com_example_swift_Number__00024plus__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, left: jlong, right: jlong) -> jlong { + ... + let result$ = UnsafeMutablePointer.allocate(capacity: 1) + result$.initialize(to: ( ((left$.pointee) + (right$.pointee))) + let resultBits$ = Int64(Int(bitPattern: result$)) + return resultBits$.getJNILocalRefValue(in: environment) + } + """ + ] + ) + } +} From 7ba06314a69bad35bc70e2723f77e5433e265427 Mon Sep 17 00:00:00 2001 From: AbdAlRahman Gad Date: Thu, 16 Jul 2026 10:45:29 +0300 Subject: [PATCH 10/10] nit --- ...MSwift2JavaGenerator+FunctionLowering.swift | 6 +++--- ...wift2JavaGenerator+SwiftThunkPrinting.swift | 18 ++++++------------ .../JavaIdentifierFactory.swift | 3 +++ .../JNI/JNIOperatorsTests.swift | 2 +- .../AnalysisResultTests.swift | 2 +- 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift index 2963e47b6..78729924f 100644 --- a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift +++ b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift @@ -1041,15 +1041,15 @@ extension LoweredFunctionSignature { case .binaryOperator: assert(paramExprs.count == 2) - resultExpr = "\(paramExprs[0]) \(callee) \(paramExprs[1])" + resultExpr = "(\(paramExprs[0]) \(callee) \(paramExprs[1]))" case .prefixOperator: assert(paramExprs.count == 1) - resultExpr = "\(callee)\(paramExprs[0])" + resultExpr = "(\(callee)\(paramExprs[0]))" case .postfixOperator: assert(paramExprs.count == 1) - resultExpr = "\(paramExprs[0])\(callee)" + resultExpr = "(\(paramExprs[0])\(callee))" case .getter: assert(paramExprs.isEmpty) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index 5b6fd2990..719daf1f1 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -638,20 +638,14 @@ extension JNISwift2JavaGenerator { .joined(separator: ", ") result = "\(tryClause)\(callee).\(decl.name)(\(downcallArguments))" case .binaryOperator: - guard arguments.count == 2 else { - fatalError("Binary operator must have exactly 2 arguments: \(decl)") - } - result = "(\(tryClause) ((\(arguments.first!)) \(decl.name) (\(arguments.last!))))" + precondition(arguments.count == 2, "Binary operator must have exactly 2 arguments: \(decl)") + result = "(\(tryClause)((\(arguments.first!)) \(decl.name) (\(arguments.last!))))" case .prefixOperator: - guard arguments.count == 1 else { - fatalError("Prefix operator must have exactly 1 argument: \(decl)") - } - result = "(\(tryClause) (\(decl.name) (\(arguments.first!))))" + precondition(arguments.count == 1, "Prefix operator must have exactly 1 argument: \(decl)") + result = "(\(tryClause)(\(decl.name) (\(arguments.first!))))" case .postfixOperator: - guard arguments.count == 1 else { - fatalError("Postfix operator must have exactly 1 argument: \(decl)") - } - result = "(\(tryClause) ((\(arguments.first!)) \(decl.name)))" + precondition(arguments.count == 1, "Postfix operator must have exactly 1 argument: \(decl)") + result = "(\(tryClause)((\(arguments.first!)) \(decl.name)))" case .enumCase: let downcallArguments = zip( decl.functionSignature.parameters, diff --git a/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift b/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift index 38c7f4ad4..40c83663d 100644 --- a/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift +++ b/Sources/JExtractSwiftLib/JavaIdentifierFactory.swift @@ -145,6 +145,9 @@ package struct JavaIdentifierFactory { "??": "coalescingNil", "!": "logicalNot", "=": "equal", + "&&": "and", + "||": "or", + ".": "dot", ] static let javaKeywords: Set = [ diff --git a/Tests/JExtractSwiftTests/JNI/JNIOperatorsTests.swift b/Tests/JExtractSwiftTests/JNI/JNIOperatorsTests.swift index 001f7cfb6..9863d109d 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIOperatorsTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIOperatorsTests.swift @@ -141,7 +141,7 @@ struct JNIOperatorsTests { public func Java_com_example_swift_Number__00024plus__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, left: jlong, right: jlong) -> jlong { ... let result$ = UnsafeMutablePointer.allocate(capacity: 1) - result$.initialize(to: ( ((left$.pointee) + (right$.pointee))) + result$.initialize(to: (((left$.pointee) + (right$.pointee))) let resultBits$ = Int64(Int(bitPattern: result$)) return resultBits$.getJNILocalRefValue(in: environment) } diff --git a/Tests/SwiftExtractTests/AnalysisResultTests.swift b/Tests/SwiftExtractTests/AnalysisResultTests.swift index 4f0a50754..26715ecad 100644 --- a/Tests/SwiftExtractTests/AnalysisResultTests.swift +++ b/Tests/SwiftExtractTests/AnalysisResultTests.swift @@ -177,7 +177,7 @@ struct AnalysisResultSuite { // ==== ----------------------------------------------------------------------- // MARK: Operators - @Test func operatortest() throws { + @Test func operators() throws { let result = try analyze( sources: [ (