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 @@ -18,7 +18,7 @@ import SwiftJavaJNICore
extension FFMSwift2JavaGenerator {
package func printFunctionDowncallMethods(
_ printer: inout CodePrinter,
_ decl: ImportedFunc
_ decl: ImportedFunc,
) {
guard let _ = translatedDecl(for: decl) else {
// Failed to translate. Skip.
Expand All @@ -38,7 +38,7 @@ extension FFMSwift2JavaGenerator {
/// Print FFM Java binding descriptors for the imported Swift API.
package func printJavaBindingDescriptorClass(
_ printer: inout CodePrinter,
_ decl: ImportedFunc
_ decl: ImportedFunc,
) {
let thunkName = thunkNameRegistry.functionThunkName(decl: decl)
let translated = self.translatedDecl(for: decl)!
Expand All @@ -58,7 +58,7 @@ extension FFMSwift2JavaGenerator {
package func printJavaBindingDescriptorClass(
_ printer: inout CodePrinter,
_ cFunc: CFunction,
additionalContent: ((inout CodePrinter) -> Void)? = nil
additionalContent: ((inout CodePrinter) -> Void)? = nil,
) {
printer.printBraceBlock(
"""
Expand Down Expand Up @@ -87,7 +87,7 @@ extension FFMSwift2JavaGenerator {
func printFunctionDescriptorDefinition(
_ printer: inout CodePrinter,
_ resultType: CType,
_ parameters: [CParameter]
_ parameters: [CParameter],
) {
printer.start("private static final FunctionDescriptor DESC = ")

Expand All @@ -113,7 +113,7 @@ extension FFMSwift2JavaGenerator {

func printJavaBindingDowncallMethod(
_ printer: inout CodePrinter,
_ cFunc: CFunction
_ cFunc: CFunction,
) {
let returnTy = cFunc.resultType.javaType
let maybeReturn = cFunc.resultType.isVoid ? "" : "return (\(returnTy)) "
Expand Down Expand Up @@ -157,7 +157,7 @@ extension FFMSwift2JavaGenerator {
/// * Unnamed-struct parameter as a record. (unimplemented)
func printParameterDescriptorClasses(
_ printer: inout CodePrinter,
_ cFunc: CFunction
_ cFunc: CFunction,
) {
for param in cFunc.parameters {
switch param.type {
Expand All @@ -172,7 +172,7 @@ extension FFMSwift2JavaGenerator {

func printUpcallParameterDescriptorClasses(
_ printer: inout CodePrinter,
_ outCallback: OutCallback
_ outCallback: OutCallback,
) {
let name = outCallback.name
printFunctionPointerParameterDescriptorClass(&printer, name, outCallback.cFunc.functionType, impl: outCallback)
Expand All @@ -199,7 +199,7 @@ extension FFMSwift2JavaGenerator {
_ printer: inout CodePrinter,
_ name: String,
_ cType: CType,
impl: OutCallback?
impl: OutCallback?,
) {
let cResultType: CType
let cParameterTypes: [CType]
Expand Down Expand Up @@ -267,7 +267,7 @@ extension FFMSwift2JavaGenerator {
/// * User-facing functional interfaces.
func printJavaBindingWrapperHelperClass(
_ printer: inout CodePrinter,
_ decl: ImportedFunc
_ decl: ImportedFunc,
) {
let translated = self.translatedDecl(for: decl)!
let bindingDescriptorName = self.thunkNameRegistry.functionThunkName(decl: decl)
Expand All @@ -290,7 +290,7 @@ extension FFMSwift2JavaGenerator {
func printJavaBindingWrapperFunctionTypeHelper(
_ printer: inout CodePrinter,
_ functionType: TranslatedFunctionType,
_ bindingDescriptorName: String
_ bindingDescriptorName: String,
) {
let cdeclDescriptor = "\(bindingDescriptorName).$\(functionType.name)"
if functionType.isCompatibleWithC {
Expand Down Expand Up @@ -356,7 +356,7 @@ extension FFMSwift2JavaGenerator {
/// with adding `SwiftArena.ofAuto()` at the end.
package func printJavaBindingWrapperMethod(
_ printer: inout CodePrinter,
_ decl: ImportedFunc
_ decl: ImportedFunc,
) {
let translated = self.translatedDecl(for: decl)!
let methodName = translated.name
Expand All @@ -382,14 +382,17 @@ extension FFMSwift2JavaGenerator {
paramDecls.append("AllocatingSwiftArena swiftArena")
}

let needsThrows = translatedSignature.parameters.contains { $0.needs32BitIntOverflowCheck != .none } || translatedSignature.result.needs32BitIntOverflowCheck != .none
let throwsClause = needsThrows ? " throws SwiftIntegerOverflowException" : ""

TranslatedDocumentation.printDocumentation(
importedFunc: decl,
translatedDecl: translated,
in: &printer
in: &printer,
)
printer.printBraceBlock(
"""
\(annotationsStr)\(modifiers) \(returnTy) \(methodName)(\(paramDecls.joined(separator: ", ")))
\(annotationsStr)\(modifiers) \(returnTy) \(methodName)(\(paramDecls.joined(separator: ", ")))\(throwsClause)
"""
) { printer in
if case .instance(_) = decl.functionSignature.selfParameter {
Expand All @@ -406,7 +409,7 @@ extension FFMSwift2JavaGenerator {
/// This assumes that all the parameters are passed-in with appropriate names.
package func printDowncall(
_ printer: inout CodePrinter,
_ decl: ImportedFunc
_ decl: ImportedFunc,
) {
//=== Part 1: prepare temporary arena if needed.
let translatedSignature = self.translatedDecl(for: decl)!.translatedSignature
Expand Down Expand Up @@ -473,6 +476,30 @@ extension FFMSwift2JavaGenerator {
)
}

let hasOverflowChecks = translatedSignature.parameters.contains { $0.needs32BitIntOverflowCheck != .none }
if hasOverflowChecks {
printer.printBraceBlock("if (SwiftValueLayout.has32bitSwiftInt)") { printer in
for (i, parameter) in translatedSignature.parameters.enumerated() {
switch parameter.needs32BitIntOverflowCheck {
case .none:
break
case .signedInt:
let original = decl.functionSignature.parameters[i]
let parameterName = original.parameterName ?? "_\(i)"
printer.printBraceBlock("if (\(parameterName) < Integer.MIN_VALUE || \(parameterName) > Integer.MAX_VALUE)") { printer in
printer.print("throw new SwiftIntegerOverflowException(\"Parameter '\(parameterName)' overflow: \" + \(parameterName));")
}
case .unsignedInt:
let original = decl.functionSignature.parameters[i]
let parameterName = original.parameterName ?? "_\(i)"
printer.printBraceBlock("if (\(parameterName) < 0 || \(parameterName) > 0xFFFFFFFFL)") { printer in
printer.print("throw new SwiftIntegerOverflowException(\"Parameter '\(parameterName)' overflow: \" + \(parameterName));")
}
}
}
}
}

//=== Part 3: Downcall.
let downCall = "\(thunkName).call(\(downCallArguments.joined(separator: ", ")))"

Expand All @@ -499,16 +526,17 @@ extension FFMSwift2JavaGenerator {
let result = translatedSignature.result.conversion.render(
&printer,
placeholder,
placeholderForDowncall: placeholderForDowncall
placeholderForDowncall: placeholderForDowncall,
)

if translatedSignature.result.javaResultType != .void {
switch translatedSignature.result.conversion {
case .initializeResultWithUpcall(_, let extractResult):
printer.print("\(result);") // the result in the callback situation is a series of setup steps
printer.print("return \(extractResult.render(&printer, placeholder));") // extract the actual result
let extracted = extractResult.render(&printer, placeholder)
printReturnWithOverflowCheck(&printer, value: extracted, overflowCheck: translatedSignature.result.needs32BitIntOverflowCheck)
default:
printer.print("return \(result);")
printReturnWithOverflowCheck(&printer, value: result, overflowCheck: translatedSignature.result.needs32BitIntOverflowCheck)
}
} else {
printer.print("\(result);")
Expand All @@ -521,6 +549,38 @@ extension FFMSwift2JavaGenerator {
}
}

/// Print a return statement with an optional 32-bit integer overflow check.
private func printReturnWithOverflowCheck(
_ printer: inout CodePrinter,
value: String,
overflowCheck: OverflowCheckType,
) {
switch overflowCheck {
case .none:
printer.print("return \(value);")
case .signedInt:
let resultVar = "_result$checked"
printer.print("long \(resultVar) = \(value);")

printer.printBraceBlock("if (SwiftValueLayout.has32bitSwiftInt)") { printer in
printer.printBraceBlock("if (\(resultVar) < Integer.MIN_VALUE || \(resultVar) > Integer.MAX_VALUE)") { printer in
printer.print("throw new SwiftIntegerOverflowException(\"Return value overflow: \" + \(resultVar));")
}
}
printer.print("return \(resultVar);")
case .unsignedInt:
let resultVar = "_result$checked"
printer.print("long \(resultVar) = \(value);")

printer.printBraceBlock("if (SwiftValueLayout.has32bitSwiftInt)") { printer in
printer.printBraceBlock("if (\(resultVar) < 0 || \(resultVar) > 0xFFFFFFFFL)") { printer in
printer.print("throw new SwiftIntegerOverflowException(\"Return value overflow: \" + \(resultVar));")
}
}
printer.print("return \(resultVar);")
}
}

func renderMemoryLayoutValue(for javaType: JavaType) -> String {
if let layout = ForeignValueLayout(javaType: javaType) {
return layout.description
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ extension FFMSwift2JavaGenerator {
/// Describes how to convert the Java parameter to the lowered arguments for
/// the foreign function.
var conversion: JavaConversionStep

/// Whether this parameter requires 32-bit integer overflow checking
var needs32BitIntOverflowCheck: OverflowCheckType = .none
}

enum OverflowCheckType {
case none
case signedInt // Int: -2147483648 to 2147483647
case unsignedInt // UInt: 0 to 4294967295
}

/// Represent a Swift API result translated to Java.
Expand Down Expand Up @@ -88,6 +97,9 @@ extension FFMSwift2JavaGenerator {
/// Describes how to construct the Java result from the foreign function return
/// value and/or the out parameters.
var conversion: JavaConversionStep

/// Whether this result requires 32-bit integer overflow checking
var needs32BitIntOverflowCheck: OverflowCheckType = .none
}

/// Translated Java API representing a Swift API.
Expand Down Expand Up @@ -342,6 +354,14 @@ extension FFMSwift2JavaGenerator {
// be expressed as a Java primitive type.
if let cType = try? CType(cdeclType: swiftType) {
let javaType = cType.javaType
let overflowCheck: OverflowCheckType
if case .integral(.ptrdiff_t) = cType {
overflowCheck = .signedInt
} else if case .integral(.size_t) = cType {
overflowCheck = .unsignedInt
} else {
overflowCheck = .none
}
return TranslatedParameter(
javaParameters: [
JavaParameter(
Expand All @@ -350,7 +370,8 @@ extension FFMSwift2JavaGenerator {
annotations: parameterAnnotations
)
],
conversion: .placeholder
conversion: .placeholder,
needs32BitIntOverflowCheck: overflowCheck
)
}

Expand Down Expand Up @@ -620,11 +641,20 @@ extension FFMSwift2JavaGenerator {
// be expressed as a Java primitive type.
if let cType = try? CType(cdeclType: swiftType) {
let javaType = cType.javaType
let overflowCheck: OverflowCheckType
if case .integral(.ptrdiff_t) = cType {
overflowCheck = .signedInt
} else if case .integral(.size_t) = cType {
overflowCheck = .unsignedInt
} else {
overflowCheck = .none
}
return TranslatedResult(
javaResultType: javaType,
annotations: resultAnnotations,
outParameters: [],
conversion: .placeholder
conversion: .placeholder,
needs32BitIntOverflowCheck: overflowCheck
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@
* @see <a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/thebasics#Int">Swift Int documentation</a>
*/
public class SwiftIntegerOverflowException extends RuntimeException {
static final String BASE_MESSAGE = "Swift runtime has detected IntegerOverflow! Most probably you are running 32-bit application while using Swift's Int type.";

public SwiftIntegerOverflowException() {
super("Swift runtime has detected IntegerOverflow! Most probably you are running 32-bit application while using Swift's Int type.");
super(BASE_MESSAGE);
}

public SwiftIntegerOverflowException(String message) {
super(BASE_MESSAGE + " " + message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,6 @@ public static long addressByteSize() {
* Java does not have unsigned integer types, so we use the layout for Swift's {@code Int}.
*/
public static ValueLayout SWIFT_UINT = SWIFT_INT;

public static final boolean has32bitSwiftInt = (SWIFT_INT == ValueLayout.JAVA_INT);
}
17 changes: 14 additions & 3 deletions Tests/JExtractSwiftTests/DataImportTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,13 @@ final class DataImportTests {
* public init(bytes: UnsafeRawPointer, count: Int)
* }
*/
public static Data init(java.lang.foreign.MemorySegment bytes, long count, AllocatingSwiftArena swiftArena) {
public static Data init(java.lang.foreign.MemorySegment bytes, long count, AllocatingSwiftArena swiftArena) throws SwiftIntegerOverflowException {
MemorySegment _result = swiftArena.allocate(Data.$LAYOUT);
if (SwiftValueLayout.has32bitSwiftInt) {
if (count < Integer.MIN_VALUE || count > Integer.MAX_VALUE) {
throw new SwiftIntegerOverflowException("Parameter 'count' overflow: " + count);
}
}
swiftjava_SwiftModule_Data_init_bytes_count.call(bytes, count, _result);
return Data.wrapMemoryAddressUnsafe(_result, swiftArena);
}
Expand Down Expand Up @@ -295,9 +300,15 @@ final class DataImportTests {
* public var count: Int
* }
*/
public long getCount() {
public long getCount() throws SwiftIntegerOverflowException {
$ensureAlive();
return swiftjava_SwiftModule_Data_count$get.call(this.$memorySegment());
long _result$checked = swiftjava_SwiftModule_Data_count$get.call(this.$memorySegment());
if (SwiftValueLayout.has32bitSwiftInt) {
if (_result$checked < Integer.MIN_VALUE || _result$checked > Integer.MAX_VALUE) {
throw new SwiftIntegerOverflowException("Return value overflow: " + _result$checked);
}
}
return _result$checked;
}
""",

Expand Down
Loading
Loading