Skip to content

Commit d146cf6

Browse files
authored
Test string edge cases for Kotlin and Swift (#1602)
* Test string edge cases for Kotlin and Swift * Kotlin: Throw `CharacterCodingException` on invalid UTF-16 `String` This exception is produced using `CharsetEncoder.onUnmappableCharacter(CodingErrorAction.REPORT)`.
1 parent 826ea99 commit d146cf6

File tree

5 files changed

+49
-7
lines changed

5 files changed

+49
-7
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
import uniffi.uniffi_type_limits.*;
6+
7+
// test_strings
8+
try {
9+
takeString("\ud800")
10+
throw RuntimeException("Should have thrown an CharacterCodingException exception!")
11+
} catch (e: java.nio.charset.CharacterCodingException) {
12+
// It's okay!
13+
}
14+
assert(takeString("") == "")
15+
assert(takeString("") == "")
16+
assert(takeString("💖") == "💖")
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
import Foundation
6+
import uniffi_type_limits
7+
8+
// test_strings
9+
do {
10+
// strings cannot contain surrogates, "\u{d800}" gives an error.
11+
assert(takeString(v: "") == "")
12+
assert(takeString(v: "") == "")
13+
assert(takeString(v: "💖") == "💖")
14+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
uniffi::build_foreign_language_testcases!(
2+
"tests/bindings/test_type_limits.kts",
23
"tests/bindings/test_type_limits.py",
34
"tests/bindings/test_type_limits.rb",
5+
"tests/bindings/test_type_limits.swift",
46
);

uniffi_bindgen/src/bindings/kotlin/templates/StringHelper.kt

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,25 @@ public object FfiConverterString: FfiConverter<String, RustBuffer.ByValue> {
1919
return byteArr.toString(Charsets.UTF_8)
2020
}
2121

22+
fun toUtf8(value: String): ByteBuffer {
23+
// Make sure we don't have invalid UTF-16, check for lone surrogates.
24+
return Charsets.UTF_8.newEncoder().run {
25+
onMalformedInput(CodingErrorAction.REPORT)
26+
encode(CharBuffer.wrap(value))
27+
}
28+
}
29+
2230
override fun lower(value: String): RustBuffer.ByValue {
23-
val byteArr = value.toByteArray(Charsets.UTF_8)
31+
val byteBuf = toUtf8(value)
2432
// Ideally we'd pass these bytes to `ffi_bytebuffer_from_bytes`, but doing so would require us
2533
// to copy them into a JNA `Memory`. So we might as well directly copy them into a `RustBuffer`.
26-
val rbuf = RustBuffer.alloc(byteArr.size)
27-
rbuf.asByteBuffer()!!.put(byteArr)
34+
val rbuf = RustBuffer.alloc(byteBuf.limit())
35+
rbuf.asByteBuffer()!!.put(byteBuf)
2836
return rbuf
2937
}
3038

3139
// We aren't sure exactly how many bytes our string will be once it's UTF-8
32-
// encoded. Allocate 3 bytes per unicode codepoint which will always be
40+
// encoded. Allocate 3 bytes per UTF-16 code unit which will always be
3341
// enough.
3442
override fun allocationSize(value: String): Int {
3543
val sizeForLength = 4
@@ -38,8 +46,8 @@ public object FfiConverterString: FfiConverter<String, RustBuffer.ByValue> {
3846
}
3947

4048
override fun write(value: String, buf: ByteBuffer) {
41-
val byteArr = value.toByteArray(Charsets.UTF_8)
42-
buf.putInt(byteArr.size)
43-
buf.put(byteArr)
49+
val byteBuf = toUtf8(value)
50+
buf.putInt(byteBuf.limit())
51+
buf.put(byteBuf)
4452
}
4553
}

uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import com.sun.jna.Callback
2626
import com.sun.jna.ptr.*
2727
import java.nio.ByteBuffer
2828
import java.nio.ByteOrder
29+
import java.nio.CharBuffer
30+
import java.nio.charset.CodingErrorAction
2931
import java.util.concurrent.ConcurrentHashMap
3032

3133
{%- for req in self.imports() %}

0 commit comments

Comments
 (0)