Skip to content

Commit db9a600

Browse files
committed
Add support for checks during conversion of values from java to swift, implemented besic check for overflow of Int/UInt.
1 parent dc3411d commit db9a600

File tree

10 files changed

+347
-32
lines changed

10 files changed

+347
-32
lines changed

Sources/JExtractSwiftLib/JNI/JNIJavaTypeTranslator.swift

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,7 @@ enum JNIJavaTypeTranslator {
3939

4040
case .int64: return .long
4141
case .uint64: return .long
42-
43-
// FIXME: 32 bit consideration.
44-
// The 'FunctionDescriptor' uses 'SWIFT_INT' which relies on the running
45-
// machine arch. That means users can't pass Java 'long' values to the
46-
// function without casting. But how do we generate code that runs both
47-
// 32 and 64 bit machine?
42+
4843
case .int, .uint: return .long
4944

5045
case .float: return .float
@@ -61,4 +56,38 @@ enum JNIJavaTypeTranslator {
6156
return nil
6257
}
6358
}
59+
60+
static func indirectConversionSetepSwiftType(for knownKind: SwiftKnownTypeDeclKind, from knownTypes: SwiftKnownTypes) -> SwiftType? {
61+
switch knownKind {
62+
case .int: knownTypes.int64
63+
case .uint: knownTypes.uint64
64+
65+
case .bool, .int8, .uint8, .int16, .uint16, .int32, .uint32, .int64, .uint64,
66+
.float, .double, .void, .string,
67+
.unsafeRawPointer, .unsafeMutableRawPointer,
68+
.unsafePointer, .unsafeMutablePointer,
69+
.unsafeRawBufferPointer, .unsafeMutableRawBufferPointer,
70+
.unsafeBufferPointer, .unsafeMutableBufferPointer,
71+
.optional, .foundationData, .foundationDataProtocol, .essentialsData, .essentialsDataProtocol,
72+
.array:
73+
nil
74+
}
75+
}
76+
77+
static func checkStep(for knownKind: SwiftKnownTypeDeclKind, from knownTypes: SwiftKnownTypes) -> JNISwift2JavaGenerator.NativeSwiftConversionCheck? {
78+
switch knownKind {
79+
case .int: .check32BitIntOverflow(typeWithMinAndMax: knownTypes.int32)
80+
case .uint: .check32BitIntOverflow(typeWithMinAndMax: knownTypes.uint32)
81+
82+
case .bool, .int8, .uint8, .int16, .uint16, .int32, .uint32, .int64, .uint64,
83+
.float, .double, .void, .string,
84+
.unsafeRawPointer, .unsafeMutableRawPointer,
85+
.unsafePointer, .unsafeMutablePointer,
86+
.unsafeRawBufferPointer, .unsafeMutableRawBufferPointer,
87+
.unsafeBufferPointer, .unsafeMutableBufferPointer,
88+
.optional, .foundationData, .foundationDataProtocol, .essentialsData, .essentialsDataProtocol,
89+
.array:
90+
nil
91+
}
92+
}
6493
}

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ extension JNISwift2JavaGenerator {
445445
let translatedSignature = translatedDecl.translatedFunctionSignature
446446
let resultType = translatedSignature.resultType.javaType
447447
var parameters = translatedDecl.translatedFunctionSignature.parameters.map { $0.parameter.renderParameter() }
448-
let throwsClause = translatedDecl.isThrowing && !translatedDecl.isAsync ? " throws Exception" : ""
448+
let throwsClause = translatedDecl.throwsClause()
449449

450450
let generics = translatedDecl.translatedFunctionSignature.parameters.reduce(into: [(String, [JavaType])]()) { generics, parameter in
451451
guard case .generic(let name, let extends) = parameter.parameter.type else {

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,12 @@ extension JNISwift2JavaGenerator {
114114
.class(package: nil,name: caseName)
115115
)
116116
])
117+
var exceptions: [JavaException] = []
118+
119+
if enumCase.parameters.contains(where: \.type.isArchDependingInteger) {
120+
exceptions.append(.integerOverflow)
121+
}
122+
117123
let getAsCaseFunction = TranslatedFunctionDecl(
118124
name: getAsCaseName,
119125
isStatic: false,
@@ -137,12 +143,15 @@ extension JNISwift2JavaGenerator {
137143
javaType: .class(package: nil, name: "Optional<\(caseName)>"),
138144
outParameters: conversions.flatMap(\.translated.outParameters),
139145
conversion: enumCase.parameters.isEmpty ? constructRecordConversion : .aggregate(variable: ("$nativeParameters", nativeParametersType), [constructRecordConversion])
140-
)
146+
),
147+
exceptions: exceptions
141148
),
142149
nativeFunctionSignature: NativeFunctionSignature(
143150
selfParameter: NativeParameter(
144151
parameters: [JavaParameter(name: "self", type: .long)],
145-
conversion: .extractSwiftValue(.placeholder, swiftType: .nominal(enumCase.enumType), allowNil: false)
152+
conversion: .extractSwiftValue(.placeholder, swiftType: .nominal(enumCase.enumType), allowNil: false),
153+
indirectConversion: nil,
154+
conversionCheck: nil
146155
),
147156
parameters: [],
148157
result: NativeResult(
@@ -291,12 +300,19 @@ extension JNISwift2JavaGenerator {
291300
genericRequirements: functionSignature.genericRequirements
292301
)
293302

303+
var exceptions: [JavaException] = []
304+
305+
if functionSignature.parameters.contains(where: \.type.isArchDependingInteger) {
306+
exceptions.append(.integerOverflow)
307+
}
308+
294309
let resultType = try translate(swiftResult: functionSignature.result)
295310

296311
return TranslatedFunctionSignature(
297312
selfParameter: selfParameter,
298313
parameters: parameters,
299-
resultType: resultType
314+
resultType: resultType,
315+
exceptions: exceptions
300316
)
301317
}
302318

@@ -955,12 +971,22 @@ extension JNISwift2JavaGenerator {
955971
var annotations: [JavaAnnotation] {
956972
self.translatedFunctionSignature.annotations
957973
}
974+
975+
func throwsClause() -> String {
976+
guard !translatedFunctionSignature.exceptions.isEmpty else {
977+
return isThrowing && !isAsync ? " throws Exception" : ""
978+
}
979+
980+
let signatureExceptions = translatedFunctionSignature.exceptions.compactMap(\.type.className).joined(separator: ", ")
981+
return " throws \(signatureExceptions)\(isThrowing ? ", Exception" : "")"
982+
}
958983
}
959984

960985
struct TranslatedFunctionSignature {
961986
var selfParameter: TranslatedParameter?
962987
var parameters: [TranslatedParameter]
963988
var resultType: TranslatedResult
989+
var exceptions: [JavaException]
964990

965991
// if the result type implied any annotations,
966992
// propagate them onto the function the result is returned from

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,16 @@ extension JNISwift2JavaGenerator {
103103
throw JavaTranslationError.unsupportedSwiftType(type)
104104
}
105105

106+
let indirectStepType = JNIJavaTypeTranslator.indirectConversionSetepSwiftType(for: knownType, from: knownTypes)
107+
let indirectCheck = JNIJavaTypeTranslator.checkStep(for: knownType, from: knownTypes)
108+
106109
return NativeParameter(
107110
parameters: [
108111
JavaParameter(name: parameterName, type: javaType)
109112
],
110-
conversion: .initFromJNI(.placeholder, swiftType: type)
113+
conversion: indirectStepType != nil ? .labelessAssignmentOfVariable(.placeholder, swiftType: type) : .initFromJNI(.placeholder, swiftType: type),
114+
indirectConversion: indirectStepType.flatMap { .initFromJNI(.placeholder, swiftType: $0) },
115+
conversionCheck: indirectCheck
111116
)
112117

113118
}
@@ -129,7 +134,9 @@ extension JNISwift2JavaGenerator {
129134
fatalErrorMessage: "\(parameterName) was null in call to \\(#function), but Swift requires non-optional!"
130135
),
131136
wrapperName: nominalTypeName
132-
)
137+
),
138+
indirectConversion: nil,
139+
conversionCheck: nil
133140
)
134141
}
135142

@@ -138,15 +145,19 @@ extension JNISwift2JavaGenerator {
138145
parameters: [
139146
JavaParameter(name: parameterName, type: .long)
140147
],
141-
conversion: .pointee(.extractSwiftValue(.placeholder, swiftType: type))
148+
conversion: .pointee(.extractSwiftValue(.placeholder, swiftType: type)),
149+
indirectConversion: nil,
150+
conversionCheck: nil
142151
)
143152

144153
case .tuple([]):
145154
return NativeParameter(
146155
parameters: [
147156
JavaParameter(name: parameterName, type: .void)
148157
],
149-
conversion: .placeholder
158+
conversion: .placeholder,
159+
indirectConversion: nil,
160+
conversionCheck: nil
150161
)
151162

152163
case .function(let fn):
@@ -169,7 +180,9 @@ extension JNISwift2JavaGenerator {
169180
conversion: .closureLowering(
170181
parameters: parameters,
171182
result: result
172-
)
183+
),
184+
indirectConversion: nil,
185+
conversionCheck: nil
173186
)
174187

175188
case .optional(let wrapped):
@@ -242,7 +255,9 @@ extension JNISwift2JavaGenerator {
242255
.placeholder,
243256
typeMetadataVariableName: .combinedName(component: "typeMetadataAddress"),
244257
protocolNames: protocolNames
245-
)
258+
),
259+
indirectConversion: nil,
260+
conversionCheck: nil
246261
)
247262
}
248263

@@ -272,7 +287,9 @@ extension JNISwift2JavaGenerator {
272287
.initFromJNI(.placeholder, swiftType: swiftType),
273288
discriminatorName: discriminatorName,
274289
valueName: valueName
275-
)
290+
),
291+
indirectConversion: nil,
292+
conversionCheck: nil
276293
)
277294
}
278295

@@ -285,7 +302,9 @@ extension JNISwift2JavaGenerator {
285302
parameters: [
286303
JavaParameter(name: parameterName, type: javaType)
287304
],
288-
conversion: .optionalMap(.initializeJavaKitWrapper(.placeholder, wrapperName: nominalTypeName))
305+
conversion: .optionalMap(.initializeJavaKitWrapper(.placeholder, wrapperName: nominalTypeName)),
306+
indirectConversion: nil,
307+
conversionCheck: nil
289308
)
290309
}
291310

@@ -300,7 +319,9 @@ extension JNISwift2JavaGenerator {
300319
allowNil: true
301320
)
302321
)
303-
)
322+
),
323+
indirectConversion: nil,
324+
conversionCheck: nil
304325
)
305326

306327
default:
@@ -434,7 +455,9 @@ extension JNISwift2JavaGenerator {
434455
parameters: [
435456
JavaParameter(name: parameterName, type: javaType)
436457
],
437-
conversion: .getJValue(.placeholder)
458+
conversion: .getJValue(.placeholder),
459+
indirectConversion: nil,
460+
conversionCheck: nil
438461
)
439462
}
440463

@@ -570,7 +593,9 @@ extension JNISwift2JavaGenerator {
570593
parameters: [
571594
JavaParameter(name: parameterName, type: .array(javaType)),
572595
],
573-
conversion: .initFromJNI(.placeholder, swiftType: .array(elementType))
596+
conversion: .initFromJNI(.placeholder, swiftType: .array(elementType)),
597+
indirectConversion: nil,
598+
conversionCheck: nil
574599
)
575600
}
576601

@@ -594,7 +619,9 @@ extension JNISwift2JavaGenerator {
594619
convertLongFromJNI: false
595620
))))
596621
]
597-
)
622+
),
623+
indirectConversion: nil,
624+
conversionCheck: nil
598625
)
599626

600627
default:
@@ -616,6 +643,12 @@ extension JNISwift2JavaGenerator {
616643

617644
/// Represents how to convert the JNI parameter to a Swift parameter
618645
let conversion: NativeSwiftConversionStep
646+
647+
/// Represents swift type for indirect variable used in required checks, e.g Int64 for Int overflow check on 32-bit platforms
648+
let indirectConversion: NativeSwiftConversionStep?
649+
650+
/// Represents check operations executed in if/guard conditional block for check during conversion
651+
let conversionCheck: NativeSwiftConversionCheck?
619652
}
620653

621654
struct NativeResult {
@@ -695,6 +728,8 @@ extension JNISwift2JavaGenerator {
695728
/// `{ (args) -> return body }`
696729
indirect case closure(args: [String] = [], body: NativeSwiftConversionStep)
697730

731+
indirect case labelessAssignmentOfVariable(NativeSwiftConversionStep, swiftType: SwiftType)
732+
698733
/// Returns the conversion string applied to the placeholder.
699734
func render(_ printer: inout CodePrinter, _ placeholder: String) -> String {
700735
// NOTE: 'printer' is used if the conversion wants to cause side-effects.
@@ -1025,6 +1060,20 @@ extension JNISwift2JavaGenerator {
10251060
}
10261061
}
10271062
return printer.finalize()
1063+
case .labelessAssignmentOfVariable(let name, let swiftType):
1064+
return "\(swiftType)(indirect_\(name.render(&printer, placeholder)))"
1065+
}
1066+
}
1067+
}
1068+
1069+
enum NativeSwiftConversionCheck {
1070+
case check32BitIntOverflow(typeWithMinAndMax: SwiftType)
1071+
1072+
// Returns the check string
1073+
func render(_ printer: inout CodePrinter, _ placeholder: String) -> String {
1074+
switch self {
1075+
case .check32BitIntOverflow(let minMaxSource):
1076+
return "\(placeholder) >= \(minMaxSource).min && \(placeholder) <= \(minMaxSource).max"
10281077
}
10291078
}
10301079
}

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -293,11 +293,43 @@ extension JNISwift2JavaGenerator {
293293
let tryClause: String = decl.isThrowing ? "try " : ""
294294

295295
// Regular parameters.
296-
var arguments = [String]()
296+
var arguments: [String] = [String]()
297+
var indirectVariables: [(name: String, lowered: String)] = []
298+
var int32OverflowChecks: [String] = []
299+
297300
for (idx, parameter) in nativeSignature.parameters.enumerated() {
298301
let javaParameterName = translatedDecl.translatedFunctionSignature.parameters[idx].parameter.name
299302
let lowered = parameter.conversion.render(&printer, javaParameterName)
300303
arguments.append(lowered)
304+
305+
parameter.indirectConversion.flatMap {
306+
indirectVariables.append((javaParameterName, $0.render(&printer, javaParameterName)))
307+
}
308+
309+
switch parameter.conversionCheck {
310+
case .check32BitIntOverflow:
311+
int32OverflowChecks.append(parameter.conversionCheck!.render(&printer, "indirect_\(javaParameterName)"))
312+
case nil:
313+
break
314+
}
315+
}
316+
317+
// Make indirect variables
318+
for (name, lowered) in indirectVariables {
319+
printer.print("let indirect_\(name) = \(lowered)")
320+
}
321+
322+
if !int32OverflowChecks.isEmpty {
323+
printer.print("#if _pointerBitWidth(_32)")
324+
325+
for check in int32OverflowChecks {
326+
printer.printBraceBlock("guard \(check) else") { printer in
327+
let dummyReturn = dummyReturn(for: nativeSignature)
328+
printer.print("environment.throwJavaException(javaException: .integerOverflow)")
329+
printer.print(dummyReturn)
330+
}
331+
}
332+
printer.print("#endif")
301333
}
302334

303335
// Callee
@@ -362,14 +394,7 @@ extension JNISwift2JavaGenerator {
362394
}
363395

364396
if decl.isThrowing, !decl.isAsync {
365-
let dummyReturn: String
366-
367-
if nativeSignature.result.javaType.isVoid {
368-
dummyReturn = ""
369-
} else {
370-
// We assume it is something that implements JavaValue
371-
dummyReturn = "return \(nativeSignature.result.javaType.swiftTypeName(resolver: { _ in "" })).jniPlaceholderValue"
372-
}
397+
let dummyReturn = dummyReturn(for: nativeSignature)
373398

374399
printer.print("do {")
375400
printer.indent()
@@ -390,6 +415,15 @@ extension JNISwift2JavaGenerator {
390415
}
391416
}
392417

418+
private func dummyReturn(for nativeSignature: NativeFunctionSignature) -> String {
419+
return if nativeSignature.result.javaType.isVoid {
420+
"return"
421+
} else {
422+
// We assume it is something that implements JavaValue
423+
"return \(nativeSignature.result.javaType.swiftTypeName(resolver: { _ in "" })).jniPlaceholderValue"
424+
}
425+
}
426+
393427
private func printCDecl(
394428
_ printer: inout CodePrinter,
395429
_ translatedDecl: TranslatedFunctionDecl,

0 commit comments

Comments
 (0)