summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_bindgen
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:35:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:35:49 +0000
commitd8bbc7858622b6d9c278469aab701ca0b609cddf (patch)
treeeff41dc61d9f714852212739e6b3738b82a2af87 /third_party/rust/uniffi_bindgen
parentReleasing progress-linux version 125.0.3-1~progress7.99u1. (diff)
downloadfirefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz
firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/uniffi_bindgen')
-rw-r--r--third_party/rust/uniffi_bindgen/.cargo-checksum.json2
-rw-r--r--third_party/rust/uniffi_bindgen/Cargo.toml15
-rw-r--r--third_party/rust/uniffi_bindgen/README.md81
-rw-r--r--third_party/rust/uniffi_bindgen/src/backend/filters.rs4
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/callback_interface.rs2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/compounds.rs115
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/executor.rs24
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/external.rs4
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs251
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/object.rs19
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/primitives.rs10
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Async.kt107
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/BooleanHelper.kt2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ByteArrayHelper.kt4
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceImpl.kt117
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceRuntime.kt65
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceTemplate.kt140
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CustomTypeTemplate.kt2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/DurationHelper.kt2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt30
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ErrorTemplate.kt18
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ExternalTypeTemplate.kt2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/FfiConverterTemplate.kt6
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Float32Helper.kt2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Float64Helper.kt2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ForeignExecutorTemplate.kt83
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/HandleMap.kt27
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Helpers.kt152
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int16Helper.kt2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int32Helper.kt2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int64Helper.kt2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int8Helper.kt2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Interface.kt14
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/MapTemplate.kt4
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt61
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelper.kt40
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelperAndroid.kt26
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelperJvm.kt25
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectRuntime.kt161
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt343
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/OptionalTemplate.kt6
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/README.md13
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt29
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/RustBufferTemplate.kt39
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/SequenceTemplate.kt6
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/StringHelper.kt10
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/TimestampHelper.kt2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt52
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt40
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt16Helper.kt2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt32Helper.kt2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt64Helper.kt2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt8Helper.kt2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/macros.kt140
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt4
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/test.rs16
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/compounds.rs16
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/executor.rs18
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/external.rs2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/mod.rs177
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Async.py86
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/BooleanHelper.py18
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/BytesHelper.py5
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py98
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceRuntime.py59
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceTemplate.py112
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/CustomType.py9
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/DurationHelper.py8
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py71
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py19
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py8
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float32Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float64Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/ForeignExecutorTemplate.py63
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/HandleMap.py33
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Helpers.py36
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int16Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int32Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int64Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int8Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/MapTemplate.py6
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py45
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py124
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/OptionalTemplate.py5
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/PointerManager.py68
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Protocol.py9
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py24
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferHelper.py18
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferTemplate.py11
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/SequenceTemplate.py5
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/StringHelper.py4
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/TimestampHelper.py4
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py24
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Types.py5
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt16Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt32Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt64Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt8Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/macros.py95
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/wrapper.py17
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/test.rs14
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs79
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb36
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb7
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferBuilder.rb2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferStream.rb4
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferTemplate.rb42
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb4
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/macros.rb10
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/test.rs15
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/callback_interface.rs12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/compounds.rs5
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/executor.rs23
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/external.rs2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs209
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/object.rs18
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/primitives.rs8
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Async.swift100
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BridgingHeaderTemplate.h48
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceImpl.swift113
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceRuntime.swift57
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift145
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift27
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ForeignExecutorTemplate.swift69
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/HandleMap.swift40
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift44
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift201
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Protocol.swift12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift17
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift4
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift49
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Types.swift3
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/macros.swift125
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift8
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/test.rs24
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/callbacks.rs207
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/enum_.rs224
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/ffi.rs195
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/function.rs32
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/mod.rs342
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/object.rs199
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/record.rs53
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/universe.rs5
-rw-r--r--third_party/rust/uniffi_bindgen/src/lib.rs138
-rw-r--r--third_party/rust/uniffi_bindgen/src/library_mode.rs40
-rw-r--r--third_party/rust/uniffi_bindgen/src/macro_metadata/ci.rs21
-rw-r--r--third_party/rust/uniffi_bindgen/src/macro_metadata/extract.rs2
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/mod.rs36
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/CallbackInterfaceTemplate.rs93
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/EnumTemplate.rs13
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/ErrorTemplate.rs10
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/ExternalTypesTemplate.rs2
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/ObjectTemplate.rs34
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/RecordTemplate.rs8
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/TopLevelFunctionTemplate.rs5
156 files changed, 4533 insertions, 2371 deletions
diff --git a/third_party/rust/uniffi_bindgen/.cargo-checksum.json b/third_party/rust/uniffi_bindgen/.cargo-checksum.json
index 880d79a96f..40b0b6e3cc 100644
--- a/third_party/rust/uniffi_bindgen/.cargo-checksum.json
+++ b/third_party/rust/uniffi_bindgen/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"5285a403f48ecf7a073cbe1839c7afbd0e14998e8315b1ff8ecefb9e7171fc4e","askama.toml":"1a245b7803adca782837e125c49100147d2de0d5a1c949ff95e91af1701f6058","src/backend/config.rs":"4861dbf251dbb10beb1ed7e3eea7d79499a0de1cd9ce9ee8381a0e729c097dea","src/backend/filters.rs":"2da4eaa9af92e449f2fa20d06fc2ab2f758a9a10d3ad6cb8a94975184d40d2ff","src/backend/mod.rs":"2ee9d974cd259f7fb156028b4f4f7601691e94fb5692a6daf0d362df3ecf79a8","src/backend/types.rs":"598df3a861f5d53b2c710848943f6049dd43cb4f37aa81f2c08fd36fc5b2f5d5","src/bindings/kotlin/gen_kotlin/callback_interface.rs":"a6c7796ca66cbaabeef401b939d3c707bba17a77581da36a3a0b46f87630440e","src/bindings/kotlin/gen_kotlin/compounds.rs":"ebd2111a74032b336e0768facfb756a9422da2f9b413ee929b24c1c4315e6a06","src/bindings/kotlin/gen_kotlin/custom.rs":"7e619f7320796ecd8c4ced82904b4bd3c6a0043b55d5829274ab3040cdf9cd7f","src/bindings/kotlin/gen_kotlin/enum_.rs":"6559bb00d8e359126b016e549263c0c9bc1dfc5654ed662c0c2912b47931b1e4","src/bindings/kotlin/gen_kotlin/executor.rs":"58a192123fd2dd4b625f29d95ae6bf5161c2fef7bf50aa8790c3ae0e7a9430d9","src/bindings/kotlin/gen_kotlin/external.rs":"bcd2a44f2559a124aa287944ab59296239033372c6b4a7a3b625b1d41c441de2","src/bindings/kotlin/gen_kotlin/miscellany.rs":"6541987e627c4ff27a17ebe919b2b5cd97cb66ff41187ed636396b4e35ea2414","src/bindings/kotlin/gen_kotlin/mod.rs":"ccae80314058df0b4988d0965ab62b0dc872e8676592e7b55037cd54011e6854","src/bindings/kotlin/gen_kotlin/object.rs":"539ec05386c1e844bef09d4de8374760daa5ba99b009615c04be9c3927feb4c9","src/bindings/kotlin/gen_kotlin/primitives.rs":"2c3020416384a67855ca5086e485c4db6d7dcc3ce51343217b4a914b318ae350","src/bindings/kotlin/gen_kotlin/record.rs":"96fd1a180095a062b4a9b71d4f603b232f0133f46355a3e427c4064701d900f2","src/bindings/kotlin/gen_kotlin/variant.rs":"d111d6888745195fc2c24bdddc57359e771616102a8d182c5c8ad268b0a13460","src/bindings/kotlin/mod.rs":"ef88eb9b5b7d6f920c62a525ea4d4bf2a3b1a9154afaa012cdb2feea597fbf23","src/bindings/kotlin/templates/Async.kt":"064ce385ac0e68719de625e0172908257714b62da296ff14b2c0153b9966212a","src/bindings/kotlin/templates/BooleanHelper.kt":"28e8a5088c8d58c9bfdbc575af8d8725060521fdd7d092684a8044b24ae567c7","src/bindings/kotlin/templates/ByteArrayHelper.kt":"6fbb424556f631beb7f28c4168c568ad840445496a29d5c8f40a9a591b1661b1","src/bindings/kotlin/templates/CallbackInterfaceRuntime.kt":"29fab10a8f6b699471e793e8d53f5bed74803a8c433ff80ccef5f56cf742c54a","src/bindings/kotlin/templates/CallbackInterfaceTemplate.kt":"3b063ea03959c95327b6082c7edc0db0df83ee8b9e8d643b92ca45cf4fda458a","src/bindings/kotlin/templates/CustomTypeTemplate.kt":"be9bdc716731f3935a4d48728e33bfeb4acd514f3719ddbb273adcd6fb4ab31f","src/bindings/kotlin/templates/DurationHelper.kt":"414a98161538a26f3a9b357353270c1f245ad6ceed99496aca7162cf473a92fd","src/bindings/kotlin/templates/EnumTemplate.kt":"865fb1badd1a128390903ab8d9f42f9208c6db0eac5e53b88207282176cfd67f","src/bindings/kotlin/templates/ErrorTemplate.kt":"394c0093c0c86a0f2a14cd5fa60a70ba9970c65448867b6aca86fc25cfe08a4c","src/bindings/kotlin/templates/ExternalTypeTemplate.kt":"40df49116f9ea227c9a64a4f45bb7c2e99275c62e93f75290808e2c930911fba","src/bindings/kotlin/templates/FfiConverterTemplate.kt":"aa22962aaa9f641d48ccf44cb56d9f8a7736cbfaa01e1a1656662cfe5dd5c1d7","src/bindings/kotlin/templates/Float32Helper.kt":"662d95af3b629d143fb4d47cb7e9aa26ed28a5f3846de0341e28b0f9fb08bc25","src/bindings/kotlin/templates/Float64Helper.kt":"a77d099fa7d91e8702c1700e7949ffb6aaba9c6e8041ff48bab34b8e1fc9a0aa","src/bindings/kotlin/templates/ForeignExecutorTemplate.kt":"09c63a67adb8c6cb807108f02d7695d3425401ea0cc51b582cfd469a322fcce0","src/bindings/kotlin/templates/Helpers.kt":"90a11ec576e82265ba0f95fc330053779aada5976477f0d4a6b38619da1282cf","src/bindings/kotlin/templates/Int16Helper.kt":"7f83c4a48e1f3b2a59a3ca6a2662be8bc9baf3a5a748b31223cb3f51721ef249","src/bindings/kotlin/templates/Int32Helper.kt":"e02e4702175554b09fd2dd6ac3089dcd2c395f08ec60e762159566a9c9889450","src/bindings/kotlin/templates/Int64Helper.kt":"7a6fd6ca486852c89399c699935a9dfa1c32b9356d9a965cfde532581f05d9fa","src/bindings/kotlin/templates/Int8Helper.kt":"0554545494b6b9a76ce94f9c1723f8cf4230a13076feb75d620b1c9ca1ac4668","src/bindings/kotlin/templates/MapTemplate.kt":"07d20d8cf58a4bca950ac22dbec5e3471ac6c18c3cca562e45628de827b03ccf","src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt":"22226bb8dde52f12c77b40bbb8f7ceb7dcef313950b8d42b2f5fe44777158bfc","src/bindings/kotlin/templates/ObjectRuntime.kt":"7f38f54a0c889d7534d23afdace6b87b6ced5c024a36b5450078a06e071caad8","src/bindings/kotlin/templates/ObjectTemplate.kt":"3f3baea52b6923446827ea1ee3f5160edfe81e00c11f61ea1f72dbc6b796a6d8","src/bindings/kotlin/templates/OptionalTemplate.kt":"c916c4545d37087ee01a6b6aef966928691d26be539c388fce608e9e3ff4b0e7","src/bindings/kotlin/templates/RecordTemplate.kt":"8d573856de75b55b985594ac4e4a6f08da931dce6b52420654b5bb080eec414f","src/bindings/kotlin/templates/RustBufferTemplate.kt":"002878ce9ce9924231e55853b86768fc3dec2caef6a28098f01c3edd739ca076","src/bindings/kotlin/templates/SequenceTemplate.kt":"477a0f6714af151ca58cfc7c4f2cf0e878d391dd9db4efe964f19d5ad4f544f0","src/bindings/kotlin/templates/StringHelper.kt":"ec0441da90a394616d0ba3492eca50602161fe42062bc4f60e9a23191e71e009","src/bindings/kotlin/templates/TimestampHelper.kt":"353c2890f06ad6dda238e9aebb4bdff7bb838e17e46abf351ed3ff1fbc4e6580","src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt":"888ef82e2637ab104f0821803666c77212b5d5940414f71e899f8f8968ffe572","src/bindings/kotlin/templates/Types.kt":"4d87ef529f666db532fb5355a339fd50be3edd6225a703564455b81ef4d4a16d","src/bindings/kotlin/templates/UInt16Helper.kt":"e84a1f30a5a899ba2c5db614d3f3c74f25bccf6dd99bf68b8830829332d051e9","src/bindings/kotlin/templates/UInt32Helper.kt":"7cdf08cc580046935f27ba07b53685968608a102e0a6be305111037c63d7ddf8","src/bindings/kotlin/templates/UInt64Helper.kt":"fd7baacbf3ab6202ff83edcc66e5f7beb11a10053ba66d0b49547616cc7cbe1f","src/bindings/kotlin/templates/UInt8Helper.kt":"bbf5a6d66c995aea9fe2fa9840c6bfa78b03520a09469b984f0e1d43191e453a","src/bindings/kotlin/templates/macros.kt":"0f64366a9d7523b3d20d7e9d8b04eb064568772dec529a12f878acf5d2246a41","src/bindings/kotlin/templates/wrapper.kt":"d515ca22d12f13b1b99c5daa411ea35d9a288076e4b7208eaf88170e5da7477c","src/bindings/kotlin/test.rs":"0f752ab0afde20194afca07af94da9d1422300032696d5f845cd864fc63c5d51","src/bindings/mod.rs":"949f323d6eb5c018497103dbb9dcffb8f395eb5960694b551a24b4887e853afb","src/bindings/python/gen_python/callback_interface.rs":"5df3e091d3c88ef7645e570f693942161a9b9c6307419c15a2534fbc5da974af","src/bindings/python/gen_python/compounds.rs":"9b7187d35826e1b12dbc2b16a13aec783a51f0952e3e2d24adaefbd0ac005016","src/bindings/python/gen_python/custom.rs":"81501641648eb638f5a338c01a71db0d0e96601c3dda83acdb2d49072b387d42","src/bindings/python/gen_python/enum_.rs":"7c3f8f6a97c1491175c8b93b8f9ab13748e2f8084bb717836b6935d024805439","src/bindings/python/gen_python/error.rs":"161bd2e041e3a63a91899de173eec8450cc10e1e9552d064969aa72a02fdfd5e","src/bindings/python/gen_python/executor.rs":"dbbf2292c79f73dacb317a8645185e42f328a017951662975c0488399c562058","src/bindings/python/gen_python/external.rs":"0325e9a39645eb5454d716d4db76a4a31083ddfafa8e9ca063257292372a0637","src/bindings/python/gen_python/miscellany.rs":"d6f6305dd0af85b7ba87b70cbe6ecba00c83d5082c5bdcaf25962fff853973ea","src/bindings/python/gen_python/mod.rs":"4ac0dc0fd9aaabf2e1f9245d13e0090ee0e9c1235ef203bec9314cac5974e9f0","src/bindings/python/gen_python/object.rs":"a4d4c20a0a52687feff2b9a547a13aa9bda09b3af9ec26508646658a88eec8b3","src/bindings/python/gen_python/primitives.rs":"b830c68e20d8539b8ac5566f1ca0dd262c1b14712a747f79e70004cd8f409ba1","src/bindings/python/gen_python/record.rs":"f8e12ce43d7e0f37f05420a849e7867b7251f9790933609a4cb99050fd063089","src/bindings/python/mod.rs":"eac32ce383460d58d3ccf1d406173465fc8a1db8a24408df67620b7d14dcd0cd","src/bindings/python/templates/Async.py":"4a35a878883a548f3bbed929a9ec74c133e2e9cf08375989503e73ddc2f9c648","src/bindings/python/templates/BooleanHelper.py":"c19e38ae3daa29a831f2394a0a2e74c924711e55ddc85db8ac9b5b8b6da9cd92","src/bindings/python/templates/BytesHelper.py":"e8fb9919acc784fb056bba4ab8d5c04ca7b2275211f8397ef2a391833e3d5e8f","src/bindings/python/templates/CallbackInterfaceRuntime.py":"795d8826d5d2b397a91c531c6b1b76d9425728efdcb90514170c8c4f35053e40","src/bindings/python/templates/CallbackInterfaceTemplate.py":"3f38e7b290fce198d188a63dcfa74486810153816283e683d6e04896c2dbea9a","src/bindings/python/templates/CustomType.py":"12064dde5e1baf4d78e541837c414cdc7ee9e827a284c54a98ac92dfaf3478e8","src/bindings/python/templates/DurationHelper.py":"271c301bc480cd48d5df2aec15789dd360f4d3098a9f360d7f8f33fa0a7fcd0a","src/bindings/python/templates/EnumTemplate.py":"1cbc2206f045c3050be1912df581a5393d6f0a4a79c96d8b49661696d25830e0","src/bindings/python/templates/ErrorTemplate.py":"f9ab6c910024e88ff92a7575d5d00cdafe448b2e85d07a9edea57ae7b6dc5864","src/bindings/python/templates/ExternalTemplate.py":"ed4d65caf2de3fd2c2a3fd2658eb44cf91cd2f0878c017be63afa4394bf56be3","src/bindings/python/templates/Float32Helper.py":"80c0a0619d2c58c100ea8db37125878c8e8cf56c42f77195aa9b4b6b6d5716c8","src/bindings/python/templates/Float64Helper.py":"62c3ed0d646d3383c890d1f8fd7cb8639433971b9ba9261ac43c1391472eb141","src/bindings/python/templates/ForeignExecutorTemplate.py":"6a7903acc65b9dac17524767d94b142d38d25d5f5bb27133ee9fd7ed8fb5f5a9","src/bindings/python/templates/Helpers.py":"3265eeb5917e0090a7dbd50fdbd12e7d1b1832a58b347cd003ecf8a433c9ebcd","src/bindings/python/templates/Int16Helper.py":"ef7fd0035a80aef556bdbfbcf074751d4e25661f4e07f9bf41f48601d171e5aa","src/bindings/python/templates/Int32Helper.py":"af7e0176ed41260089426498946e47565a7d57e98dffaae4562dfe541c3019d1","src/bindings/python/templates/Int64Helper.py":"171908319be9edcfe3b178d1d74f0173df2aae6a4a92895a2079fa476caed7df","src/bindings/python/templates/Int8Helper.py":"d02a4a5452ec1096b1b1953e4d661d699f9f8f0ce5086a6f3577a0119f479666","src/bindings/python/templates/MapTemplate.py":"fd0bd7e396a6288a16ecb3dae087ab725be556789887a4dc0c00ab97d815f3fb","src/bindings/python/templates/NamespaceLibraryTemplate.py":"b78b7161d43a95f5a0b5d7da6cae0d8bf47017c6c77ad210e15be9531413baa9","src/bindings/python/templates/ObjectTemplate.py":"3af0c737d1b482169d62dee1b6c49f1a18ea3f485e9fc323b070f35cf00c242c","src/bindings/python/templates/OptionalTemplate.py":"59df962441fb1c50cb99be014c87ecf69450c1a3b60fa6763bd40e9e948124b1","src/bindings/python/templates/PointerManager.py":"22faf6a2801cf756f3b09415b597f0cd403a3872ac99a7e44e3b7b6217606cd7","src/bindings/python/templates/RecordTemplate.py":"04fcbd662bc9817597366046e09321d2516eb8240e796dd9b6f971c347475429","src/bindings/python/templates/RustBufferHelper.py":"8a8c20d195534e465a173dc778ae98957c50e209ec824af2d2b143f5ba6061f5","src/bindings/python/templates/RustBufferTemplate.py":"f5e247b0f8988f29ce94ab50b5fe7749d0423cda1d37b6145cc8a6c5a9818449","src/bindings/python/templates/SequenceTemplate.py":"047f19074fe08982b59001da2ba7318b331ce431d73503e111330579a3ba065f","src/bindings/python/templates/StringHelper.py":"a3f874ea9330413e854c2ebbeff5507e32a166de6967c5cea63ede1f021e267a","src/bindings/python/templates/TimestampHelper.py":"de099ce51ceaa86519c28bb38e21933ead36ff341f4907695029212bcdfed3ac","src/bindings/python/templates/TopLevelFunctionTemplate.py":"93b6101fae2cafdf1a9325bed07019609ac35bacef2dc31ba4be5c256d827473","src/bindings/python/templates/Types.py":"feb69d895e9279e52479146e1dfe2fec48c547378ef2cc0fb988f6acaf6bcc63","src/bindings/python/templates/UInt16Helper.py":"5fbb30ece1f9a2680b60baf680ec4e2936d64de2ff107018e751ef1c041443ef","src/bindings/python/templates/UInt32Helper.py":"84207c380e38a38bb919d58769384a0f4fa175ebbd04ac451b37ccfe01ff68a1","src/bindings/python/templates/UInt64Helper.py":"4606f381834740319a9f604a418ba149917a6dbd43d3d3d8da50c655893e2c8e","src/bindings/python/templates/UInt8Helper.py":"aecf7cf08b7dc75fe81e8dcac78dfce166b132d5c20b4301d845c479ab9f49ba","src/bindings/python/templates/macros.py":"7d0d08f418edf65ff365bf1fb37e3132aebb720dfecdcef3e3ce50529fe0eb6b","src/bindings/python/templates/wrapper.py":"91e8cbf18e5b7d0b2be31c0e09b230318d19406c316b453dab973341eb2c6add","src/bindings/python/test.rs":"48e93959ce3e34ff0191126416301b170239d3e2665711da786e0b8b7a90a2ab","src/bindings/ruby/gen_ruby/mod.rs":"7f3a94537c331a941e6e010e35563247f11f1fedaf971cb8538e17797cb17efa","src/bindings/ruby/gen_ruby/tests.rs":"7dcb86b08e643c43503f4cac6396833497f6988b004321c0067700ee29ffbf32","src/bindings/ruby/mod.rs":"0fdfab5306dc5c05fbcbfb273340d96ad70c5caf5074834ad6851af1a9a56734","src/bindings/ruby/templates/EnumTemplate.rb":"5480edb347f5829e478d19474691babd72f37616ed846d519b5a61cb1d6cf047","src/bindings/ruby/templates/ErrorTemplate.rb":"301c177e639f0a27f19d4935c5317e672aadecbee2f9bfa778df982320f5148d","src/bindings/ruby/templates/Helpers.rb":"ce7ed4be97dad507b991c69c28dc7bb6427e5e79a4b2fba9dad9dccabc3e090c","src/bindings/ruby/templates/NamespaceLibraryTemplate.rb":"9a24c427b9eba99d9e13181a5559a385b5d1d16beae2b72a402f2645b22a9048","src/bindings/ruby/templates/ObjectTemplate.rb":"0cfd9438e4821cf2164b23d748b3227a8cffbe2fab5b7eb70832228ccb628ee0","src/bindings/ruby/templates/RecordTemplate.rb":"4aeff886928ca972e5dc9b799581b30c66a6f6dce446af3285dd3ed6b422dea9","src/bindings/ruby/templates/RustBufferBuilder.rb":"8da4e425b36dde4f171b238cbe57e02fb55e91a45a82134c1dccc0fc360733c0","src/bindings/ruby/templates/RustBufferStream.rb":"43ad2defc772fd24b68df0736533c26597ba007e89b6a5ba0d31fbe356648151","src/bindings/ruby/templates/RustBufferTemplate.rb":"405a32592cab145175b64e21398f83e6e0f16354552c0395479270e732910e08","src/bindings/ruby/templates/TopLevelFunctionTemplate.rb":"88213e7e25bef664da939c04dd5621f438af735ffcb4d2d0c24a529538630069","src/bindings/ruby/templates/macros.rb":"79d7d0e9af749dadbf242f37c0f86af7c616ea5318da127747def40f6cdb20d1","src/bindings/ruby/templates/wrapper.rb":"f82b41543546f8e5804cd0e1785f4735d9dd95383566d0e5ba1cd4d9e8c0578d","src/bindings/ruby/test.rs":"d19837119725233bd9971ca2dfc3256156071c64e6dfaf07ad2307432c055bbb","src/bindings/swift/gen_swift/callback_interface.rs":"6b51276350f506f96fefd0ae8cb3afdcd514e8a529d9e982afc68cbf68d74578","src/bindings/swift/gen_swift/compounds.rs":"1aba37cf2f438423a4ce476eea6a36f71f1d5daddbb77c88556bc3abde287ca7","src/bindings/swift/gen_swift/custom.rs":"bddb601b4ea8810ecbad01271d5ec0b3958999b09bc9382c83637dfd43451734","src/bindings/swift/gen_swift/enum_.rs":"87be67ec3394616368d9ef8e99b7f234c053b3bee9a7f9e6f2dff37f147c8837","src/bindings/swift/gen_swift/executor.rs":"ab672e2d05acbc2c4a839af22034aa557d5e69f1d9c913158310ea1e93851557","src/bindings/swift/gen_swift/external.rs":"321974136d58e649e60b2a3f70a369dce2d49f474f79579f8e0d66eb63d2d634","src/bindings/swift/gen_swift/miscellany.rs":"7fc2444596d76545ad82ee6c4bed64a29dd4a0438d50bfaafe511f41f6a0e409","src/bindings/swift/gen_swift/mod.rs":"41e4bf2fbe622d0dba85363455e287e00dc48e4043910cef35c30ce170acf52b","src/bindings/swift/gen_swift/object.rs":"2269f65a6b58a24bd08fedb133a38b37663bcf11d0586c50a67028022706a156","src/bindings/swift/gen_swift/primitives.rs":"c8346601008ac6a6d07f08ec7395182c45a4d86c163dc1d6d9c326c49f2acda1","src/bindings/swift/gen_swift/record.rs":"5ad98ab04a5d8178daf0956db819c87d26aae7bf968184e88d512e34c02feb90","src/bindings/swift/mod.rs":"26ba270cb7913661f3cee703038d1ea4a70bff64c3b31351d6bc77e67cdee20d","src/bindings/swift/templates/Async.swift":"84b9be2b5eca2dcfad7eee0cb8d34fec613a4bfdc8a7170b8d11575e457f567b","src/bindings/swift/templates/BooleanHelper.swift":"f02e391bed44ca0e03c66c4e6a1545caaae490fc72c4cf5935e66220082a8c30","src/bindings/swift/templates/BridgingHeaderTemplate.h":"3f468869e77b9293836822b8f2ac348716ab5d487f7b8fef1a87ec30ddafa8d5","src/bindings/swift/templates/CallbackInterfaceRuntime.swift":"2c71ac715ad0bca6f73559748453ba37ca242c90de38f76876989be05d21c49b","src/bindings/swift/templates/CallbackInterfaceTemplate.swift":"790f50b49d5b07dd44e8b215ed1fff02991c1f65aba9a9a9925275a544348813","src/bindings/swift/templates/CustomType.swift":"71520eb38a4be9035dca9e3e0402f386e7eaa79b28568bbc2f20d3fd53b4544d","src/bindings/swift/templates/DataHelper.swift":"df11547a2df57dcca0ff9cddc691bb5fa07d5ffd3d328d1c3b4443078008b111","src/bindings/swift/templates/DurationHelper.swift":"cbc41aaa58bda6c2313ede36a9f656a01a28f9c72aa1624e0e1c1da7b841ffb6","src/bindings/swift/templates/EnumTemplate.swift":"fe205dd28defea8ed6126a45b2a95240a920dfebda8927134a50c3b6d0d7e9d7","src/bindings/swift/templates/ErrorTemplate.swift":"3dddb278763b75b38294c1165522fa91078a951ce05c91fbdfde43b5a097f34f","src/bindings/swift/templates/Float32Helper.swift":"ea32538058c4b3c72b1cd2530ac00d0349fadab5e1bc617d33aae4c87692fc98","src/bindings/swift/templates/Float64Helper.swift":"e27e9424dc6e97b8cacc6ca4c796dd2d16dcfcb877e2f19c45eca03381a41e78","src/bindings/swift/templates/ForeignExecutorTemplate.swift":"205933825e691fec525286d263ea34d592cc462257764ee76325bf98cb3cd240","src/bindings/swift/templates/Helpers.swift":"a88fd909787b855998671e551cdb3284109e2fd2b2e7492b1c93c82aad0e9d35","src/bindings/swift/templates/Int16Helper.swift":"204906911813a3931436057c23032f4c4e39e023df90d641d6c6086aefe2f820","src/bindings/swift/templates/Int32Helper.swift":"0997f059c9c4edd3c41aee0bbad4aa2bda6d791a0d623ad8014d5aa6bdae718d","src/bindings/swift/templates/Int64Helper.swift":"bcf8c2deebb3ee9bce87735adc4bd100981989943b69f6a7fb499a9aec4c25d9","src/bindings/swift/templates/Int8Helper.swift":"ad1ec0fa213724933fa4dc4e2e304e13ac4722b774bfffac44793986b997dd33","src/bindings/swift/templates/MapTemplate.swift":"53971ec388417b02519f8deb8d66361ab4693eae77d116f6051cbea4738054ec","src/bindings/swift/templates/ModuleMapTemplate.modulemap":"99ad1e9bf550a21497296f9248ecd4385dd6d0b5892951d24cf990cdbf3eec2c","src/bindings/swift/templates/ObjectTemplate.swift":"4633572ac6d27a0f82f3b125c2136ad4fa126391c0b64b5db1bde4b04a77f807","src/bindings/swift/templates/OptionalTemplate.swift":"2376487ceadca3975f0e82ddf6ce61af8bbbf5b0592fa9cd977460f148d8c99d","src/bindings/swift/templates/RecordTemplate.swift":"16e0b98354b624a8922d7d384a005fa660a6a388d70381872ebbcd0de9fb78a4","src/bindings/swift/templates/RustBufferTemplate.swift":"f4422fdf0cb5b4db267d461f063dedc319ea1a5a13bae1b82c3f108ba8c658bb","src/bindings/swift/templates/SequenceTemplate.swift":"8425b279197582a94b4cf363ab0463907a68a624e24045720ef7df2bcacf3383","src/bindings/swift/templates/StringHelper.swift":"968b9b9b7fbe06a2ac2143016edaff3552e201652accb8d613b03645f0d24a90","src/bindings/swift/templates/TimestampHelper.swift":"82eece13aa186c8e3745c8ad2f1290639ca9689573018a2bdc5c75afbae58c26","src/bindings/swift/templates/TopLevelFunctionTemplate.swift":"ffe0287861e67dcad2be77f30c00c0a326ab59ffbd37409de3bafc969d49df26","src/bindings/swift/templates/Types.swift":"98c654bfc5d2d4ece965cfe15b00e7151b815e26bfb55abf17e799efffccc2c0","src/bindings/swift/templates/UInt16Helper.swift":"d6fba577324fc0e9e50329c968df99341de418011be126bd29702f8a94d87c02","src/bindings/swift/templates/UInt32Helper.swift":"5e0cf151a0c40098b3d96815ba3de23e15fe52f3c517577e1b0c7e7a4c12428f","src/bindings/swift/templates/UInt64Helper.swift":"17237b38d09ced8d2a8ff1ad9ca86873a19e417280e0e60f33d7063397ea4b7b","src/bindings/swift/templates/UInt8Helper.swift":"c4cb2ee4a78b54ca8d0013383c6b43e9ecd42776e3dc7d6e40086325d41714e5","src/bindings/swift/templates/macros.swift":"438091831b355b0ba6726dab7c17c0687ca58854c7799e946a8012e95a42f4ba","src/bindings/swift/templates/wrapper.swift":"d83a1b8ac3ffc761d4e560adae57d5ad275e0fd1bf3fdc35f3b3b3699317c6e6","src/bindings/swift/test.rs":"c15d19e7f324613e2dbd7995dffba875ed430919a0882f05e8e1f6cc8aea613c","src/interface/callbacks.rs":"34384f1a4e89cd30e2c35beab2bbd8cc0a9e3dd21ed0b390859610fbe209b16a","src/interface/enum_.rs":"3a4f9e77d80128444a8a226ef1e5ad7cd339f8d815aa7012554cf94c2e4edd98","src/interface/ffi.rs":"fa23a2e6fcd89d956523bd0aa5c946138ac629752f0be9085e09f78aade75fac","src/interface/function.rs":"835ee542cb19c67fb95502a790771e54deb712272baed8bdc3f6bfabd9c69d6f","src/interface/mod.rs":"5bd3b5a2dd91382089323a21d4d0419dd5621799add4786a4710df0afebcca69","src/interface/object.rs":"2364c4e7f887d6e4256d065f9ad049ffad19f92fe1c1d76789c33a22376bdd7f","src/interface/record.rs":"931dbaa8cb440debfcf14d51be3e6c517ba2c28655033ad2486e384a9bea25c4","src/interface/universe.rs":"66deaa33394e401e3005670731a0685068302fc01640655335a7cbe9fb4cf48e","src/lib.rs":"64a6239dbaa8196089600e7b0c577b43eefc4a9935893292298bf400fd77a03d","src/library_mode.rs":"89c9fc47db09ccb779fd2842d3c7e07b7863164bb5585eeaf959490ca5555438","src/macro_metadata/ci.rs":"07482b87bf5912277d114b24ad3e560cac94ca0087bd6ee9bee2c328d992bb18","src/macro_metadata/extract.rs":"a106a5b6ad8e5deba474646162b83ec4065796c58c60fc13dfbf1a72ed713833","src/macro_metadata/mod.rs":"bcb5e9a015510e9d74c288da928a4bfb8d80926a8ff85227c0eae8cdb2605519","src/scaffolding/mod.rs":"65749e72181c63edae55d49493aa49a1efece93c0a27deda230f5de4b9ba7d60","src/scaffolding/templates/CallbackInterfaceTemplate.rs":"0581f257c0eb7f0be922ef3ebf7892aafad15e9dc7865e53fc9396375f3188fb","src/scaffolding/templates/Checksums.rs":"ee926e840875c2e48e1d0cc5185c11f7a1ed3bde5264b07540812cb13c1d7481","src/scaffolding/templates/EnumTemplate.rs":"350cdcb4f23be6e6e2b442e0d4ea65549bb35a407bef1ac745a37a2e2b527fae","src/scaffolding/templates/ErrorTemplate.rs":"b93e8bc08d818fbf8976b766635ae192042b78c85f8086d4ee41ae03ab7f3b6f","src/scaffolding/templates/ExternalTypesTemplate.rs":"6cf8a89d9e6f1b6f5af4bc86e49454323fa4981846ec76d6661fcff41c348a8e","src/scaffolding/templates/ObjectTemplate.rs":"df614156bee529fd28905a6ad2270685e51736eade6af856edba5de6e9484141","src/scaffolding/templates/RecordTemplate.rs":"5b0f351739d5770f874fb7da56700c557df1d73ac219f3b18c4212d26fc422e0","src/scaffolding/templates/ReexportUniFFIScaffolding.rs":"aa8a1ffa98b6033707d965f90b5709474ed6bc79486fb47dacae8417fc056cf8","src/scaffolding/templates/TopLevelFunctionTemplate.rs":"96b99b38d074492673797737ddac0683c803a3908e03cc9b999d16f7a76ed178","src/scaffolding/templates/UdlMetadata.rs":"d7c50af1de92ef85630b385a910c7b29875502d622eb90da5541a7012b93d9e2","src/scaffolding/templates/macros.rs":"ea6bacd8dd9116ad739bdafe893d70407050f35e4a7ac8dd2c78b8ef34263e8e","src/scaffolding/templates/scaffolding_template.rs":"c8e18306a73ec5b764f665660fc5c91d498b63b6c3f489e524b2bae50f81f231"},"package":"fd992f2929a053829d5875af1eff2ee3d7a7001cb3b9a46cc7895f2caede6940"} \ No newline at end of file
+{"files":{"Cargo.toml":"037dacd80bb367cfc530c5ca19fbfac091f385cf88ad5bd33c2009fde6d06e3e","README.md":"37c1af00ec81a9f1bc206ab3578356e5f9ad4077dc46dd1bb623d81d804948b8","askama.toml":"1a245b7803adca782837e125c49100147d2de0d5a1c949ff95e91af1701f6058","src/backend/config.rs":"4861dbf251dbb10beb1ed7e3eea7d79499a0de1cd9ce9ee8381a0e729c097dea","src/backend/filters.rs":"8a818952896f9c5f438d6705bb28f56e77fbfce6d9757a2d74e1b3a925ed36e1","src/backend/mod.rs":"2ee9d974cd259f7fb156028b4f4f7601691e94fb5692a6daf0d362df3ecf79a8","src/backend/types.rs":"598df3a861f5d53b2c710848943f6049dd43cb4f37aa81f2c08fd36fc5b2f5d5","src/bindings/kotlin/gen_kotlin/callback_interface.rs":"741100c2b4b484583d34408b276394078a24918c47101fbaa6233df9c4da32f2","src/bindings/kotlin/gen_kotlin/compounds.rs":"b40d1ab8c70d7da458ff45d2ce58efb6cc3b24bf560c093cbec7d0854d461dc4","src/bindings/kotlin/gen_kotlin/custom.rs":"7e619f7320796ecd8c4ced82904b4bd3c6a0043b55d5829274ab3040cdf9cd7f","src/bindings/kotlin/gen_kotlin/enum_.rs":"6559bb00d8e359126b016e549263c0c9bc1dfc5654ed662c0c2912b47931b1e4","src/bindings/kotlin/gen_kotlin/external.rs":"38f42be67105b9a2ca5ecefec959e6659af728bedb9d107a51c595fe6ff5d332","src/bindings/kotlin/gen_kotlin/miscellany.rs":"6541987e627c4ff27a17ebe919b2b5cd97cb66ff41187ed636396b4e35ea2414","src/bindings/kotlin/gen_kotlin/mod.rs":"34328d11c59a67159620a21bc660fae149bbf452a817d6c9d3f7657cc5b79134","src/bindings/kotlin/gen_kotlin/object.rs":"1cb8d1f5eaf06ceaadb6d2cedca482fdd1502c24500ba270d8fcac48ef2f1231","src/bindings/kotlin/gen_kotlin/primitives.rs":"249896ec7d18f0f8d1d5dc8dc66ea6f3d0cc7b13344ab6892fb985f339d99b9f","src/bindings/kotlin/gen_kotlin/record.rs":"96fd1a180095a062b4a9b71d4f603b232f0133f46355a3e427c4064701d900f2","src/bindings/kotlin/gen_kotlin/variant.rs":"d111d6888745195fc2c24bdddc57359e771616102a8d182c5c8ad268b0a13460","src/bindings/kotlin/mod.rs":"ef88eb9b5b7d6f920c62a525ea4d4bf2a3b1a9154afaa012cdb2feea597fbf23","src/bindings/kotlin/templates/Async.kt":"2130ef176ffabe85cc737059203f2bda38df1f22f2398aa338c9d3099e6d46e2","src/bindings/kotlin/templates/BooleanHelper.kt":"50d8a5109e2d2676f25a02772079efbaac61776a76e3e84eebd1fb13294842de","src/bindings/kotlin/templates/ByteArrayHelper.kt":"dc4aafffacb1fa8f3b4e15f714c13b8d715eec178c63bdba6260baf612dd80d8","src/bindings/kotlin/templates/CallbackInterfaceImpl.kt":"be4d5a5d3ed4f7b1d4c9822905c5732bdb8593c3dbf8d4aabab62291d7ccec99","src/bindings/kotlin/templates/CallbackInterfaceRuntime.kt":"76689c1bfa8aa7dc6e2c9e77c42212b9f317763fb35cd7704ca470675dd2648d","src/bindings/kotlin/templates/CallbackInterfaceTemplate.kt":"b497250899bfd0c79bd01d77f23454b12b108fa269055d6f3699be74fb93d015","src/bindings/kotlin/templates/CustomTypeTemplate.kt":"d42eba4334c39749037d14ef9e2219a2e515479c18905df3f49534424317c848","src/bindings/kotlin/templates/DurationHelper.kt":"dfb45fe1b47bc04dd8c70cb98531c40606eca554791132ee6bed2846f8ee099c","src/bindings/kotlin/templates/EnumTemplate.kt":"7cefbb1e29d4e89420f6a95275bbab891984a56978cf4852a1e52bcc82afd9e8","src/bindings/kotlin/templates/ErrorTemplate.kt":"8f41de90753a42cfe33ba837997baa2954208b987e70cec13ecb4124faf25aa6","src/bindings/kotlin/templates/ExternalTypeTemplate.kt":"b1df8566d000431bfc3820a2e455426e810cba6d8683e77ab78ab0bb7d003720","src/bindings/kotlin/templates/FfiConverterTemplate.kt":"bc0bdbc99ee2459f50c84abf6c1bb236a6179eaf519f1c5f5b3f72d8184dc662","src/bindings/kotlin/templates/Float32Helper.kt":"789246343d34594fc39072c1a5393b848cecadb353659fc6e9080fc7e760fc21","src/bindings/kotlin/templates/Float64Helper.kt":"b87eac72da313b1d559b1738bba1c771f43bb7566fdbb3a34546dd56beeb5832","src/bindings/kotlin/templates/HandleMap.kt":"feb456ea4dfb2ad07331d49d606faf396737817c6f6712a2d9a9d843daebdc1e","src/bindings/kotlin/templates/Helpers.kt":"e7657732f44e8092d492ef291e3ce11aa803531d82be99645b8513c00dca99ac","src/bindings/kotlin/templates/Int16Helper.kt":"54bb1aefbcae1c3c10e0cdc6a9d45e070e3ca57c9ffa53b2d65e5c59808c9743","src/bindings/kotlin/templates/Int32Helper.kt":"49b3e5274d5c7853d227ec986d0a0c71621a448391b2c9aaf4351b86f310dadf","src/bindings/kotlin/templates/Int64Helper.kt":"264fd99a4109f0b2c40b806fc1a0181f27331346a02d94e398f1e34110e3414b","src/bindings/kotlin/templates/Int8Helper.kt":"a50c315d8474a212914f10f43ca7e75061bb73067e0de686b182891c6c7f1bc3","src/bindings/kotlin/templates/Interface.kt":"c66912069c3f61848bbee3d1886abbfa74895f759853f6b4a3c62cef5976766c","src/bindings/kotlin/templates/MapTemplate.kt":"f7e0360d3be74e543573bd56925bf25c6c22e6203aecc1cf519464704eeeb0ee","src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt":"db4c30cfcb709c5413892cf3cf69391ba36c6160543274e8d1f2bedf9001d058","src/bindings/kotlin/templates/ObjectCleanerHelper.kt":"9ebcfcb3fe7788e93cf8cba30fd7470b363719e9ed25cdde43c95aebe1b90c2a","src/bindings/kotlin/templates/ObjectCleanerHelperAndroid.kt":"049e1e32a23b7923393e3dcabce49532737d44e9dbb331f62984ada67bde3125","src/bindings/kotlin/templates/ObjectCleanerHelperJvm.kt":"b6287f72afdb0ab9af5e56136c28e6a4f5e18a50305bce8923ee061b9406cfc3","src/bindings/kotlin/templates/ObjectTemplate.kt":"6a776feb36b0379c43e0013a26ba85cdef385aa1e59b4c2efa7a794140aa99bb","src/bindings/kotlin/templates/OptionalTemplate.kt":"918f2029e60710f4b048a77830b12b388c917af1a488c9f05f38323c58ee0f9e","src/bindings/kotlin/templates/README.md":"83587ff54a31fa47d2c0849cb5db52d6f079551e1cfb73c76c6dd02a7b164ad9","src/bindings/kotlin/templates/RecordTemplate.kt":"677bb63ae4fae9117e9c77928370a8911ab959c6b884c6af2ba4efc686c52721","src/bindings/kotlin/templates/RustBufferTemplate.kt":"b3b78b2c41cbfff6262d758f9ebe064e76d20841ec4db7705142449f7ffc75a9","src/bindings/kotlin/templates/SequenceTemplate.kt":"c1aa28ca87528c97c62656f850205023c2eb8d218264ef7b1e70207ab4f1b9b6","src/bindings/kotlin/templates/StringHelper.kt":"4e942e36af05dc823d5f28ab336c55ff86bf0f95bbb748399bcaa8ad291c7032","src/bindings/kotlin/templates/TimestampHelper.kt":"70137e78de18796996889e8d648f1e90830aa652a93a0b4639ff9f7ccb967a25","src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt":"68c714cc8c7fa244166c5902a59c90317cdfc402193624cade405e3454f9bf67","src/bindings/kotlin/templates/Types.kt":"c725f7e57eda5b2d52c3c92b24f93d9321591e72c89d16973163b3b8d713b85b","src/bindings/kotlin/templates/UInt16Helper.kt":"ee96270f426933cfcd914894d4c7895544f7e3d4a7c24be78afc2896b46cbcbe","src/bindings/kotlin/templates/UInt32Helper.kt":"b2d7543098277e7b92502a0a6693dc25dd42e360f776b19987a48dd7fc6db7fd","src/bindings/kotlin/templates/UInt64Helper.kt":"fc855eb78a4b50d76fc53509dae8218c48a221db5bf73cf5368d755fb9aae478","src/bindings/kotlin/templates/UInt8Helper.kt":"af22d9e6f99fe9d8d7d5175cb03f7a9f62628c9dd939dbbfb5a4085359e52e0e","src/bindings/kotlin/templates/macros.kt":"0a221962503f6977b129eb3c1e3772e3e9d51cbab6d813c55b0387c24d784184","src/bindings/kotlin/templates/wrapper.kt":"a02028a86c620679602f26714c7feb4a306867cda1cba8240ca6e83d99cebd91","src/bindings/kotlin/test.rs":"28bf88a9e9aa9510adbe78005a2027a62818433f49426172046dc83a3ad41911","src/bindings/mod.rs":"949f323d6eb5c018497103dbb9dcffb8f395eb5960694b551a24b4887e853afb","src/bindings/python/gen_python/callback_interface.rs":"5df3e091d3c88ef7645e570f693942161a9b9c6307419c15a2534fbc5da974af","src/bindings/python/gen_python/compounds.rs":"4a83b02e11ae969ab360ba61df44d91dc790f372b5960b350b0b019a57d19de4","src/bindings/python/gen_python/custom.rs":"81501641648eb638f5a338c01a71db0d0e96601c3dda83acdb2d49072b387d42","src/bindings/python/gen_python/enum_.rs":"7c3f8f6a97c1491175c8b93b8f9ab13748e2f8084bb717836b6935d024805439","src/bindings/python/gen_python/error.rs":"161bd2e041e3a63a91899de173eec8450cc10e1e9552d064969aa72a02fdfd5e","src/bindings/python/gen_python/external.rs":"d7101124c22dd7837e227a7f1b683c57b92229a2cd5b25b06740f2fe3d76bed5","src/bindings/python/gen_python/miscellany.rs":"d6f6305dd0af85b7ba87b70cbe6ecba00c83d5082c5bdcaf25962fff853973ea","src/bindings/python/gen_python/mod.rs":"b8aac9a146551cd660f1cd310f8ef02e3bd4a11540a087551bbb7c7706b99e16","src/bindings/python/gen_python/object.rs":"a4d4c20a0a52687feff2b9a547a13aa9bda09b3af9ec26508646658a88eec8b3","src/bindings/python/gen_python/primitives.rs":"b830c68e20d8539b8ac5566f1ca0dd262c1b14712a747f79e70004cd8f409ba1","src/bindings/python/gen_python/record.rs":"f8e12ce43d7e0f37f05420a849e7867b7251f9790933609a4cb99050fd063089","src/bindings/python/mod.rs":"eac32ce383460d58d3ccf1d406173465fc8a1db8a24408df67620b7d14dcd0cd","src/bindings/python/templates/Async.py":"f1cf32d8e28b5e2fcbad6ccd00d03fd49f4b54eac47adcfe23cbf786d523eee7","src/bindings/python/templates/BooleanHelper.py":"cf7bcd414197258b0cfa54c6ad2aeb81a1a6a4a45af5b6aaf6f8e484bc5af59d","src/bindings/python/templates/BytesHelper.py":"8c39cf1760678316cf2b3903632f2bacae4f8aaa961b37eeb03e06e9f07241d1","src/bindings/python/templates/CallbackInterfaceImpl.py":"7dbb049ffebc3565ffb4605d53843c69f782f3e86472e060e3194be4986d328b","src/bindings/python/templates/CallbackInterfaceRuntime.py":"54dbda8a6ffe284ef2045da290a69d37974fed672eb57309c9fc7ac665969397","src/bindings/python/templates/CallbackInterfaceTemplate.py":"ef235bd7927592eb19a2db422352a435b7466595ef31e4822a16c3caa24cdda6","src/bindings/python/templates/CustomType.py":"4647a60dbe63ead2b23d07cf3a3a4a190a219d81357532364fd4afdf990d6e1b","src/bindings/python/templates/DurationHelper.py":"eb9278b546f79b71525ae61a5b30bfe4a1260fd2268c87c600d157bf9b0e2a44","src/bindings/python/templates/EnumTemplate.py":"49903d969b8b160d8f1a0747c803d5f54a6f000a6781493eacad1f6ca7811d7a","src/bindings/python/templates/ErrorTemplate.py":"d7af297596e5ef894e3fdaeb92bd6446843c987f8283c77973bd10fce537c9c1","src/bindings/python/templates/ExternalTemplate.py":"0cd36fc89f0a587dadfe0cb89c4d45a641822ba07cb9410299bdcd73ad3edb79","src/bindings/python/templates/Float32Helper.py":"4aa522163f121fcb84d2f024774d8dd9321c31f09b9a95da3a3131b6d2756971","src/bindings/python/templates/Float64Helper.py":"e7fa247fd9c3907b818f0d1ba28c2cee897e75fdd07fdacad1b8a2b5c26ba418","src/bindings/python/templates/HandleMap.py":"9dbfdcb4ddde5927fd9b9fb26b5194bc16b1d2280c2259895fd0ea443af4afd6","src/bindings/python/templates/Helpers.py":"09ddd46d6fcc6ee7e9b1c123b0830426c967f94e22ab18b3ee248b873f7d5ebc","src/bindings/python/templates/Int16Helper.py":"613345b35e63e7284caf97de9630747ec9cdadc8dd3f8451d2e878cb762958f5","src/bindings/python/templates/Int32Helper.py":"758b093b66dc0a8d3f0b13b9388d21f47de31b5e948689041c4d43ef98cf2c4f","src/bindings/python/templates/Int64Helper.py":"c7e76441ee14e78e856f8819f73243bc04b33ec16083ae7390e0ec27141855f2","src/bindings/python/templates/Int8Helper.py":"d963a76b218a32ea2b3bb26f265dbbc47e859b7d1bc939b43fd9b93c51a62292","src/bindings/python/templates/MapTemplate.py":"9dc81ebced353d0137ef6fe3187e170e3e72d32a3b5520dbbcc1f95354ebf62d","src/bindings/python/templates/NamespaceLibraryTemplate.py":"e480a80a27ed5e54a3ff9c72d3d6ab13343764da6c413d813c4bd72429139193","src/bindings/python/templates/ObjectTemplate.py":"976aa726baf36b53d1c319b262c34b8b2de2e414cb8d3c645ed3bf006833b9e8","src/bindings/python/templates/OptionalTemplate.py":"2629f3b46ff394df620bbff1699935e6844d9aa017e74ac43c0b38acd05f8d42","src/bindings/python/templates/Protocol.py":"8446fe51d7c9d16d7086694cee8016c6f571dc5c930fd18848fadcf109aa0566","src/bindings/python/templates/RecordTemplate.py":"c99d10cc061af339349bb0c7e8b67223fdcd9064362badc137a2ad0df17c57c0","src/bindings/python/templates/RustBufferHelper.py":"a48e5ed1dcde19993ae50bec9b881afa3bc6dd5f7d8257fd60214f2100224929","src/bindings/python/templates/RustBufferTemplate.py":"017f31fd5075306f5c8c2bd0e3a21ea965c694c0daf2523187ab076fc786e9ca","src/bindings/python/templates/SequenceTemplate.py":"1b262e5f546a1923de6968e0233cc621a5fae16062e9e6ac874c9b62d8f145df","src/bindings/python/templates/StringHelper.py":"b303b7fcbbc0981a28c6a7d0cc5bd90f8e9c8b8d572792e217a324b2bdb95dbd","src/bindings/python/templates/TimestampHelper.py":"b3da14de54822f44ada4459355c842550b944b3cd2a85a4eac0f59e82d646877","src/bindings/python/templates/TopLevelFunctionTemplate.py":"1d9da1b6ca2175b30f3277a46a1749590490e82bee6b990ff35efb04e5f102ef","src/bindings/python/templates/Types.py":"3653e2cf773493c6ddfd13ef298b0c7cb33fabc1dba495fca64b9287aae03042","src/bindings/python/templates/UInt16Helper.py":"8ffe4b69a5d4a2b3c5677ff1d8954efc67ab67713ffe297380e930e0379d493d","src/bindings/python/templates/UInt32Helper.py":"83f9603aceae05f2134c7183313ab0a1a8f64cabd8070ae19557494fe41dd6d2","src/bindings/python/templates/UInt64Helper.py":"97269025377a256e821e57991b07e17af05f4d1c4228e01fe5f243d784cb509d","src/bindings/python/templates/UInt8Helper.py":"4896723ed0ab8f5aef4a58d599e0a0dbd63d373f5740821c21b4b429b6a7afda","src/bindings/python/templates/macros.py":"d766feb4dedd2d0e4cd2052da7a69c0b074b97f880b857ee457faa43975230ed","src/bindings/python/templates/wrapper.py":"ab05168e3d01d1a26e9589cd9855d7776c46c59d699f1402a29dfae6eb9ebfbc","src/bindings/python/test.rs":"69d3ee230820f38d743438c8212e1bfc4e92f948d9e73548a38c093e164b2759","src/bindings/ruby/gen_ruby/mod.rs":"861be105f9001d4ad8f7b8ac4a303a95459ec7de7a0c2fdac14a083c43d5a07c","src/bindings/ruby/gen_ruby/tests.rs":"7dcb86b08e643c43503f4cac6396833497f6988b004321c0067700ee29ffbf32","src/bindings/ruby/mod.rs":"0fdfab5306dc5c05fbcbfb273340d96ad70c5caf5074834ad6851af1a9a56734","src/bindings/ruby/templates/EnumTemplate.rb":"5480edb347f5829e478d19474691babd72f37616ed846d519b5a61cb1d6cf047","src/bindings/ruby/templates/ErrorTemplate.rb":"301c177e639f0a27f19d4935c5317e672aadecbee2f9bfa778df982320f5148d","src/bindings/ruby/templates/Helpers.rb":"ce7ed4be97dad507b991c69c28dc7bb6427e5e79a4b2fba9dad9dccabc3e090c","src/bindings/ruby/templates/NamespaceLibraryTemplate.rb":"9a24c427b9eba99d9e13181a5559a385b5d1d16beae2b72a402f2645b22a9048","src/bindings/ruby/templates/ObjectTemplate.rb":"a1c0cc38865195d61df3540284f4756f1b6406b205d74e3855e7089d763b2791","src/bindings/ruby/templates/RecordTemplate.rb":"343a4b159cf298045747fb48f17552e3bf2c9775fa5b4fa40b424976dc67e33a","src/bindings/ruby/templates/RustBufferBuilder.rb":"a36d9183f3e66cbbb1c3e584b78ab86e01bd6b89a4a5ef9614c5df24dc383acc","src/bindings/ruby/templates/RustBufferStream.rb":"ab4fc736906e320fca56dca280daf40138ba443d957c42fbf5cfbf1c6acf463a","src/bindings/ruby/templates/RustBufferTemplate.rb":"de577fbae811f72e260270656f2c12ad7a4d157c78f97898d0cd4e309d92ad6a","src/bindings/ruby/templates/TopLevelFunctionTemplate.rb":"26c9c2d53853792270795bd822e41968e995375478d246f808f9935af77a7d6a","src/bindings/ruby/templates/macros.rb":"dc60ed79844b04fe828a24aef3550a6b6c30f7c0b66f03608d7c56725287ceed","src/bindings/ruby/templates/wrapper.rb":"f82b41543546f8e5804cd0e1785f4735d9dd95383566d0e5ba1cd4d9e8c0578d","src/bindings/ruby/test.rs":"027d62085498b20977f025117e1fb7c30923a189961d679823f16ca62a575d0d","src/bindings/swift/gen_swift/callback_interface.rs":"1a2b56d16db841574be0762d66b57fbaef0519273d45c47ca687bf656546f201","src/bindings/swift/gen_swift/compounds.rs":"d62206bdab8a2a65b19342933efad54c171f0f8c217b82ee8b41617043662fe5","src/bindings/swift/gen_swift/custom.rs":"bddb601b4ea8810ecbad01271d5ec0b3958999b09bc9382c83637dfd43451734","src/bindings/swift/gen_swift/enum_.rs":"87be67ec3394616368d9ef8e99b7f234c053b3bee9a7f9e6f2dff37f147c8837","src/bindings/swift/gen_swift/external.rs":"a1d34b688679a74b0ddcfcb1147a7064b53883d9df9c0670f950078516492ee7","src/bindings/swift/gen_swift/miscellany.rs":"7fc2444596d76545ad82ee6c4bed64a29dd4a0438d50bfaafe511f41f6a0e409","src/bindings/swift/gen_swift/mod.rs":"e6c12506217d0a5479e946998a24ee984e4ea4c4f19334cbd014f53504300181","src/bindings/swift/gen_swift/object.rs":"45a6d6bb053f3ef397ab8c6feba8d0e126a8d14cd87597d25015f97c6ffc3417","src/bindings/swift/gen_swift/primitives.rs":"26a29ea764988d9e021bbac6505ef45e49ae42426522d6e3822e949b6f0b589c","src/bindings/swift/gen_swift/record.rs":"5ad98ab04a5d8178daf0956db819c87d26aae7bf968184e88d512e34c02feb90","src/bindings/swift/mod.rs":"26ba270cb7913661f3cee703038d1ea4a70bff64c3b31351d6bc77e67cdee20d","src/bindings/swift/templates/Async.swift":"1645ac8dbea8575dec05acf0aeb18e210f76231c36ea0178b183e02a3ff6e18f","src/bindings/swift/templates/BooleanHelper.swift":"f02e391bed44ca0e03c66c4e6a1545caaae490fc72c4cf5935e66220082a8c30","src/bindings/swift/templates/BridgingHeaderTemplate.h":"4e1e91859c4fc6f40db32648645f046fb7e71841f44ae84737ea85bdecff7fa3","src/bindings/swift/templates/CallbackInterfaceImpl.swift":"514a0932c445e4040460da2969e4f21595e17b9b960eb23c6d1526e47dd56c51","src/bindings/swift/templates/CallbackInterfaceRuntime.swift":"a5def6b3b41698a42e6ccf5c85d365fe0abc7eff629d9f49d9d396ee90aad3a0","src/bindings/swift/templates/CallbackInterfaceTemplate.swift":"4dcab3e590f897499782aef3c657b9b838b312d8b49a018bf0f1ebde15ada786","src/bindings/swift/templates/CustomType.swift":"71520eb38a4be9035dca9e3e0402f386e7eaa79b28568bbc2f20d3fd53b4544d","src/bindings/swift/templates/DataHelper.swift":"df11547a2df57dcca0ff9cddc691bb5fa07d5ffd3d328d1c3b4443078008b111","src/bindings/swift/templates/DurationHelper.swift":"cbc41aaa58bda6c2313ede36a9f656a01a28f9c72aa1624e0e1c1da7b841ffb6","src/bindings/swift/templates/EnumTemplate.swift":"4b980f8bfe65266d27d561e88c7d79d87f426b35b4b842ef80c5d56841e2f672","src/bindings/swift/templates/ErrorTemplate.swift":"1233d119320a44dbf6099681595dda9bf5dd2a1474af4380b704bff0563c38ef","src/bindings/swift/templates/Float32Helper.swift":"ea32538058c4b3c72b1cd2530ac00d0349fadab5e1bc617d33aae4c87692fc98","src/bindings/swift/templates/Float64Helper.swift":"e27e9424dc6e97b8cacc6ca4c796dd2d16dcfcb877e2f19c45eca03381a41e78","src/bindings/swift/templates/HandleMap.swift":"acd2b06d678e64a573f7b842c7d08b87140ddb5d7146c0bf3401d99999399ec2","src/bindings/swift/templates/Helpers.swift":"491553eb82cdc5c944451a541d4e4655537cccb961f220783459b57b2311ca84","src/bindings/swift/templates/Int16Helper.swift":"204906911813a3931436057c23032f4c4e39e023df90d641d6c6086aefe2f820","src/bindings/swift/templates/Int32Helper.swift":"0997f059c9c4edd3c41aee0bbad4aa2bda6d791a0d623ad8014d5aa6bdae718d","src/bindings/swift/templates/Int64Helper.swift":"bcf8c2deebb3ee9bce87735adc4bd100981989943b69f6a7fb499a9aec4c25d9","src/bindings/swift/templates/Int8Helper.swift":"ad1ec0fa213724933fa4dc4e2e304e13ac4722b774bfffac44793986b997dd33","src/bindings/swift/templates/MapTemplate.swift":"53971ec388417b02519f8deb8d66361ab4693eae77d116f6051cbea4738054ec","src/bindings/swift/templates/ModuleMapTemplate.modulemap":"99ad1e9bf550a21497296f9248ecd4385dd6d0b5892951d24cf990cdbf3eec2c","src/bindings/swift/templates/ObjectTemplate.swift":"37e57815e60900ae48b953fe01e01535d4ab8076f6160fc93c37dd08fdee47a4","src/bindings/swift/templates/OptionalTemplate.swift":"2376487ceadca3975f0e82ddf6ce61af8bbbf5b0592fa9cd977460f148d8c99d","src/bindings/swift/templates/Protocol.swift":"2614b1378cadf14e7617fedd7367c227ac2a774d528acd3a42e44fd0c4f58528","src/bindings/swift/templates/RecordTemplate.swift":"f9f576b72fda9d1e1db34d1765ec6ec8206103a297329720c1c9a1f58ad085b5","src/bindings/swift/templates/RustBufferTemplate.swift":"89ed33846c0cfb220e823a1002238b16f006f3170d8de0dbbf7775d4f8143c31","src/bindings/swift/templates/SequenceTemplate.swift":"8425b279197582a94b4cf363ab0463907a68a624e24045720ef7df2bcacf3383","src/bindings/swift/templates/StringHelper.swift":"968b9b9b7fbe06a2ac2143016edaff3552e201652accb8d613b03645f0d24a90","src/bindings/swift/templates/TimestampHelper.swift":"82eece13aa186c8e3745c8ad2f1290639ca9689573018a2bdc5c75afbae58c26","src/bindings/swift/templates/TopLevelFunctionTemplate.swift":"7aa473a5b12ad7623f61d6c31f6879f269f51d2c4134dd899ce24c7b31ef35f1","src/bindings/swift/templates/Types.swift":"15e255e35e267f2aca49ed5a4fe16ef79520f4261433fd30c5e6c7f637a4d3f6","src/bindings/swift/templates/UInt16Helper.swift":"d6fba577324fc0e9e50329c968df99341de418011be126bd29702f8a94d87c02","src/bindings/swift/templates/UInt32Helper.swift":"5e0cf151a0c40098b3d96815ba3de23e15fe52f3c517577e1b0c7e7a4c12428f","src/bindings/swift/templates/UInt64Helper.swift":"17237b38d09ced8d2a8ff1ad9ca86873a19e417280e0e60f33d7063397ea4b7b","src/bindings/swift/templates/UInt8Helper.swift":"c4cb2ee4a78b54ca8d0013383c6b43e9ecd42776e3dc7d6e40086325d41714e5","src/bindings/swift/templates/macros.swift":"b30ffd93fe2213e13c3b9910bf2404403b4b231d4cd32c81e0f76c3bb4d151b5","src/bindings/swift/templates/wrapper.swift":"e553af470320391d150e6489eac549064689a37e5db6947914ce5609d0128031","src/bindings/swift/test.rs":"f55ba6c05c250093b26ae91404fd9200951462c1cd99e6b2718f7fb4ebcb7fbb","src/interface/callbacks.rs":"4a019376ec8fbaec495a9e3a1d5cb079af65767b6d85bc9f508f92a1e7f5344f","src/interface/enum_.rs":"7baee60e02cc7f751d7a941e877c10a6afaffea626e79897a0e8b17702f13c15","src/interface/ffi.rs":"11b48aaf22fd9cd9eeded30afe950b26cc1c6d8ec6f9385c9e4cd3bdb2881f43","src/interface/function.rs":"be0f9f268e1947381fa235c5a0cf3c1965fd73121172d31f9c130acf539f2ac0","src/interface/mod.rs":"b97b11295b91691e7e6b7b023bba019729ad02f2204bba460c48acf62c5ee363","src/interface/object.rs":"d37d55edc62f52cf7fac4e3b8be1e46557dcbcfa8eb2e5998a91be2c6c062d92","src/interface/record.rs":"d8ddf873c35beaff45ab522bc4cb809c459a7937fd4061dae8c2db0db4c4edb4","src/interface/universe.rs":"76f368ff2b5326c517025a460405d343618bcc9fc9cfb28346313c8f7a335050","src/lib.rs":"2e3adccd5f0a3dacce6e533edfb5640ddee05e4f87ce8144cba859a14af219f6","src/library_mode.rs":"43ee55e4bb8d27dcec8a164961f22de941603d79d4e10c270ba9e7a751d92a1b","src/macro_metadata/ci.rs":"fa87ef42065c821aa89d3fb7938888c035ce6fc03bb3fce575a878c8e49012fb","src/macro_metadata/extract.rs":"7554c7b19b50d40e90bb503320311c2caa3b1e45e4376a9266ce32c0d48afffb","src/macro_metadata/mod.rs":"bcb5e9a015510e9d74c288da928a4bfb8d80926a8ff85227c0eae8cdb2605519","src/scaffolding/mod.rs":"66c3f2d9e81ded234fdd5b34cbb1da334cc271fa6ff3daa41b9acf9ac02f2194","src/scaffolding/templates/CallbackInterfaceTemplate.rs":"11acee064df46f7b5132401ae49c03c77f296bc04065085d6fd5c4ad6b628718","src/scaffolding/templates/Checksums.rs":"ee926e840875c2e48e1d0cc5185c11f7a1ed3bde5264b07540812cb13c1d7481","src/scaffolding/templates/EnumTemplate.rs":"305b8f0e6ec38300f0ae576a1bf1c576d0088d0df8d0b45818ad25f0216a7ac0","src/scaffolding/templates/ErrorTemplate.rs":"e6ec4e1d4c594d9f14a8dfe0a24103a66c0cc91d2129f0e1644775740f85bea1","src/scaffolding/templates/ExternalTypesTemplate.rs":"4c45cefca1774de3f3b650ce3b9a1b1b8fc10c62e0e48e54ac300748c32959f6","src/scaffolding/templates/ObjectTemplate.rs":"80689b74cbb426e6ce8bce77359b122d747b34b48d9a30aef44f93c9aa726fa7","src/scaffolding/templates/RecordTemplate.rs":"644177d86b52bf39c277b4e60a66f594b3fb0454f6b62837f9041297135c09c9","src/scaffolding/templates/ReexportUniFFIScaffolding.rs":"aa8a1ffa98b6033707d965f90b5709474ed6bc79486fb47dacae8417fc056cf8","src/scaffolding/templates/TopLevelFunctionTemplate.rs":"c11a688cafc2e21c3be105533b34c1f73eab55202936f7c8a97191d7e27f26e0","src/scaffolding/templates/UdlMetadata.rs":"d7c50af1de92ef85630b385a910c7b29875502d622eb90da5541a7012b93d9e2","src/scaffolding/templates/macros.rs":"ea6bacd8dd9116ad739bdafe893d70407050f35e4a7ac8dd2c78b8ef34263e8e","src/scaffolding/templates/scaffolding_template.rs":"c8e18306a73ec5b764f665660fc5c91d498b63b6c3f489e524b2bae50f81f231"},"package":"4a77bb514bcd4bf27c9bd404d7c3f2a6a8131b957eba9c22cfeb7751c4278e09"} \ No newline at end of file
diff --git a/third_party/rust/uniffi_bindgen/Cargo.toml b/third_party/rust/uniffi_bindgen/Cargo.toml
index 9469a9cf25..f0c7af8665 100644
--- a/third_party/rust/uniffi_bindgen/Cargo.toml
+++ b/third_party/rust/uniffi_bindgen/Cargo.toml
@@ -12,11 +12,12 @@
[package]
edition = "2021"
name = "uniffi_bindgen"
-version = "0.25.3"
+version = "0.27.1"
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
description = "a multi-language bindings generator for rust (codegen and cli tooling)"
homepage = "https://mozilla.github.io/uniffi-rs"
documentation = "https://mozilla.github.io/uniffi-rs"
+readme = "README.md"
keywords = [
"ffi",
"bindgen",
@@ -54,7 +55,7 @@ version = "2.7.0"
version = "0.3"
[dependencies.goblin]
-version = "0.6"
+version = "0.8"
[dependencies.heck]
version = "0.4"
@@ -67,15 +68,19 @@ version = "1.0"
[dependencies.serde]
version = "1"
+features = ["derive"]
+
+[dependencies.textwrap]
+version = "0.16"
[dependencies.toml]
version = "0.5"
[dependencies.uniffi_meta]
-version = "=0.25.3"
+version = "=0.27.1"
[dependencies.uniffi_testing]
-version = "=0.25.3"
+version = "=0.27.1"
[dependencies.uniffi_udl]
-version = "=0.25.3"
+version = "=0.27.1"
diff --git a/third_party/rust/uniffi_bindgen/README.md b/third_party/rust/uniffi_bindgen/README.md
new file mode 100644
index 0000000000..64ac3486a3
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/README.md
@@ -0,0 +1,81 @@
+# UniFFI - a multi-language bindings generator for Rust
+
+UniFFI is a toolkit for building cross-platform software components in Rust.
+
+For the impatient, see [**the UniFFI user guide**](https://mozilla.github.io/uniffi-rs/)
+or [**the UniFFI examples**](https://github.com/mozilla/uniffi-rs/tree/main/examples#example-uniffi-components).
+
+By writing your core business logic in Rust and describing its interface in an "object model",
+you can use UniFFI to help you:
+
+* Compile your Rust code into a shared library for use on different target platforms.
+* Generate bindings to load and use the library from different target languages.
+
+You can describe your object model in an [interface definition file](https://mozilla.github.io/uniffi-rs/udl_file_spec.html)
+or [by using proc-macros](https://mozilla.github.io/uniffi-rs/proc_macro/index.html).
+
+UniFFI is currently used extensively by Mozilla in Firefox mobile and desktop browsers;
+written once in Rust, auto-generated bindings allow that functionality to be called
+from both Kotlin (for Android apps) and Swift (for iOS apps).
+It also has a growing community of users shipping various cool things to many users.
+
+UniFFI comes with support for **Kotlin**, **Swift**, **Python** and **Ruby** with 3rd party bindings available for **C#** and **Golang**.
+Additional foreign language bindings can be developed externally and we welcome contributions to list them here.
+See [Third-party foreign language bindings](#third-party-foreign-language-bindings).
+
+## User Guide
+
+You can read more about using the tool in [**the UniFFI user guide**](https://mozilla.github.io/uniffi-rs/).
+
+We consider it ready for production use, but UniFFI is a long way from a 1.0 release with lots of internal work still going on.
+We try hard to avoid breaking simple consumers, but more advanced things might break as you upgrade over time.
+
+### Etymology and Pronunciation
+
+ˈjuːnɪfaɪ. Pronounced to rhyme with "unify".
+
+A portmanteau word that also puns with "unify", to signify the joining of one codebase accessed from many languages.
+
+uni - [Latin ūni-, from ūnus, one]
+FFI - [Abbreviation, Foreign Function Interface]
+
+## Alternative tools
+
+Other tools we know of which try and solve a similarly shaped problem are:
+
+* [Diplomat](https://github.com/rust-diplomat/diplomat/) - see our [writeup of
+ the different approach taken by that tool](docs/diplomat-and-macros.md)
+* [Interoptopus](https://github.com/ralfbiedert/interoptopus/)
+
+(Please open a PR if you think other tools should be listed!)
+
+## Third-party foreign language bindings
+
+* [Kotlin Multiplatform support](https://gitlab.com/trixnity/uniffi-kotlin-multiplatform-bindings). The repository contains Kotlin Multiplatform bindings generation for UniFFI, letting you target both JVM and Native.
+* [Go bindings](https://github.com/NordSecurity/uniffi-bindgen-go)
+* [C# bindings](https://github.com/NordSecurity/uniffi-bindgen-cs)
+* [Dart bindings](https://github.com/NiallBunting/uniffi-rs-dart)
+
+### External resources
+
+There are a few third-party resources that make it easier to work with UniFFI:
+
+* [Plugin support for `.udl` files](https://github.com/Lonami/uniffi-dl) for the IDEA platform ([*uniffi-dl* in the JetBrains marketplace](https://plugins.jetbrains.com/plugin/20527-uniffi-dl)). It provides syntax highlighting, code folding, code completion, reference resolution and navigation (among others features) for the [UniFFI Definition Language (UDL)](https://mozilla.github.io/uniffi-rs/).
+* [cargo swift](https://github.com/antoniusnaumann/cargo-swift), a cargo plugin to build a Swift Package from Rust code. It provides an init command for setting up a UniFFI crate and a package command for building a Swift package from Rust code - without the need for additional configuration or build scripts.
+* [Cargo NDK Gradle Plugin](https://github.com/willir/cargo-ndk-android-gradle) allows you to build Rust code using [`cargo-ndk`](https://github.com/bbqsrc/cargo-ndk), which generally makes Android library builds less painful.
+* [`uniffi-starter`](https://github.com/ianthetechie/uniffi-starter) is a minimal project demonstrates a wide range of UniFFI in a complete project in a compact manner. It includes a full Android library build process, an XCFramework generation script, and example Swift package structure.
+
+(Please open a PR if you think other resources should be listed!)
+
+## Contributing
+
+If this tool sounds interesting to you, please help us develop it! You can:
+
+* View the [contributor guidelines](./docs/contributing.md).
+* File or work on [issues](https://github.com/mozilla/uniffi-rs/issues) here in GitHub.
+* Join discussions in the [#uniffi:mozilla.org](https://matrix.to/#/#uniffi:mozilla.org)
+ room on Matrix.
+
+## Code of Conduct
+
+This project is governed by Mozilla's [Community Participation Guidelines](./CODE_OF_CONDUCT.md).
diff --git a/third_party/rust/uniffi_bindgen/src/backend/filters.rs b/third_party/rust/uniffi_bindgen/src/backend/filters.rs
index 0d2da8cab2..f4dde0e420 100644
--- a/third_party/rust/uniffi_bindgen/src/backend/filters.rs
+++ b/third_party/rust/uniffi_bindgen/src/backend/filters.rs
@@ -13,12 +13,12 @@ use std::fmt;
// Need to define an error that implements std::error::Error, which neither String nor
// anyhow::Error do.
#[derive(Debug)]
-struct UniFFIError {
+pub struct UniFFIError {
message: String,
}
impl UniFFIError {
- fn new(message: String) -> Self {
+ pub fn new(message: String) -> Self {
Self { message }
}
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/callback_interface.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/callback_interface.rs
index e20020e87c..ae4bffc973 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/callback_interface.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/callback_interface.rs
@@ -26,6 +26,6 @@ impl CodeType for CallbackInterfaceCodeType {
}
fn initialization_fn(&self) -> Option<String> {
- Some(format!("{}.register", self.ffi_converter_name()))
+ Some(format!("uniffiCallbackInterface{}.register", self.id))
}
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/compounds.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/compounds.rs
index 4329f32f4c..8d075bbedb 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/compounds.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/compounds.rs
@@ -5,55 +5,81 @@
use super::{AsCodeType, CodeType};
use crate::backend::{Literal, Type};
use crate::ComponentInterface;
-use paste::paste;
-fn render_literal(literal: &Literal, inner: &Type, ci: &ComponentInterface) -> String {
- match literal {
- Literal::Null => "null".into(),
- Literal::EmptySequence => "listOf()".into(),
- Literal::EmptyMap => "mapOf()".into(),
+#[derive(Debug)]
+pub struct OptionalCodeType {
+ inner: Type,
+}
- // For optionals
- _ => super::KotlinCodeOracle.find(inner).literal(literal, ci),
+impl OptionalCodeType {
+ pub fn new(inner: Type) -> Self {
+ Self { inner }
+ }
+ fn inner(&self) -> &Type {
+ &self.inner
}
}
-macro_rules! impl_code_type_for_compound {
- ($T:ty, $type_label_pattern:literal, $canonical_name_pattern: literal) => {
- paste! {
- #[derive(Debug)]
- pub struct $T {
- inner: Type,
- }
-
- impl $T {
- pub fn new(inner: Type) -> Self {
- Self { inner }
- }
- fn inner(&self) -> &Type {
- &self.inner
- }
- }
-
- impl CodeType for $T {
- fn type_label(&self, ci: &ComponentInterface) -> String {
- format!($type_label_pattern, super::KotlinCodeOracle.find(self.inner()).type_label(ci))
- }
-
- fn canonical_name(&self) -> String {
- format!($canonical_name_pattern, super::KotlinCodeOracle.find(self.inner()).canonical_name())
- }
-
- fn literal(&self, literal: &Literal, ci: &ComponentInterface) -> String {
- render_literal(literal, self.inner(), ci)
- }
- }
+impl CodeType for OptionalCodeType {
+ fn type_label(&self, ci: &ComponentInterface) -> String {
+ format!(
+ "{}?",
+ super::KotlinCodeOracle.find(self.inner()).type_label(ci)
+ )
+ }
+
+ fn canonical_name(&self) -> String {
+ format!(
+ "Optional{}",
+ super::KotlinCodeOracle.find(self.inner()).canonical_name()
+ )
+ }
+
+ fn literal(&self, literal: &Literal, ci: &ComponentInterface) -> String {
+ match literal {
+ Literal::None => "null".into(),
+ Literal::Some { inner } => super::KotlinCodeOracle.find(&self.inner).literal(inner, ci),
+ _ => panic!("Invalid literal for Optional type: {literal:?}"),
}
}
- }
+}
-impl_code_type_for_compound!(OptionalCodeType, "{}?", "Optional{}");
-impl_code_type_for_compound!(SequenceCodeType, "List<{}>", "Sequence{}");
+#[derive(Debug)]
+pub struct SequenceCodeType {
+ inner: Type,
+}
+
+impl SequenceCodeType {
+ pub fn new(inner: Type) -> Self {
+ Self { inner }
+ }
+ fn inner(&self) -> &Type {
+ &self.inner
+ }
+}
+
+impl CodeType for SequenceCodeType {
+ fn type_label(&self, ci: &ComponentInterface) -> String {
+ format!(
+ "List<{}>",
+ super::KotlinCodeOracle.find(self.inner()).type_label(ci)
+ )
+ }
+
+ fn canonical_name(&self) -> String {
+ format!(
+ "Sequence{}",
+ super::KotlinCodeOracle.find(self.inner()).canonical_name()
+ )
+ }
+
+ fn literal(&self, literal: &Literal, _ci: &ComponentInterface) -> String {
+ match literal {
+ Literal::EmptySequence => "listOf()".into(),
+ _ => panic!("Invalid literal for List type: {literal:?}"),
+ }
+ }
+}
#[derive(Debug)]
pub struct MapCodeType {
@@ -92,7 +118,10 @@ impl CodeType for MapCodeType {
)
}
- fn literal(&self, literal: &Literal, ci: &ComponentInterface) -> String {
- render_literal(literal, &self.value, ci)
+ fn literal(&self, literal: &Literal, _ci: &ComponentInterface) -> String {
+ match literal {
+ Literal::EmptyMap => "mapOf()".into(),
+ _ => panic!("Invalid literal for Map type: {literal:?}"),
+ }
}
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/executor.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/executor.rs
deleted file mode 100644
index 154e12a381..0000000000
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/executor.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use super::CodeType;
-use crate::ComponentInterface;
-
-#[derive(Debug)]
-pub struct ForeignExecutorCodeType;
-
-impl CodeType for ForeignExecutorCodeType {
- fn type_label(&self, _ci: &ComponentInterface) -> String {
- // Kotlin uses a CoroutineScope for ForeignExecutor
- "CoroutineScope".into()
- }
-
- fn canonical_name(&self) -> String {
- "ForeignExecutor".into()
- }
-
- fn initialization_fn(&self) -> Option<String> {
- Some("FfiConverterForeignExecutor.register".into())
- }
-}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/external.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/external.rs
index 3ecf09d47f..d55c78f760 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/external.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/external.rs
@@ -17,8 +17,8 @@ impl ExternalCodeType {
}
impl CodeType for ExternalCodeType {
- fn type_label(&self, _ci: &ComponentInterface) -> String {
- self.name.clone()
+ fn type_label(&self, ci: &ComponentInterface) -> String {
+ super::KotlinCodeOracle.class_name(ci, &self.name)
}
fn canonical_name(&self) -> String {
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs
index 1ed0575a9a..c4fc8e0ed6 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs
@@ -7,20 +7,21 @@ use std::cell::RefCell;
use std::collections::{BTreeSet, HashMap, HashSet};
use std::fmt::Debug;
-use anyhow::{Context, Result};
+use anyhow::{bail, Context, Result};
use askama::Template;
+use camino::Utf8Path;
use heck::{ToLowerCamelCase, ToShoutySnakeCase, ToUpperCamelCase};
use serde::{Deserialize, Serialize};
use crate::backend::TemplateExpression;
+use crate::bindings::kotlin;
use crate::interface::*;
-use crate::BindingsConfig;
+use crate::{BindingGenerator, BindingsConfig};
mod callback_interface;
mod compounds;
mod custom;
mod enum_;
-mod executor;
mod external;
mod miscellany;
mod object;
@@ -28,6 +29,28 @@ mod primitives;
mod record;
mod variant;
+pub struct KotlinBindingGenerator;
+impl BindingGenerator for KotlinBindingGenerator {
+ type Config = Config;
+
+ fn write_bindings(
+ &self,
+ ci: &ComponentInterface,
+ config: &Config,
+ out_dir: &Utf8Path,
+ try_format_code: bool,
+ ) -> Result<()> {
+ kotlin::write_bindings(config, ci, out_dir, try_format_code)
+ }
+
+ fn check_library_path(&self, library_path: &Utf8Path, cdylib_name: Option<&str>) -> Result<()> {
+ if cdylib_name.is_none() {
+ bail!("Generate bindings for Kotlin requires a cdylib, but {library_path} was given");
+ }
+ Ok(())
+ }
+}
+
trait CodeType: Debug {
/// The language specific label used to reference this type. This will be used in
/// method signatures and property declarations.
@@ -73,10 +96,21 @@ trait CodeType: Debug {
pub struct Config {
package_name: Option<String>,
cdylib_name: Option<String>,
+ generate_immutable_records: Option<bool>,
#[serde(default)]
custom_types: HashMap<String, CustomTypeConfig>,
#[serde(default)]
external_packages: HashMap<String, String>,
+ #[serde(default)]
+ android: bool,
+ #[serde(default)]
+ android_cleaner: Option<bool>,
+}
+
+impl Config {
+ pub(crate) fn android_cleaner(&self) -> bool {
+ self.android_cleaner.unwrap_or(self.android)
+ }
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
@@ -103,6 +137,11 @@ impl Config {
"uniffi".into()
}
}
+
+ /// Whether to generate immutable records (`val` instead of `var`)
+ pub fn generate_immutable_records(&self) -> bool {
+ self.generate_immutable_records.unwrap_or(false)
+ }
}
impl BindingsConfig for Config {
@@ -236,7 +275,6 @@ pub struct KotlinWrapper<'a> {
ci: &'a ComponentInterface,
type_helper_code: String,
type_imports: BTreeSet<ImportRequirement>,
- has_async_fns: bool,
}
impl<'a> KotlinWrapper<'a> {
@@ -249,7 +287,6 @@ impl<'a> KotlinWrapper<'a> {
ci,
type_helper_code,
type_imports,
- has_async_fns: ci.has_async_fns(),
}
}
@@ -258,10 +295,6 @@ impl<'a> KotlinWrapper<'a> {
.iter_types()
.map(|t| KotlinCodeOracle.find(t))
.filter_map(|ct| ct.initialization_fn())
- .chain(
- self.has_async_fns
- .then(|| "uniffiRustFutureContinuationCallback.register".into()),
- )
.collect()
}
@@ -301,7 +334,12 @@ impl KotlinCodeOracle {
/// Get the idiomatic Kotlin rendering of a variable name.
fn var_name(&self, nm: &str) -> String {
- format!("`{}`", nm.to_string().to_lower_camel_case())
+ format!("`{}`", self.var_name_raw(nm))
+ }
+
+ /// `var_name` without the backticks. Useful for using in `@Structure.FieldOrder`.
+ pub fn var_name_raw(&self, nm: &str) -> String {
+ nm.to_string().to_lower_camel_case()
}
/// Get the idiomatic Kotlin rendering of an individual enum variant.
@@ -309,14 +347,78 @@ impl KotlinCodeOracle {
nm.to_string().to_shouty_snake_case()
}
- fn ffi_type_label_by_value(ffi_type: &FfiType) -> String {
+ /// Get the idiomatic Kotlin rendering of an FFI callback function name
+ fn ffi_callback_name(&self, nm: &str) -> String {
+ format!("Uniffi{}", nm.to_upper_camel_case())
+ }
+
+ /// Get the idiomatic Kotlin rendering of an FFI struct name
+ fn ffi_struct_name(&self, nm: &str) -> String {
+ format!("Uniffi{}", nm.to_upper_camel_case())
+ }
+
+ fn ffi_type_label_by_value(&self, ffi_type: &FfiType) -> String {
+ match ffi_type {
+ FfiType::RustBuffer(_) => format!("{}.ByValue", self.ffi_type_label(ffi_type)),
+ FfiType::Struct(name) => format!("{}.UniffiByValue", self.ffi_struct_name(name)),
+ _ => self.ffi_type_label(ffi_type),
+ }
+ }
+
+ /// FFI type name to use inside structs
+ ///
+ /// The main requirement here is that all types must have default values or else the struct
+ /// won't work in some JNA contexts.
+ fn ffi_type_label_for_ffi_struct(&self, ffi_type: &FfiType) -> String {
+ match ffi_type {
+ // Make callbacks function pointers nullable. This matches the semantics of a C
+ // function pointer better and allows for `null` as a default value.
+ FfiType::Callback(name) => format!("{}?", self.ffi_callback_name(name)),
+ _ => self.ffi_type_label_by_value(ffi_type),
+ }
+ }
+
+ /// Default values for FFI
+ ///
+ /// This is used to:
+ /// - Set a default return value for error results
+ /// - Set a default for structs, which JNA sometimes requires
+ fn ffi_default_value(&self, ffi_type: &FfiType) -> String {
+ match ffi_type {
+ FfiType::UInt8 | FfiType::Int8 => "0.toByte()".to_owned(),
+ FfiType::UInt16 | FfiType::Int16 => "0.toShort()".to_owned(),
+ FfiType::UInt32 | FfiType::Int32 => "0".to_owned(),
+ FfiType::UInt64 | FfiType::Int64 => "0.toLong()".to_owned(),
+ FfiType::Float32 => "0.0f".to_owned(),
+ FfiType::Float64 => "0.0".to_owned(),
+ FfiType::RustArcPtr(_) => "Pointer.NULL".to_owned(),
+ FfiType::RustBuffer(_) => "RustBuffer.ByValue()".to_owned(),
+ FfiType::Callback(_) => "null".to_owned(),
+ FfiType::RustCallStatus => "UniffiRustCallStatus.ByValue()".to_owned(),
+ _ => unimplemented!("ffi_default_value: {ffi_type:?}"),
+ }
+ }
+
+ fn ffi_type_label_by_reference(&self, ffi_type: &FfiType) -> String {
match ffi_type {
- FfiType::RustBuffer(_) => format!("{}.ByValue", Self::ffi_type_label(ffi_type)),
- _ => Self::ffi_type_label(ffi_type),
+ FfiType::Int8
+ | FfiType::UInt8
+ | FfiType::Int16
+ | FfiType::UInt16
+ | FfiType::Int32
+ | FfiType::UInt32
+ | FfiType::Int64
+ | FfiType::UInt64
+ | FfiType::Float32
+ | FfiType::Float64 => format!("{}ByReference", self.ffi_type_label(ffi_type)),
+ FfiType::RustArcPtr(_) => "PointerByReference".to_owned(),
+ // JNA structs default to ByReference
+ FfiType::RustBuffer(_) | FfiType::Struct(_) => self.ffi_type_label(ffi_type),
+ _ => panic!("{ffi_type:?} by reference is not implemented"),
}
}
- fn ffi_type_label(ffi_type: &FfiType) -> String {
+ fn ffi_type_label(&self, ffi_type: &FfiType) -> String {
match ffi_type {
// Note that unsigned integers in Kotlin are currently experimental, but java.nio.ByteBuffer does not
// support them yet. Thus, we use the signed variants to represent both signed and unsigned
@@ -327,19 +429,35 @@ impl KotlinCodeOracle {
FfiType::Int64 | FfiType::UInt64 => "Long".to_string(),
FfiType::Float32 => "Float".to_string(),
FfiType::Float64 => "Double".to_string(),
+ FfiType::Handle => "Long".to_string(),
FfiType::RustArcPtr(_) => "Pointer".to_string(),
FfiType::RustBuffer(maybe_suffix) => {
format!("RustBuffer{}", maybe_suffix.as_deref().unwrap_or_default())
}
+ FfiType::RustCallStatus => "UniffiRustCallStatus.ByValue".to_string(),
FfiType::ForeignBytes => "ForeignBytes.ByValue".to_string(),
- FfiType::ForeignCallback => "ForeignCallback".to_string(),
- FfiType::ForeignExecutorHandle => "USize".to_string(),
- FfiType::ForeignExecutorCallback => "UniFfiForeignExecutorCallback".to_string(),
- FfiType::RustFutureHandle => "Pointer".to_string(),
- FfiType::RustFutureContinuationCallback => {
- "UniFffiRustFutureContinuationCallbackType".to_string()
- }
- FfiType::RustFutureContinuationData => "USize".to_string(),
+ FfiType::Callback(name) => self.ffi_callback_name(name),
+ FfiType::Struct(name) => self.ffi_struct_name(name),
+ FfiType::Reference(inner) => self.ffi_type_label_by_reference(inner),
+ FfiType::VoidPointer => "Pointer".to_string(),
+ }
+ }
+
+ /// Get the name of the interface and class name for an object.
+ ///
+ /// If we support callback interfaces, the interface name is the object name, and the class name is derived from that.
+ /// Otherwise, the class name is the object name and the interface name is derived from that.
+ ///
+ /// This split determines what types `FfiConverter.lower()` inputs. If we support callback
+ /// interfaces, `lower` must lower anything that implements the interface. If not, then lower
+ /// only lowers the concrete class.
+ fn object_names(&self, ci: &ComponentInterface, obj: &Object) -> (String, String) {
+ let class_name = self.class_name(ci, obj.name());
+ if obj.has_callback_interface() {
+ let impl_name = format!("{class_name}Impl");
+ (class_name, impl_name)
+ } else {
+ (format!("{class_name}Interface"), class_name)
}
}
}
@@ -376,12 +494,11 @@ impl<T: AsType> AsCodeType for T {
Type::Duration => Box::new(miscellany::DurationCodeType),
Type::Enum { name, .. } => Box::new(enum_::EnumCodeType::new(name)),
- Type::Object { name, .. } => Box::new(object::ObjectCodeType::new(name)),
+ Type::Object { name, imp, .. } => Box::new(object::ObjectCodeType::new(name, imp)),
Type::Record { name, .. } => Box::new(record::RecordCodeType::new(name)),
Type::CallbackInterface { name, .. } => {
Box::new(callback_interface::CallbackInterfaceCodeType::new(name))
}
- Type::ForeignExecutor => Box::new(executor::ForeignExecutorCodeType),
Type::Optional { inner_type } => {
Box::new(compounds::OptionalCodeType::new(*inner_type))
}
@@ -401,6 +518,7 @@ impl<T: AsType> AsCodeType for T {
mod filters {
use super::*;
pub use crate::backend::filters::*;
+ use uniffi_meta::LiteralMetadata;
pub(super) fn type_name(
as_ct: &impl AsCodeType,
@@ -454,8 +572,52 @@ mod filters {
Ok(as_ct.as_codetype().literal(literal, ci))
}
+ // Get the idiomatic Kotlin rendering of an integer.
+ fn int_literal(t: &Option<Type>, base10: String) -> Result<String, askama::Error> {
+ if let Some(t) = t {
+ match t {
+ Type::Int8 | Type::Int16 | Type::Int32 | Type::Int64 => Ok(base10),
+ Type::UInt8 | Type::UInt16 | Type::UInt32 | Type::UInt64 => Ok(base10 + "u"),
+ _ => Err(askama::Error::Custom(Box::new(UniFFIError::new(
+ "Only ints are supported.".to_string(),
+ )))),
+ }
+ } else {
+ Err(askama::Error::Custom(Box::new(UniFFIError::new(
+ "Enum hasn't defined a repr".to_string(),
+ ))))
+ }
+ }
+
+ // Get the idiomatic Kotlin rendering of an individual enum variant's discriminant
+ pub fn variant_discr_literal(e: &Enum, index: &usize) -> Result<String, askama::Error> {
+ let literal = e.variant_discr(*index).expect("invalid index");
+ match literal {
+ // Kotlin doesn't convert between signed and unsigned by default
+ // so we'll need to make sure we define the type as appropriately
+ LiteralMetadata::UInt(v, _, _) => int_literal(e.variant_discr_type(), v.to_string()),
+ LiteralMetadata::Int(v, _, _) => int_literal(e.variant_discr_type(), v.to_string()),
+ _ => Err(askama::Error::Custom(Box::new(UniFFIError::new(
+ "Only ints are supported.".to_string(),
+ )))),
+ }
+ }
+
pub fn ffi_type_name_by_value(type_: &FfiType) -> Result<String, askama::Error> {
- Ok(KotlinCodeOracle::ffi_type_label_by_value(type_))
+ Ok(KotlinCodeOracle.ffi_type_label_by_value(type_))
+ }
+
+ pub fn ffi_type_name_for_ffi_struct(type_: &FfiType) -> Result<String, askama::Error> {
+ Ok(KotlinCodeOracle.ffi_type_label_for_ffi_struct(type_))
+ }
+
+ pub fn ffi_default_value(type_: FfiType) -> Result<String, askama::Error> {
+ Ok(KotlinCodeOracle.ffi_default_value(&type_))
+ }
+
+ /// Get the idiomatic Kotlin rendering of a function name.
+ pub fn class_name(nm: &str, ci: &ComponentInterface) -> Result<String, askama::Error> {
+ Ok(KotlinCodeOracle.class_name(ci, nm))
}
/// Get the idiomatic Kotlin rendering of a function name.
@@ -468,6 +630,11 @@ mod filters {
Ok(KotlinCodeOracle.var_name(nm))
}
+ /// Get the idiomatic Kotlin rendering of a variable name.
+ pub fn var_name_raw(nm: &str) -> Result<String, askama::Error> {
+ Ok(KotlinCodeOracle.var_name_raw(nm))
+ }
+
/// Get a String representing the name used for an individual enum variant.
pub fn variant_name(v: &Variant) -> Result<String, askama::Error> {
Ok(KotlinCodeOracle.enum_variant_name(v.name()))
@@ -478,13 +645,30 @@ mod filters {
Ok(KotlinCodeOracle.convert_error_suffix(&name))
}
+ /// Get the idiomatic Kotlin rendering of an FFI callback function name
+ pub fn ffi_callback_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(KotlinCodeOracle.ffi_callback_name(nm))
+ }
+
+ /// Get the idiomatic Kotlin rendering of an FFI struct name
+ pub fn ffi_struct_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(KotlinCodeOracle.ffi_struct_name(nm))
+ }
+
+ pub fn object_names(
+ obj: &Object,
+ ci: &ComponentInterface,
+ ) -> Result<(String, String), askama::Error> {
+ Ok(KotlinCodeOracle.object_names(ci, obj))
+ }
+
pub fn async_poll(
callable: impl Callable,
ci: &ComponentInterface,
) -> Result<String, askama::Error> {
let ffi_func = callable.ffi_rust_future_poll(ci);
Ok(format!(
- "{{ future, continuation -> _UniFFILib.INSTANCE.{ffi_func}(future, continuation) }}"
+ "{{ future, callback, continuation -> UniffiLib.INSTANCE.{ffi_func}(future, callback, continuation) }}"
))
}
@@ -493,7 +677,7 @@ mod filters {
ci: &ComponentInterface,
) -> Result<String, askama::Error> {
let ffi_func = callable.ffi_rust_future_complete(ci);
- let call = format!("_UniFFILib.INSTANCE.{ffi_func}(future, continuation)");
+ let call = format!("UniffiLib.INSTANCE.{ffi_func}(future, continuation)");
let call = match callable.return_type() {
Some(Type::External {
kind: ExternalKind::DataClass,
@@ -502,7 +686,7 @@ mod filters {
}) => {
// Need to convert the RustBuffer from our package to the RustBuffer of the external package
let suffix = KotlinCodeOracle.class_name(ci, &name);
- format!("{call}.let {{ RustBuffer{suffix}.create(it.capacity, it.len, it.data) }}")
+ format!("{call}.let {{ RustBuffer{suffix}.create(it.capacity.toULong(), it.len.toULong(), it.data) }}")
}
_ => call,
};
@@ -515,7 +699,7 @@ mod filters {
) -> Result<String, askama::Error> {
let ffi_func = callable.ffi_rust_future_free(ci);
Ok(format!(
- "{{ future -> _UniFFILib.INSTANCE.{ffi_func}(future) }}"
+ "{{ future -> UniffiLib.INSTANCE.{ffi_func}(future) }}"
))
}
@@ -527,4 +711,13 @@ mod filters {
pub fn unquote(nm: &str) -> Result<String, askama::Error> {
Ok(nm.trim_matches('`').to_string())
}
+
+ /// Get the idiomatic Kotlin rendering of docstring
+ pub fn docstring(docstring: &str, spaces: &i32) -> Result<String, askama::Error> {
+ let middle = textwrap::indent(&textwrap::dedent(docstring), " * ");
+ let wrapped = format!("/**\n{middle}\n */");
+
+ let spaces = usize::try_from(*spaces).unwrap_or_default();
+ Ok(textwrap::indent(&wrapped, &" ".repeat(spaces)))
+ }
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/object.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/object.rs
index c39ae59cce..5a4305d14a 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/object.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/object.rs
@@ -3,25 +3,32 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use super::CodeType;
-use crate::ComponentInterface;
+use crate::{interface::ObjectImpl, ComponentInterface};
#[derive(Debug)]
pub struct ObjectCodeType {
- id: String,
+ name: String,
+ imp: ObjectImpl,
}
impl ObjectCodeType {
- pub fn new(id: String) -> Self {
- Self { id }
+ pub fn new(name: String, imp: ObjectImpl) -> Self {
+ Self { name, imp }
}
}
impl CodeType for ObjectCodeType {
fn type_label(&self, ci: &ComponentInterface) -> String {
- super::KotlinCodeOracle.class_name(ci, &self.id)
+ super::KotlinCodeOracle.class_name(ci, &self.name)
}
fn canonical_name(&self) -> String {
- format!("Type{}", self.id)
+ format!("Type{}", self.name)
+ }
+
+ fn initialization_fn(&self) -> Option<String> {
+ self.imp
+ .has_callback_interface()
+ .then(|| format!("uniffiCallbackInterface{}.register", self.name))
}
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/primitives.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/primitives.rs
index 22495fa209..0bc5a5d99e 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/primitives.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/primitives.rs
@@ -9,7 +9,11 @@ use paste::paste;
fn render_literal(literal: &Literal, _ci: &ComponentInterface) -> String {
fn typed_number(type_: &Type, num_str: String) -> String {
- match type_ {
+ let unwrapped_type = match type_ {
+ Type::Optional { inner_type } => inner_type,
+ t => t,
+ };
+ match unwrapped_type {
// Bytes, Shorts and Ints can all be inferred from the type.
Type::Int8 | Type::Int16 | Type::Int32 => num_str,
Type::Int64 => format!("{num_str}L"),
@@ -19,7 +23,7 @@ fn render_literal(literal: &Literal, _ci: &ComponentInterface) -> String {
Type::Float32 => format!("{num_str}f"),
Type::Float64 => num_str,
- _ => panic!("Unexpected literal: {num_str} is not a number"),
+ _ => panic!("Unexpected literal: {num_str} for type: {type_:?}"),
}
}
@@ -56,7 +60,7 @@ macro_rules! impl_code_type_for_primitive {
impl CodeType for $T {
fn type_label(&self, _ci: &ComponentInterface) -> String {
- $class_name.into()
+ format!("kotlin.{}", $class_name)
}
fn canonical_name(&self) -> String {
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Async.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Async.kt
index c6a32655f2..b28fbd2c80 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Async.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Async.kt
@@ -1,44 +1,117 @@
// Async return type handlers
-internal const val UNIFFI_RUST_FUTURE_POLL_READY = 0.toShort()
-internal const val UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1.toShort()
+internal const val UNIFFI_RUST_FUTURE_POLL_READY = 0.toByte()
+internal const val UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1.toByte()
-internal val uniffiContinuationHandleMap = UniFfiHandleMap<CancellableContinuation<Short>>()
+internal val uniffiContinuationHandleMap = UniffiHandleMap<CancellableContinuation<Byte>>()
// FFI type for Rust future continuations
-internal object uniffiRustFutureContinuationCallback: UniFffiRustFutureContinuationCallbackType {
- override fun callback(continuationHandle: USize, pollResult: Short) {
- uniffiContinuationHandleMap.remove(continuationHandle)?.resume(pollResult)
- }
-
- internal fun register(lib: _UniFFILib) {
- lib.{{ ci.ffi_rust_future_continuation_callback_set().name() }}(this)
+internal object uniffiRustFutureContinuationCallbackImpl: UniffiRustFutureContinuationCallback {
+ override fun callback(data: Long, pollResult: Byte) {
+ uniffiContinuationHandleMap.remove(data).resume(pollResult)
}
}
internal suspend fun<T, F, E: Exception> uniffiRustCallAsync(
- rustFuture: Pointer,
- pollFunc: (Pointer, USize) -> Unit,
- completeFunc: (Pointer, RustCallStatus) -> F,
- freeFunc: (Pointer) -> Unit,
+ rustFuture: Long,
+ pollFunc: (Long, UniffiRustFutureContinuationCallback, Long) -> Unit,
+ completeFunc: (Long, UniffiRustCallStatus) -> F,
+ freeFunc: (Long) -> Unit,
liftFunc: (F) -> T,
- errorHandler: CallStatusErrorHandler<E>
+ errorHandler: UniffiRustCallStatusErrorHandler<E>
): T {
try {
do {
- val pollResult = suspendCancellableCoroutine<Short> { continuation ->
+ val pollResult = suspendCancellableCoroutine<Byte> { continuation ->
pollFunc(
rustFuture,
+ uniffiRustFutureContinuationCallbackImpl,
uniffiContinuationHandleMap.insert(continuation)
)
}
} while (pollResult != UNIFFI_RUST_FUTURE_POLL_READY);
return liftFunc(
- rustCallWithError(errorHandler, { status -> completeFunc(rustFuture, status) })
+ uniffiRustCallWithError(errorHandler, { status -> completeFunc(rustFuture, status) })
)
} finally {
freeFunc(rustFuture)
}
}
+{%- if ci.has_async_callback_interface_definition() %}
+internal inline fun<T> uniffiTraitInterfaceCallAsync(
+ crossinline makeCall: suspend () -> T,
+ crossinline handleSuccess: (T) -> Unit,
+ crossinline handleError: (UniffiRustCallStatus.ByValue) -> Unit,
+): UniffiForeignFuture {
+ // Using `GlobalScope` is labeled as a "delicate API" and generally discouraged in Kotlin programs, since it breaks structured concurrency.
+ // However, our parent task is a Rust future, so we're going to need to break structure concurrency in any case.
+ //
+ // Uniffi does its best to support structured concurrency across the FFI.
+ // If the Rust future is dropped, `uniffiForeignFutureFreeImpl` is called, which will cancel the Kotlin coroutine if it's still running.
+ @OptIn(DelicateCoroutinesApi::class)
+ val job = GlobalScope.launch {
+ try {
+ handleSuccess(makeCall())
+ } catch(e: Exception) {
+ handleError(
+ UniffiRustCallStatus.create(
+ UNIFFI_CALL_UNEXPECTED_ERROR,
+ {{ Type::String.borrow()|lower_fn }}(e.toString()),
+ )
+ )
+ }
+ }
+ val handle = uniffiForeignFutureHandleMap.insert(job)
+ return UniffiForeignFuture(handle, uniffiForeignFutureFreeImpl)
+}
+
+internal inline fun<T, reified E: Throwable> uniffiTraitInterfaceCallAsyncWithError(
+ crossinline makeCall: suspend () -> T,
+ crossinline handleSuccess: (T) -> Unit,
+ crossinline handleError: (UniffiRustCallStatus.ByValue) -> Unit,
+ crossinline lowerError: (E) -> RustBuffer.ByValue,
+): UniffiForeignFuture {
+ // See uniffiTraitInterfaceCallAsync for details on `DelicateCoroutinesApi`
+ @OptIn(DelicateCoroutinesApi::class)
+ val job = GlobalScope.launch {
+ try {
+ handleSuccess(makeCall())
+ } catch(e: Exception) {
+ if (e is E) {
+ handleError(
+ UniffiRustCallStatus.create(
+ UNIFFI_CALL_ERROR,
+ lowerError(e),
+ )
+ )
+ } else {
+ handleError(
+ UniffiRustCallStatus.create(
+ UNIFFI_CALL_UNEXPECTED_ERROR,
+ {{ Type::String.borrow()|lower_fn }}(e.toString()),
+ )
+ )
+ }
+ }
+ }
+ val handle = uniffiForeignFutureHandleMap.insert(job)
+ return UniffiForeignFuture(handle, uniffiForeignFutureFreeImpl)
+}
+
+internal val uniffiForeignFutureHandleMap = UniffiHandleMap<Job>()
+
+internal object uniffiForeignFutureFreeImpl: UniffiForeignFutureFree {
+ override fun callback(handle: Long) {
+ val job = uniffiForeignFutureHandleMap.remove(handle)
+ if (!job.isCompleted) {
+ job.cancel()
+ }
+ }
+}
+
+// For testing
+public fun uniffiForeignFutureHandleCount() = uniffiForeignFutureHandleMap.size
+
+{%- endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/BooleanHelper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/BooleanHelper.kt
index 8cfa2ce000..c6b266066d 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/BooleanHelper.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/BooleanHelper.kt
@@ -11,7 +11,7 @@ public object FfiConverterBoolean: FfiConverter<Boolean, Byte> {
return if (value) 1.toByte() else 0.toByte()
}
- override fun allocationSize(value: Boolean) = 1
+ override fun allocationSize(value: Boolean) = 1UL
override fun write(value: Boolean, buf: ByteBuffer) {
buf.put(lower(value))
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ByteArrayHelper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ByteArrayHelper.kt
index 4840a199b4..c9449069e2 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ByteArrayHelper.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ByteArrayHelper.kt
@@ -5,8 +5,8 @@ public object FfiConverterByteArray: FfiConverterRustBuffer<ByteArray> {
buf.get(byteArr)
return byteArr
}
- override fun allocationSize(value: ByteArray): Int {
- return 4 + value.size
+ override fun allocationSize(value: ByteArray): ULong {
+ return 4UL + value.size.toULong()
}
override fun write(value: ByteArray, buf: ByteBuffer) {
buf.putInt(value.size)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceImpl.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceImpl.kt
new file mode 100644
index 0000000000..30a39d9afb
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceImpl.kt
@@ -0,0 +1,117 @@
+{% if self.include_once_check("CallbackInterfaceRuntime.kt") %}{% include "CallbackInterfaceRuntime.kt" %}{% endif %}
+
+{%- let trait_impl=format!("uniffiCallbackInterface{}", name) %}
+
+// Put the implementation in an object so we don't pollute the top-level namespace
+internal object {{ trait_impl }} {
+ {%- for (ffi_callback, meth) in vtable_methods.iter() %}
+ internal object {{ meth.name()|var_name }}: {{ ffi_callback.name()|ffi_callback_name }} {
+ override fun callback(
+ {%- for arg in ffi_callback.arguments() -%}
+ {{ arg.name().borrow()|var_name }}: {{ arg.type_().borrow()|ffi_type_name_by_value }},
+ {%- endfor -%}
+ {%- if ffi_callback.has_rust_call_status_arg() -%}
+ uniffiCallStatus: UniffiRustCallStatus,
+ {%- endif -%}
+ )
+ {%- match ffi_callback.return_type() %}
+ {%- when Some(return_type) %}: {{ return_type|ffi_type_name_by_value }},
+ {%- when None %}
+ {%- endmatch %} {
+ val uniffiObj = {{ ffi_converter_name }}.handleMap.get(uniffiHandle)
+ val makeCall = {% if meth.is_async() %}suspend {% endif %}{ ->
+ uniffiObj.{{ meth.name()|fn_name() }}(
+ {%- for arg in meth.arguments() %}
+ {{ arg|lift_fn }}({{ arg.name()|var_name }}),
+ {%- endfor %}
+ )
+ }
+ {%- if !meth.is_async() %}
+
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ val writeReturn = { value: {{ return_type|type_name(ci) }} -> uniffiOutReturn.setValue({{ return_type|lower_fn }}(value)) }
+ {%- when None %}
+ val writeReturn = { _: Unit -> Unit }
+ {%- endmatch %}
+
+ {%- match meth.throws_type() %}
+ {%- when None %}
+ uniffiTraitInterfaceCall(uniffiCallStatus, makeCall, writeReturn)
+ {%- when Some(error_type) %}
+ uniffiTraitInterfaceCallWithError(
+ uniffiCallStatus,
+ makeCall,
+ writeReturn,
+ { e: {{error_type|type_name(ci) }} -> {{ error_type|lower_fn }}(e) }
+ )
+ {%- endmatch %}
+
+ {%- else %}
+ val uniffiHandleSuccess = { {% if meth.return_type().is_some() %}returnValue{% else %}_{% endif %}: {% match meth.return_type() %}{%- when Some(return_type) %}{{ return_type|type_name(ci) }}{%- when None %}Unit{% endmatch %} ->
+ val uniffiResult = {{ meth.foreign_future_ffi_result_struct().name()|ffi_struct_name }}.UniffiByValue(
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ {{ return_type|lower_fn }}(returnValue),
+ {%- when None %}
+ {%- endmatch %}
+ UniffiRustCallStatus.ByValue()
+ )
+ uniffiResult.write()
+ uniffiFutureCallback.callback(uniffiCallbackData, uniffiResult)
+ }
+ val uniffiHandleError = { callStatus: UniffiRustCallStatus.ByValue ->
+ uniffiFutureCallback.callback(
+ uniffiCallbackData,
+ {{ meth.foreign_future_ffi_result_struct().name()|ffi_struct_name }}.UniffiByValue(
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ {{ return_type.into()|ffi_default_value }},
+ {%- when None %}
+ {%- endmatch %}
+ callStatus,
+ ),
+ )
+ }
+
+ uniffiOutReturn.uniffiSetValue(
+ {%- match meth.throws_type() %}
+ {%- when None %}
+ uniffiTraitInterfaceCallAsync(
+ makeCall,
+ uniffiHandleSuccess,
+ uniffiHandleError
+ )
+ {%- when Some(error_type) %}
+ uniffiTraitInterfaceCallAsyncWithError(
+ makeCall,
+ uniffiHandleSuccess,
+ uniffiHandleError,
+ { e: {{error_type|type_name(ci) }} -> {{ error_type|lower_fn }}(e) }
+ )
+ {%- endmatch %}
+ )
+ {%- endif %}
+ }
+ }
+ {%- endfor %}
+
+ internal object uniffiFree: {{ "CallbackInterfaceFree"|ffi_callback_name }} {
+ override fun callback(handle: Long) {
+ {{ ffi_converter_name }}.handleMap.remove(handle)
+ }
+ }
+
+ internal var vtable = {{ vtable|ffi_type_name_by_value }}(
+ {%- for (ffi_callback, meth) in vtable_methods.iter() %}
+ {{ meth.name()|var_name() }},
+ {%- endfor %}
+ uniffiFree,
+ )
+
+ // Registers the foreign callback with the Rust side.
+ // This method is generated for each callback interface.
+ internal fun register(lib: UniffiLib) {
+ lib.{{ ffi_init_callback.name() }}(vtable)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceRuntime.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceRuntime.kt
index 62a71e02f1..d58a651e24 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceRuntime.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceRuntime.kt
@@ -1,43 +1,3 @@
-internal typealias Handle = Long
-internal class ConcurrentHandleMap<T>(
- private val leftMap: MutableMap<Handle, T> = mutableMapOf(),
- private val rightMap: MutableMap<T, Handle> = mutableMapOf()
-) {
- private val lock = java.util.concurrent.locks.ReentrantLock()
- private val currentHandle = AtomicLong(0L)
- private val stride = 1L
-
- fun insert(obj: T): Handle =
- lock.withLock {
- rightMap[obj] ?:
- currentHandle.getAndAdd(stride)
- .also { handle ->
- leftMap[handle] = obj
- rightMap[obj] = handle
- }
- }
-
- fun get(handle: Handle) = lock.withLock {
- leftMap[handle]
- }
-
- fun delete(handle: Handle) {
- this.remove(handle)
- }
-
- fun remove(handle: Handle): T? =
- lock.withLock {
- leftMap.remove(handle)?.let { obj ->
- rightMap.remove(obj)
- obj
- }
- }
-}
-
-interface ForeignCallback : com.sun.jna.Callback {
- public fun callback(handle: Handle, method: Int, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int
-}
-
// Magic number for the Rust proxy to call using the same mechanism as every other method,
// to free the callback once it's dropped by Rust.
internal const val IDX_CALLBACK_FREE = 0
@@ -46,31 +6,22 @@ internal const val UNIFFI_CALLBACK_SUCCESS = 0
internal const val UNIFFI_CALLBACK_ERROR = 1
internal const val UNIFFI_CALLBACK_UNEXPECTED_ERROR = 2
-public abstract class FfiConverterCallbackInterface<CallbackInterface>(
- protected val foreignCallback: ForeignCallback
-): FfiConverter<CallbackInterface, Handle> {
- private val handleMap = ConcurrentHandleMap<CallbackInterface>()
-
- // Registers the foreign callback with the Rust side.
- // This method is generated for each callback interface.
- internal abstract fun register(lib: _UniFFILib)
+public abstract class FfiConverterCallbackInterface<CallbackInterface: Any>: FfiConverter<CallbackInterface, Long> {
+ internal val handleMap = UniffiHandleMap<CallbackInterface>()
- fun drop(handle: Handle): RustBuffer.ByValue {
- return handleMap.remove(handle).let { RustBuffer.ByValue() }
+ internal fun drop(handle: Long) {
+ handleMap.remove(handle)
}
- override fun lift(value: Handle): CallbackInterface {
- return handleMap.get(value) ?: throw InternalException("No callback in handlemap; this is a Uniffi bug")
+ override fun lift(value: Long): CallbackInterface {
+ return handleMap.get(value)
}
override fun read(buf: ByteBuffer) = lift(buf.getLong())
- override fun lower(value: CallbackInterface) =
- handleMap.insert(value).also {
- assert(handleMap.get(it) === value) { "Handle map is not returning the object we just placed there. This is a bug in the HandleMap." }
- }
+ override fun lower(value: CallbackInterface) = handleMap.insert(value)
- override fun allocationSize(value: CallbackInterface) = 8
+ override fun allocationSize(value: CallbackInterface) = 8UL
override fun write(value: CallbackInterface, buf: ByteBuffer) {
buf.putLong(lower(value))
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceTemplate.kt
index 5a29f0acc3..d2cdee4f33 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceTemplate.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceTemplate.kt
@@ -1,129 +1,13 @@
{%- let cbi = ci|get_callback_interface_definition(name) %}
-{%- let type_name = cbi|type_name(ci) %}
-{%- let foreign_callback = format!("ForeignCallback{}", canonical_type_name) %}
-
-{% if self.include_once_check("CallbackInterfaceRuntime.kt") %}{% include "CallbackInterfaceRuntime.kt" %}{% endif %}
-{{- self.add_import("java.util.concurrent.atomic.AtomicLong") }}
-{{- self.add_import("java.util.concurrent.locks.ReentrantLock") }}
-{{- self.add_import("kotlin.concurrent.withLock") }}
-
-// Declaration and FfiConverters for {{ type_name }} Callback Interface
-
-public interface {{ type_name }} {
- {% for meth in cbi.methods() -%}
- fun {{ meth.name()|fn_name }}({% call kt::arg_list_decl(meth) %})
- {%- match meth.return_type() -%}
- {%- when Some with (return_type) %}: {{ return_type|type_name(ci) -}}
- {%- else -%}
- {%- endmatch %}
- {% endfor %}
- companion object
-}
-
-// The ForeignCallback that is passed to Rust.
-internal class {{ foreign_callback }} : ForeignCallback {
- @Suppress("TooGenericExceptionCaught")
- override fun callback(handle: Handle, method: Int, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int {
- val cb = {{ ffi_converter_name }}.lift(handle)
- return when (method) {
- IDX_CALLBACK_FREE -> {
- {{ ffi_converter_name }}.drop(handle)
- // Successful return
- // See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
- UNIFFI_CALLBACK_SUCCESS
- }
- {% for meth in cbi.methods() -%}
- {% let method_name = format!("invoke_{}", meth.name())|fn_name -%}
- {{ loop.index }} -> {
- // Call the method, write to outBuf and return a status code
- // See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs` for info
- try {
- this.{{ method_name }}(cb, argsData, argsLen, outBuf)
- } catch (e: Throwable) {
- // Unexpected error
- try {
- // Try to serialize the error into a string
- outBuf.setValue({{ Type::String.borrow()|ffi_converter_name }}.lower(e.toString()))
- } catch (e: Throwable) {
- // If that fails, then it's time to give up and just return
- }
- UNIFFI_CALLBACK_UNEXPECTED_ERROR
- }
- }
- {% endfor %}
- else -> {
- // An unexpected error happened.
- // See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
- try {
- // Try to serialize the error into a string
- outBuf.setValue({{ Type::String.borrow()|ffi_converter_name }}.lower("Invalid Callback index"))
- } catch (e: Throwable) {
- // If that fails, then it's time to give up and just return
- }
- UNIFFI_CALLBACK_UNEXPECTED_ERROR
- }
- }
- }
-
- {% for meth in cbi.methods() -%}
- {% let method_name = format!("invoke_{}", meth.name())|fn_name %}
- @Suppress("UNUSED_PARAMETER")
- private fun {{ method_name }}(kotlinCallbackInterface: {{ type_name }}, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int {
- {%- if meth.arguments().len() > 0 %}
- val argsBuf = argsData.getByteBuffer(0, argsLen.toLong()).also {
- it.order(ByteOrder.BIG_ENDIAN)
- }
- {%- endif %}
-
- {%- match meth.return_type() %}
- {%- when Some with (return_type) %}
- fun makeCall() : Int {
- val returnValue = kotlinCallbackInterface.{{ meth.name()|fn_name }}(
- {%- for arg in meth.arguments() %}
- {{ arg|read_fn }}(argsBuf)
- {% if !loop.last %}, {% endif %}
- {%- endfor %}
- )
- outBuf.setValue({{ return_type|ffi_converter_name }}.lowerIntoRustBuffer(returnValue))
- return UNIFFI_CALLBACK_SUCCESS
- }
- {%- when None %}
- fun makeCall() : Int {
- kotlinCallbackInterface.{{ meth.name()|fn_name }}(
- {%- for arg in meth.arguments() %}
- {{ arg|read_fn }}(argsBuf)
- {%- if !loop.last %}, {% endif %}
- {%- endfor %}
- )
- return UNIFFI_CALLBACK_SUCCESS
- }
- {%- endmatch %}
-
- {%- match meth.throws_type() %}
- {%- when None %}
- fun makeCallAndHandleError() : Int = makeCall()
- {%- when Some(error_type) %}
- fun makeCallAndHandleError() : Int = try {
- makeCall()
- } catch (e: {{ error_type|type_name(ci) }}) {
- // Expected error, serialize it into outBuf
- outBuf.setValue({{ error_type|ffi_converter_name }}.lowerIntoRustBuffer(e))
- UNIFFI_CALLBACK_ERROR
- }
- {%- endmatch %}
-
- return makeCallAndHandleError()
- }
- {% endfor %}
-}
-
-// The ffiConverter which transforms the Callbacks in to Handles to pass to Rust.
-public object {{ ffi_converter_name }}: FfiConverterCallbackInterface<{{ type_name }}>(
- foreignCallback = {{ foreign_callback }}()
-) {
- override fun register(lib: _UniFFILib) {
- rustCall() { status ->
- lib.{{ cbi.ffi_init_callback().name() }}(this.foreignCallback, status)
- }
- }
-}
+{%- let ffi_init_callback = cbi.ffi_init_callback() %}
+{%- let interface_name = cbi|type_name(ci) %}
+{%- let interface_docstring = cbi.docstring() %}
+{%- let methods = cbi.methods() %}
+{%- let vtable = cbi.vtable() %}
+{%- let vtable_methods = cbi.vtable_methods() %}
+
+{% include "Interface.kt" %}
+{% include "CallbackInterfaceImpl.kt" %}
+
+// The ffiConverter which transforms the Callbacks in to handles to pass to Rust.
+public object {{ ffi_converter_name }}: FfiConverterCallbackInterface<{{ interface_name }}>()
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CustomTypeTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CustomTypeTemplate.kt
index 04150c5d78..aeb5f58002 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CustomTypeTemplate.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CustomTypeTemplate.kt
@@ -49,7 +49,7 @@ public object {{ ffi_converter_name }}: FfiConverter<{{ name }}, {{ ffi_type_nam
return {{ config.into_custom.render("builtinValue") }}
}
- override fun allocationSize(value: {{ name }}): Int {
+ override fun allocationSize(value: {{ name }}): ULong {
val builtinValue = {{ config.from_custom.render("value") }}
return {{ builtin|allocation_size_fn }}(builtinValue)
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/DurationHelper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/DurationHelper.kt
index 4237c6f9a8..62e02607f3 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/DurationHelper.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/DurationHelper.kt
@@ -14,7 +14,7 @@ public object FfiConverterDuration: FfiConverterRustBuffer<java.time.Duration> {
}
// 8 bytes for seconds, 4 bytes for nanoseconds
- override fun allocationSize(value: java.time.Duration) = 12
+ override fun allocationSize(value: java.time.Duration) = 12UL
override fun write(value: java.time.Duration, buf: ByteBuffer) {
if (value.seconds < 0) {
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt
index d4c4a1684a..8d1c2235ec 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt
@@ -7,12 +7,25 @@
{%- if e.is_flat() %}
+{%- call kt::docstring(e, 0) %}
+{% match e.variant_discr_type() %}
+{% when None %}
enum class {{ type_name }} {
{% for variant in e.variants() -%}
+ {%- call kt::docstring(variant, 4) %}
{{ variant|variant_name }}{% if loop.last %};{% else %},{% endif %}
{%- endfor %}
companion object
}
+{% when Some with (variant_discr_type) %}
+enum class {{ type_name }}(val value: {{ variant_discr_type|type_name(ci) }}) {
+ {% for variant in e.variants() -%}
+ {%- call kt::docstring(variant, 4) %}
+ {{ variant|variant_name }}({{ e|variant_discr_literal(loop.index0) }}){% if loop.last %};{% else %},{% endif %}
+ {%- endfor %}
+ companion object
+}
+{% endmatch %}
public object {{ e|ffi_converter_name }}: FfiConverterRustBuffer<{{ type_name }}> {
override fun read(buf: ByteBuffer) = try {
@@ -21,7 +34,7 @@ public object {{ e|ffi_converter_name }}: FfiConverterRustBuffer<{{ type_name }}
throw RuntimeException("invalid enum value, something is very wrong!!", e)
}
- override fun allocationSize(value: {{ type_name }}) = 4
+ override fun allocationSize(value: {{ type_name }}) = 4UL
override fun write(value: {{ type_name }}, buf: ByteBuffer) {
buf.putInt(value.ordinal + 1)
@@ -30,15 +43,18 @@ public object {{ e|ffi_converter_name }}: FfiConverterRustBuffer<{{ type_name }}
{% else %}
+{%- call kt::docstring(e, 0) %}
sealed class {{ type_name }}{% if contains_object_references %}: Disposable {% endif %} {
{% for variant in e.variants() -%}
+ {%- call kt::docstring(variant, 4) %}
{% if !variant.has_fields() -%}
object {{ variant|type_name(ci) }} : {{ type_name }}()
{% else -%}
data class {{ variant|type_name(ci) }}(
- {% for field in variant.fields() -%}
- val {{ field.name()|var_name }}: {{ field|type_name(ci) }}{% if loop.last %}{% else %}, {% endif %}
- {% endfor -%}
+ {%- for field in variant.fields() -%}
+ {%- call kt::docstring(field, 8) %}
+ val {% call kt::field_name(field, loop.index) %}: {{ field|type_name(ci) }}{% if loop.last %}{% else %}, {% endif %}
+ {%- endfor -%}
) : {{ type_name }}() {
companion object
}
@@ -83,9 +99,9 @@ public object {{ e|ffi_converter_name }} : FfiConverterRustBuffer<{{ type_name }
is {{ type_name }}.{{ variant|type_name(ci) }} -> {
// Add the size for the Int that specifies the variant plus the size needed for all fields
(
- 4
+ 4UL
{%- for field in variant.fields() %}
- + {{ field|allocation_size_fn }}(value.{{ field.name()|var_name }})
+ + {{ field|allocation_size_fn }}(value.{%- call kt::field_name(field, loop.index) -%})
{%- endfor %}
)
}
@@ -98,7 +114,7 @@ public object {{ e|ffi_converter_name }} : FfiConverterRustBuffer<{{ type_name }
is {{ type_name }}.{{ variant|type_name(ci) }} -> {
buf.putInt({{ loop.index }})
{%- for field in variant.fields() %}
- {{ field|write_fn }}(value.{{ field.name()|var_name }}, buf)
+ {{ field|write_fn }}(value.{%- call kt::field_name(field, loop.index) -%}, buf)
{%- endfor %}
Unit
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ErrorTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ErrorTemplate.kt
index 986db5424d..4760c03fd6 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ErrorTemplate.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ErrorTemplate.kt
@@ -3,24 +3,26 @@
{%- let canonical_type_name = type_|canonical_name %}
{% if e.is_flat() %}
+{%- call kt::docstring(e, 0) %}
sealed class {{ type_name }}(message: String): Exception(message){% if contains_object_references %}, Disposable {% endif %} {
- // Each variant is a nested class
- // Flat enums carries a string error message, so no special implementation is necessary.
{% for variant in e.variants() -%}
+ {%- call kt::docstring(variant, 4) %}
class {{ variant|error_variant_name }}(message: String) : {{ type_name }}(message)
{% endfor %}
- companion object ErrorHandler : CallStatusErrorHandler<{{ type_name }}> {
+ companion object ErrorHandler : UniffiRustCallStatusErrorHandler<{{ type_name }}> {
override fun lift(error_buf: RustBuffer.ByValue): {{ type_name }} = {{ ffi_converter_name }}.lift(error_buf)
}
}
{%- else %}
+{%- call kt::docstring(e, 0) %}
sealed class {{ type_name }}: Exception(){% if contains_object_references %}, Disposable {% endif %} {
- // Each variant is a nested class
{% for variant in e.variants() -%}
+ {%- call kt::docstring(variant, 4) %}
{%- let variant_name = variant|error_variant_name %}
class {{ variant_name }}(
{% for field in variant.fields() -%}
+ {%- call kt::docstring(field, 8) %}
val {{ field.name()|var_name }}: {{ field|type_name(ci) }}{% if loop.last %}{% else %}, {% endif %}
{% endfor -%}
) : {{ type_name }}() {
@@ -29,7 +31,7 @@ sealed class {{ type_name }}: Exception(){% if contains_object_references %}, Di
}
{% endfor %}
- companion object ErrorHandler : CallStatusErrorHandler<{{ type_name }}> {
+ companion object ErrorHandler : UniffiRustCallStatusErrorHandler<{{ type_name }}> {
override fun lift(error_buf: RustBuffer.ByValue): {{ type_name }} = {{ ffi_converter_name }}.lift(error_buf)
}
@@ -76,15 +78,15 @@ public object {{ e|ffi_converter_name }} : FfiConverterRustBuffer<{{ type_name }
{%- endif %}
}
- override fun allocationSize(value: {{ type_name }}): Int {
+ override fun allocationSize(value: {{ type_name }}): ULong {
{%- if e.is_flat() %}
- return 4
+ return 4UL
{%- else %}
return when(value) {
{%- for variant in e.variants() %}
is {{ type_name }}.{{ variant|error_variant_name }} -> (
// Add the size for the Int that specifies the variant plus the size needed for all fields
- 4
+ 4UL
{%- for field in variant.fields() %}
+ {{ field|allocation_size_fn }}(value.{{ field.name()|var_name }})
{%- endfor %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ExternalTypeTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ExternalTypeTemplate.kt
index 0fade7a0bc..b7e77f0b2d 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ExternalTypeTemplate.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ExternalTypeTemplate.kt
@@ -1,5 +1,5 @@
{%- let package_name=self.external_type_package_name(module_path, namespace) %}
-{%- let fully_qualified_type_name = "{}.{}"|format(package_name, name) %}
+{%- let fully_qualified_type_name = "{}.{}"|format(package_name, name|class_name(ci)) %}
{%- let fully_qualified_ffi_converter_name = "{}.FfiConverterType{}"|format(package_name, name) %}
{%- let fully_qualified_rustbuffer_name = "{}.RustBuffer"|format(package_name) %}
{%- let local_rustbuffer_name = "RustBuffer{}"|format(name) %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/FfiConverterTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/FfiConverterTemplate.kt
index 3b2c9d225a..0de90b9c4b 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/FfiConverterTemplate.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/FfiConverterTemplate.kt
@@ -20,7 +20,7 @@ public interface FfiConverter<KotlinType, FfiType> {
// encoding, so we pessimistically allocate the largest size possible (3
// bytes per codepoint). Allocating extra bytes is not really a big deal
// because the `RustBuffer` is short-lived.
- fun allocationSize(value: KotlinType): Int
+ fun allocationSize(value: KotlinType): ULong
// Write a Kotlin type to a `ByteBuffer`
fun write(value: KotlinType, buf: ByteBuffer)
@@ -34,11 +34,11 @@ public interface FfiConverter<KotlinType, FfiType> {
fun lowerIntoRustBuffer(value: KotlinType): RustBuffer.ByValue {
val rbuf = RustBuffer.alloc(allocationSize(value))
try {
- val bbuf = rbuf.data!!.getByteBuffer(0, rbuf.capacity.toLong()).also {
+ val bbuf = rbuf.data!!.getByteBuffer(0, rbuf.capacity).also {
it.order(ByteOrder.BIG_ENDIAN)
}
write(value, bbuf)
- rbuf.writeField("len", bbuf.position())
+ rbuf.writeField("len", bbuf.position().toLong())
return rbuf
} catch (e: Throwable) {
RustBuffer.free(rbuf)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Float32Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Float32Helper.kt
index eafec5d122..be91ac8fcb 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Float32Helper.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Float32Helper.kt
@@ -11,7 +11,7 @@ public object FfiConverterFloat: FfiConverter<Float, Float> {
return value
}
- override fun allocationSize(value: Float) = 4
+ override fun allocationSize(value: Float) = 4UL
override fun write(value: Float, buf: ByteBuffer) {
buf.putFloat(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Float64Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Float64Helper.kt
index 9fc2892c95..5eb465f0df 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Float64Helper.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Float64Helper.kt
@@ -11,7 +11,7 @@ public object FfiConverterDouble: FfiConverter<Double, Double> {
return value
}
- override fun allocationSize(value: Double) = 8
+ override fun allocationSize(value: Double) = 8UL
override fun write(value: Double, buf: ByteBuffer) {
buf.putDouble(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ForeignExecutorTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ForeignExecutorTemplate.kt
deleted file mode 100644
index 3544b2f9e6..0000000000
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ForeignExecutorTemplate.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-{{ self.add_import("kotlinx.coroutines.CoroutineScope") }}
-{{ self.add_import("kotlinx.coroutines.delay") }}
-{{ self.add_import("kotlinx.coroutines.isActive") }}
-{{ self.add_import("kotlinx.coroutines.launch") }}
-
-internal const val UNIFFI_RUST_TASK_CALLBACK_SUCCESS = 0.toByte()
-internal const val UNIFFI_RUST_TASK_CALLBACK_CANCELLED = 1.toByte()
-internal const val UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS = 0.toByte()
-internal const val UNIFFI_FOREIGN_EXECUTOR_CALLBACK_CANCELLED = 1.toByte()
-internal const val UNIFFI_FOREIGN_EXECUTOR_CALLBACK_ERROR = 2.toByte()
-
-// Callback function to execute a Rust task. The Kotlin code schedules these in a coroutine then
-// invokes them.
-internal interface UniFfiRustTaskCallback : com.sun.jna.Callback {
- fun callback(rustTaskData: Pointer?, statusCode: Byte)
-}
-
-internal object UniFfiForeignExecutorCallback : com.sun.jna.Callback {
- fun callback(handle: USize, delayMs: Int, rustTask: UniFfiRustTaskCallback?, rustTaskData: Pointer?) : Byte {
- if (rustTask == null) {
- FfiConverterForeignExecutor.drop(handle)
- return UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS
- } else {
- val coroutineScope = FfiConverterForeignExecutor.lift(handle)
- if (coroutineScope.isActive) {
- val job = coroutineScope.launch {
- if (delayMs > 0) {
- delay(delayMs.toLong())
- }
- rustTask.callback(rustTaskData, UNIFFI_RUST_TASK_CALLBACK_SUCCESS)
- }
- job.invokeOnCompletion { cause ->
- if (cause != null) {
- rustTask.callback(rustTaskData, UNIFFI_RUST_TASK_CALLBACK_CANCELLED)
- }
- }
- return UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS
- } else {
- return UNIFFI_FOREIGN_EXECUTOR_CALLBACK_CANCELLED
- }
- }
- }
-}
-
-public object FfiConverterForeignExecutor: FfiConverter<CoroutineScope, USize> {
- internal val handleMap = UniFfiHandleMap<CoroutineScope>()
-
- internal fun drop(handle: USize) {
- handleMap.remove(handle)
- }
-
- internal fun register(lib: _UniFFILib) {
- {%- match ci.ffi_foreign_executor_callback_set() %}
- {%- when Some with (fn) %}
- lib.{{ fn.name() }}(UniFfiForeignExecutorCallback)
- {%- when None %}
- {#- No foreign executor, we don't set anything #}
- {% endmatch %}
- }
-
- // Number of live handles, exposed so we can test the memory management
- public fun handleCount() : Int {
- return handleMap.size
- }
-
- override fun allocationSize(value: CoroutineScope) = USize.size
-
- override fun lift(value: USize): CoroutineScope {
- return handleMap.get(value) ?: throw RuntimeException("unknown handle in FfiConverterForeignExecutor.lift")
- }
-
- override fun read(buf: ByteBuffer): CoroutineScope {
- return lift(USize.readFromBuffer(buf))
- }
-
- override fun lower(value: CoroutineScope): USize {
- return handleMap.insert(value)
- }
-
- override fun write(value: CoroutineScope, buf: ByteBuffer) {
- lower(value).writeToBuffer(buf)
- }
-}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/HandleMap.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/HandleMap.kt
new file mode 100644
index 0000000000..3a56648190
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/HandleMap.kt
@@ -0,0 +1,27 @@
+// Map handles to objects
+//
+// This is used pass an opaque 64-bit handle representing a foreign object to the Rust code.
+internal class UniffiHandleMap<T: Any> {
+ private val map = ConcurrentHashMap<Long, T>()
+ private val counter = java.util.concurrent.atomic.AtomicLong(0)
+
+ val size: Int
+ get() = map.size
+
+ // Insert a new object into the handle map and get a handle for it
+ fun insert(obj: T): Long {
+ val handle = counter.getAndAdd(1)
+ map.put(handle, obj)
+ return handle
+ }
+
+ // Get an object from the handle map
+ fun get(handle: Long): T {
+ return map.get(handle) ?: throw InternalException("UniffiHandleMap.get: Invalid handle")
+ }
+
+ // Remove an entry from the handlemap and get the Kotlin object back
+ fun remove(handle: Long): T {
+ return map.remove(handle) ?: throw InternalException("UniffiHandleMap: Invalid handle")
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Helpers.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Helpers.kt
index 382a5f7413..1fdbd3ffc0 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Helpers.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Helpers.kt
@@ -1,30 +1,43 @@
// A handful of classes and functions to support the generated data structures.
// This would be a good candidate for isolating in its own ffi-support lib.
-// Error runtime.
+
+internal const val UNIFFI_CALL_SUCCESS = 0.toByte()
+internal const val UNIFFI_CALL_ERROR = 1.toByte()
+internal const val UNIFFI_CALL_UNEXPECTED_ERROR = 2.toByte()
+
@Structure.FieldOrder("code", "error_buf")
-internal open class RustCallStatus : Structure() {
+internal open class UniffiRustCallStatus : Structure() {
@JvmField var code: Byte = 0
@JvmField var error_buf: RustBuffer.ByValue = RustBuffer.ByValue()
- class ByValue: RustCallStatus(), Structure.ByValue
+ class ByValue: UniffiRustCallStatus(), Structure.ByValue
fun isSuccess(): Boolean {
- return code == 0.toByte()
+ return code == UNIFFI_CALL_SUCCESS
}
fun isError(): Boolean {
- return code == 1.toByte()
+ return code == UNIFFI_CALL_ERROR
}
fun isPanic(): Boolean {
- return code == 2.toByte()
+ return code == UNIFFI_CALL_UNEXPECTED_ERROR
+ }
+
+ companion object {
+ fun create(code: Byte, errorBuf: RustBuffer.ByValue): UniffiRustCallStatus.ByValue {
+ val callStatus = UniffiRustCallStatus.ByValue()
+ callStatus.code = code
+ callStatus.error_buf = errorBuf
+ return callStatus
+ }
}
}
class InternalException(message: String) : Exception(message)
// Each top-level error class has a companion object that can lift the error from the call status's rust buffer
-interface CallStatusErrorHandler<E> {
+interface UniffiRustCallStatusErrorHandler<E> {
fun lift(error_buf: RustBuffer.ByValue): E;
}
@@ -33,15 +46,15 @@ interface CallStatusErrorHandler<E> {
// synchronize itself
// Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err
-private inline fun <U, E: Exception> rustCallWithError(errorHandler: CallStatusErrorHandler<E>, callback: (RustCallStatus) -> U): U {
- var status = RustCallStatus();
+private inline fun <U, E: Exception> uniffiRustCallWithError(errorHandler: UniffiRustCallStatusErrorHandler<E>, callback: (UniffiRustCallStatus) -> U): U {
+ var status = UniffiRustCallStatus();
val return_value = callback(status)
- checkCallStatus(errorHandler, status)
+ uniffiCheckCallStatus(errorHandler, status)
return return_value
}
-// Check RustCallStatus and throw an error if the call wasn't successful
-private fun<E: Exception> checkCallStatus(errorHandler: CallStatusErrorHandler<E>, status: RustCallStatus) {
+// Check UniffiRustCallStatus and throw an error if the call wasn't successful
+private fun<E: Exception> uniffiCheckCallStatus(errorHandler: UniffiRustCallStatusErrorHandler<E>, status: UniffiRustCallStatus) {
if (status.isSuccess()) {
return
} else if (status.isError()) {
@@ -60,8 +73,8 @@ private fun<E: Exception> checkCallStatus(errorHandler: CallStatusErrorHandler<E
}
}
-// CallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR
-object NullCallStatusErrorHandler: CallStatusErrorHandler<InternalException> {
+// UniffiRustCallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR
+object UniffiNullRustCallStatusErrorHandler: UniffiRustCallStatusErrorHandler<InternalException> {
override fun lift(error_buf: RustBuffer.ByValue): InternalException {
RustBuffer.free(error_buf)
return InternalException("Unexpected CALL_ERROR")
@@ -69,93 +82,38 @@ object NullCallStatusErrorHandler: CallStatusErrorHandler<InternalException> {
}
// Call a rust function that returns a plain value
-private inline fun <U> rustCall(callback: (RustCallStatus) -> U): U {
- return rustCallWithError(NullCallStatusErrorHandler, callback);
+private inline fun <U> uniffiRustCall(callback: (UniffiRustCallStatus) -> U): U {
+ return uniffiRustCallWithError(UniffiNullRustCallStatusErrorHandler, callback);
}
-// IntegerType that matches Rust's `usize` / C's `size_t`
-public class USize(value: Long = 0) : IntegerType(Native.SIZE_T_SIZE, value, true) {
- // This is needed to fill in the gaps of IntegerType's implementation of Number for Kotlin.
- override fun toByte() = toInt().toByte()
- // Needed until https://youtrack.jetbrains.com/issue/KT-47902 is fixed.
- @Deprecated("`toInt().toChar()` is deprecated")
- override fun toChar() = toInt().toChar()
- override fun toShort() = toInt().toShort()
-
- fun writeToBuffer(buf: ByteBuffer) {
- // Make sure we always write usize integers using native byte-order, since they may be
- // casted to pointer values
- buf.order(ByteOrder.nativeOrder())
- try {
- when (Native.SIZE_T_SIZE) {
- 4 -> buf.putInt(toInt())
- 8 -> buf.putLong(toLong())
- else -> throw RuntimeException("Invalid SIZE_T_SIZE: ${Native.SIZE_T_SIZE}")
- }
- } finally {
- buf.order(ByteOrder.BIG_ENDIAN)
- }
- }
-
- companion object {
- val size: Int
- get() = Native.SIZE_T_SIZE
-
- fun readFromBuffer(buf: ByteBuffer) : USize {
- // Make sure we always read usize integers using native byte-order, since they may be
- // casted from pointer values
- buf.order(ByteOrder.nativeOrder())
- try {
- return when (Native.SIZE_T_SIZE) {
- 4 -> USize(buf.getInt().toLong())
- 8 -> USize(buf.getLong())
- else -> throw RuntimeException("Invalid SIZE_T_SIZE: ${Native.SIZE_T_SIZE}")
- }
- } finally {
- buf.order(ByteOrder.BIG_ENDIAN)
- }
- }
+internal inline fun<T> uniffiTraitInterfaceCall(
+ callStatus: UniffiRustCallStatus,
+ makeCall: () -> T,
+ writeReturn: (T) -> Unit,
+) {
+ try {
+ writeReturn(makeCall())
+ } catch(e: Exception) {
+ callStatus.code = UNIFFI_CALL_UNEXPECTED_ERROR
+ callStatus.error_buf = {{ Type::String.borrow()|lower_fn }}(e.toString())
}
}
-
-// Map handles to objects
-//
-// This is used when the Rust code expects an opaque pointer to represent some foreign object.
-// Normally we would pass a pointer to the object, but JNA doesn't support getting a pointer from an
-// object reference , nor does it support leaking a reference to Rust.
-//
-// Instead, this class maps USize values to objects so that we can pass a pointer-sized type to
-// Rust when it needs an opaque pointer.
-//
-// TODO: refactor callbacks to use this class
-internal class UniFfiHandleMap<T: Any> {
- private val map = ConcurrentHashMap<USize, T>()
- // Use AtomicInteger for our counter, since we may be on a 32-bit system. 4 billion possible
- // values seems like enough. If somehow we generate 4 billion handles, then this will wrap
- // around back to zero and we can assume the first handle generated will have been dropped by
- // then.
- private val counter = java.util.concurrent.atomic.AtomicInteger(0)
-
- val size: Int
- get() = map.size
-
- fun insert(obj: T): USize {
- val handle = USize(counter.getAndAdd(1).toLong())
- map.put(handle, obj)
- return handle
- }
-
- fun get(handle: USize): T? {
- return map.get(handle)
- }
-
- fun remove(handle: USize): T? {
- return map.remove(handle)
+internal inline fun<T, reified E: Throwable> uniffiTraitInterfaceCallWithError(
+ callStatus: UniffiRustCallStatus,
+ makeCall: () -> T,
+ writeReturn: (T) -> Unit,
+ lowerError: (E) -> RustBuffer.ByValue
+) {
+ try {
+ writeReturn(makeCall())
+ } catch(e: Exception) {
+ if (e is E) {
+ callStatus.code = UNIFFI_CALL_ERROR
+ callStatus.error_buf = lowerError(e)
+ } else {
+ callStatus.code = UNIFFI_CALL_UNEXPECTED_ERROR
+ callStatus.error_buf = {{ Type::String.borrow()|lower_fn }}(e.toString())
+ }
}
}
-
-// FFI type for Rust future continuations
-internal interface UniFffiRustFutureContinuationCallbackType : com.sun.jna.Callback {
- fun callback(continuationHandle: USize, pollResult: Short);
-}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int16Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int16Helper.kt
index 75564276be..de8296fff6 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int16Helper.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int16Helper.kt
@@ -11,7 +11,7 @@ public object FfiConverterShort: FfiConverter<Short, Short> {
return value
}
- override fun allocationSize(value: Short) = 2
+ override fun allocationSize(value: Short) = 2UL
override fun write(value: Short, buf: ByteBuffer) {
buf.putShort(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int32Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int32Helper.kt
index b7a8131c8b..171809a9c4 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int32Helper.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int32Helper.kt
@@ -11,7 +11,7 @@ public object FfiConverterInt: FfiConverter<Int, Int> {
return value
}
- override fun allocationSize(value: Int) = 4
+ override fun allocationSize(value: Int) = 4UL
override fun write(value: Int, buf: ByteBuffer) {
buf.putInt(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int64Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int64Helper.kt
index 601cfc7c2c..35cf8f3169 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int64Helper.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int64Helper.kt
@@ -11,7 +11,7 @@ public object FfiConverterLong: FfiConverter<Long, Long> {
return value
}
- override fun allocationSize(value: Long) = 8
+ override fun allocationSize(value: Long) = 8UL
override fun write(value: Long, buf: ByteBuffer) {
buf.putLong(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int8Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int8Helper.kt
index 9237768dbf..27c98a6659 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int8Helper.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int8Helper.kt
@@ -11,7 +11,7 @@ public object FfiConverterByte: FfiConverter<Byte, Byte> {
return value
}
- override fun allocationSize(value: Byte) = 1
+ override fun allocationSize(value: Byte) = 1UL
override fun write(value: Byte, buf: ByteBuffer) {
buf.put(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Interface.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Interface.kt
new file mode 100644
index 0000000000..0b4249fb11
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Interface.kt
@@ -0,0 +1,14 @@
+{%- call kt::docstring_value(interface_docstring, 0) %}
+public interface {{ interface_name }} {
+ {% for meth in methods.iter() -%}
+ {%- call kt::docstring(meth, 4) %}
+ {% if meth.is_async() -%}suspend {% endif -%}
+ fun {{ meth.name()|fn_name }}({% call kt::arg_list(meth, true) %})
+ {%- match meth.return_type() -%}
+ {%- when Some with (return_type) %}: {{ return_type|type_name(ci) -}}
+ {%- else -%}
+ {%- endmatch %}
+ {% endfor %}
+ companion object
+}
+
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/MapTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/MapTemplate.kt
index 776c402727..a80418eb00 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/MapTemplate.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/MapTemplate.kt
@@ -12,8 +12,8 @@ public object {{ ffi_converter_name }}: FfiConverterRustBuffer<Map<{{ key_type_n
}
}
- override fun allocationSize(value: Map<{{ key_type_name }}, {{ value_type_name }}>): Int {
- val spaceForMapSize = 4
+ override fun allocationSize(value: Map<{{ key_type_name }}, {{ value_type_name }}>): ULong {
+ val spaceForMapSize = 4UL
val spaceForChildren = value.map { (k, v) ->
{{ key_type|allocation_size_fn }}(k) +
{{ value_type|allocation_size_fn }}(v)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt
index 6a3aeada35..1bac8a435c 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt
@@ -13,14 +13,57 @@ private inline fun <reified Lib : Library> loadIndirect(
return Native.load<Lib>(findLibraryName(componentName), Lib::class.java)
}
+// Define FFI callback types
+{%- for def in ci.ffi_definitions() %}
+{%- match def %}
+{%- when FfiDefinition::CallbackFunction(callback) %}
+internal interface {{ callback.name()|ffi_callback_name }} : com.sun.jna.Callback {
+ fun callback(
+ {%- for arg in callback.arguments() -%}
+ {{ arg.name().borrow()|var_name }}: {{ arg.type_().borrow()|ffi_type_name_by_value }},
+ {%- endfor -%}
+ {%- if callback.has_rust_call_status_arg() -%}
+ uniffiCallStatus: UniffiRustCallStatus,
+ {%- endif -%}
+ )
+ {%- match callback.return_type() %}
+ {%- when Some(return_type) %}: {{ return_type|ffi_type_name_by_value }}
+ {%- when None %}
+ {%- endmatch %}
+}
+{%- when FfiDefinition::Struct(ffi_struct) %}
+@Structure.FieldOrder({% for field in ffi_struct.fields() %}"{{ field.name()|var_name_raw }}"{% if !loop.last %}, {% endif %}{% endfor %})
+internal open class {{ ffi_struct.name()|ffi_struct_name }}(
+ {%- for field in ffi_struct.fields() %}
+ @JvmField internal var {{ field.name()|var_name }}: {{ field.type_().borrow()|ffi_type_name_for_ffi_struct }} = {{ field.type_()|ffi_default_value }},
+ {%- endfor %}
+) : Structure() {
+ class UniffiByValue(
+ {%- for field in ffi_struct.fields() %}
+ {{ field.name()|var_name }}: {{ field.type_().borrow()|ffi_type_name_for_ffi_struct }} = {{ field.type_()|ffi_default_value }},
+ {%- endfor %}
+ ): {{ ffi_struct.name()|ffi_struct_name }}({%- for field in ffi_struct.fields() %}{{ field.name()|var_name }}, {%- endfor %}), Structure.ByValue
+
+ internal fun uniffiSetValue(other: {{ ffi_struct.name()|ffi_struct_name }}) {
+ {%- for field in ffi_struct.fields() %}
+ {{ field.name()|var_name }} = other.{{ field.name()|var_name }}
+ {%- endfor %}
+ }
+
+}
+{%- when FfiDefinition::Function(_) %}
+{# functions are handled below #}
+{%- endmatch %}
+{%- endfor %}
+
// A JNA Library to expose the extern-C FFI definitions.
// This is an implementation detail which will be called internally by the public API.
-internal interface _UniFFILib : Library {
+internal interface UniffiLib : Library {
companion object {
- internal val INSTANCE: _UniFFILib by lazy {
- loadIndirect<_UniFFILib>(componentName = "{{ ci.namespace() }}")
- .also { lib: _UniFFILib ->
+ internal val INSTANCE: UniffiLib by lazy {
+ loadIndirect<UniffiLib>(componentName = "{{ ci.namespace() }}")
+ .also { lib: UniffiLib ->
uniffiCheckContractApiVersion(lib)
uniffiCheckApiChecksums(lib)
{% for fn in self.initialization_fns() -%}
@@ -28,6 +71,12 @@ internal interface _UniFFILib : Library {
{% endfor -%}
}
}
+ {% if ci.contains_object_types() %}
+ // The Cleaner for the whole library
+ internal val CLEANER: UniffiCleaner by lazy {
+ UniffiCleaner.create()
+ }
+ {%- endif %}
}
{% for func in ci.iter_ffi_function_definitions() -%}
@@ -37,7 +86,7 @@ internal interface _UniFFILib : Library {
{% endfor %}
}
-private fun uniffiCheckContractApiVersion(lib: _UniFFILib) {
+private fun uniffiCheckContractApiVersion(lib: UniffiLib) {
// Get the bindings contract version from our ComponentInterface
val bindings_contract_version = {{ ci.uniffi_contract_version() }}
// Get the scaffolding contract version by calling the into the dylib
@@ -48,7 +97,7 @@ private fun uniffiCheckContractApiVersion(lib: _UniFFILib) {
}
@Suppress("UNUSED_PARAMETER")
-private fun uniffiCheckApiChecksums(lib: _UniFFILib) {
+private fun uniffiCheckApiChecksums(lib: UniffiLib) {
{%- for (name, expected_checksum) in ci.iter_checksums() %}
if (lib.{{ name }}() != {{ expected_checksum }}.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelper.kt
new file mode 100644
index 0000000000..e3e85544d7
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelper.kt
@@ -0,0 +1,40 @@
+
+// The cleaner interface for Object finalization code to run.
+// This is the entry point to any implementation that we're using.
+//
+// The cleaner registers objects and returns cleanables, so now we are
+// defining a `UniffiCleaner` with a `UniffiClenaer.Cleanable` to abstract the
+// different implmentations available at compile time.
+interface UniffiCleaner {
+ interface Cleanable {
+ fun clean()
+ }
+
+ fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable
+
+ companion object
+}
+
+// The fallback Jna cleaner, which is available for both Android, and the JVM.
+private class UniffiJnaCleaner : UniffiCleaner {
+ private val cleaner = com.sun.jna.internal.Cleaner.getCleaner()
+
+ override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable =
+ UniffiJnaCleanable(cleaner.register(value, cleanUpTask))
+}
+
+private class UniffiJnaCleanable(
+ private val cleanable: com.sun.jna.internal.Cleaner.Cleanable,
+) : UniffiCleaner.Cleanable {
+ override fun clean() = cleanable.clean()
+}
+
+// We decide at uniffi binding generation time whether we were
+// using Android or not.
+// There are further runtime checks to chose the correct implementation
+// of the cleaner.
+{% if config.android_cleaner() %}
+{%- include "ObjectCleanerHelperAndroid.kt" %}
+{%- else %}
+{%- include "ObjectCleanerHelperJvm.kt" %}
+{%- endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelperAndroid.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelperAndroid.kt
new file mode 100644
index 0000000000..d025879848
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelperAndroid.kt
@@ -0,0 +1,26 @@
+{{- self.add_import("android.os.Build") }}
+{{- self.add_import("androidx.annotation.RequiresApi") }}
+
+private fun UniffiCleaner.Companion.create(): UniffiCleaner =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ AndroidSystemCleaner()
+ } else {
+ UniffiJnaCleaner()
+ }
+
+// The SystemCleaner, available from API Level 33.
+// Some API Level 33 OSes do not support using it, so we require API Level 34.
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+private class AndroidSystemCleaner : UniffiCleaner {
+ val cleaner = android.system.SystemCleaner.cleaner()
+
+ override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable =
+ AndroidSystemCleanable(cleaner.register(value, cleanUpTask))
+}
+
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+private class AndroidSystemCleanable(
+ private val cleanable: java.lang.ref.Cleaner.Cleanable,
+) : UniffiCleaner.Cleanable {
+ override fun clean() = cleanable.clean()
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelperJvm.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelperJvm.kt
new file mode 100644
index 0000000000..c43bc167fc
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelperJvm.kt
@@ -0,0 +1,25 @@
+private fun UniffiCleaner.Companion.create(): UniffiCleaner =
+ try {
+ // For safety's sake: if the library hasn't been run in android_cleaner = true
+ // mode, but is being run on Android, then we still need to think about
+ // Android API versions.
+ // So we check if java.lang.ref.Cleaner is there, and use that…
+ java.lang.Class.forName("java.lang.ref.Cleaner")
+ JavaLangRefCleaner()
+ } catch (e: ClassNotFoundException) {
+ // … otherwise, fallback to the JNA cleaner.
+ UniffiJnaCleaner()
+ }
+
+private class JavaLangRefCleaner : UniffiCleaner {
+ val cleaner = java.lang.ref.Cleaner.create()
+
+ override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable =
+ JavaLangRefCleanable(cleaner.register(value, cleanUpTask))
+}
+
+private class JavaLangRefCleanable(
+ val cleanable: java.lang.ref.Cleaner.Cleanable
+) : UniffiCleaner.Cleanable {
+ override fun clean() = cleanable.clean()
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectRuntime.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectRuntime.kt
deleted file mode 100644
index b9352c690f..0000000000
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectRuntime.kt
+++ /dev/null
@@ -1,161 +0,0 @@
-// Interface implemented by anything that can contain an object reference.
-//
-// Such types expose a `destroy()` method that must be called to cleanly
-// dispose of the contained objects. Failure to call this method may result
-// in memory leaks.
-//
-// The easiest way to ensure this method is called is to use the `.use`
-// helper method to execute a block and destroy the object at the end.
-interface Disposable {
- fun destroy()
- companion object {
- fun destroy(vararg args: Any?) {
- args.filterIsInstance<Disposable>()
- .forEach(Disposable::destroy)
- }
- }
-}
-
-inline fun <T : Disposable?, R> T.use(block: (T) -> R) =
- try {
- block(this)
- } finally {
- try {
- // N.B. our implementation is on the nullable type `Disposable?`.
- this?.destroy()
- } catch (e: Throwable) {
- // swallow
- }
- }
-
-// The base class for all UniFFI Object types.
-//
-// This class provides core operations for working with the Rust `Arc<T>` pointer to
-// the live Rust struct on the other side of the FFI.
-//
-// There's some subtlety here, because we have to be careful not to operate on a Rust
-// struct after it has been dropped, and because we must expose a public API for freeing
-// the Kotlin wrapper object in lieu of reliable finalizers. The core requirements are:
-//
-// * Each `FFIObject` instance holds an opaque pointer to the underlying Rust struct.
-// Method calls need to read this pointer from the object's state and pass it in to
-// the Rust FFI.
-//
-// * When an `FFIObject` is no longer needed, its pointer should be passed to a
-// special destructor function provided by the Rust FFI, which will drop the
-// underlying Rust struct.
-//
-// * Given an `FFIObject` instance, calling code is expected to call the special
-// `destroy` method in order to free it after use, either by calling it explicitly
-// or by using a higher-level helper like the `use` method. Failing to do so will
-// leak the underlying Rust struct.
-//
-// * We can't assume that calling code will do the right thing, and must be prepared
-// to handle Kotlin method calls executing concurrently with or even after a call to
-// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`.
-//
-// * We must never allow Rust code to operate on the underlying Rust struct after
-// the destructor has been called, and must never call the destructor more than once.
-// Doing so may trigger memory unsafety.
-//
-// If we try to implement this with mutual exclusion on access to the pointer, there is the
-// possibility of a race between a method call and a concurrent call to `destroy`:
-//
-// * Thread A starts a method call, reads the value of the pointer, but is interrupted
-// before it can pass the pointer over the FFI to Rust.
-// * Thread B calls `destroy` and frees the underlying Rust struct.
-// * Thread A resumes, passing the already-read pointer value to Rust and triggering
-// a use-after-free.
-//
-// One possible solution would be to use a `ReadWriteLock`, with each method call taking
-// a read lock (and thus allowed to run concurrently) and the special `destroy` method
-// taking a write lock (and thus blocking on live method calls). However, we aim not to
-// generate methods with any hidden blocking semantics, and a `destroy` method that might
-// block if called incorrectly seems to meet that bar.
-//
-// So, we achieve our goals by giving each `FFIObject` an associated `AtomicLong` counter to track
-// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy`
-// has been called. These are updated according to the following rules:
-//
-// * The initial value of the counter is 1, indicating a live object with no in-flight calls.
-// The initial value for the flag is false.
-//
-// * At the start of each method call, we atomically check the counter.
-// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted.
-// If it is nonzero them we atomically increment it by 1 and proceed with the method call.
-//
-// * At the end of each method call, we atomically decrement and check the counter.
-// If it has reached zero then we destroy the underlying Rust struct.
-//
-// * When `destroy` is called, we atomically flip the flag from false to true.
-// If the flag was already true we silently fail.
-// Otherwise we atomically decrement and check the counter.
-// If it has reached zero then we destroy the underlying Rust struct.
-//
-// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc<T>` works,
-// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`.
-//
-// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been
-// called *and* all in-flight method calls have completed, avoiding violating any of the expectations
-// of the underlying Rust code.
-//
-// In the future we may be able to replace some of this with automatic finalization logic, such as using
-// the new "Cleaner" functionaility in Java 9. The above scheme has been designed to work even if `destroy` is
-// invoked by garbage-collection machinery rather than by calling code (which by the way, it's apparently also
-// possible for the JVM to finalize an object while there is an in-flight call to one of its methods [1],
-// so there would still be some complexity here).
-//
-// Sigh...all of this for want of a robust finalization mechanism.
-//
-// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219
-//
-abstract class FFIObject(
- protected val pointer: Pointer
-): Disposable, AutoCloseable {
-
- private val wasDestroyed = AtomicBoolean(false)
- private val callCounter = AtomicLong(1)
-
- open protected fun freeRustArcPtr() {
- // To be overridden in subclasses.
- }
-
- override fun destroy() {
- // Only allow a single call to this method.
- // TODO: maybe we should log a warning if called more than once?
- if (this.wasDestroyed.compareAndSet(false, true)) {
- // This decrement always matches the initial count of 1 given at creation time.
- if (this.callCounter.decrementAndGet() == 0L) {
- this.freeRustArcPtr()
- }
- }
- }
-
- @Synchronized
- override fun close() {
- this.destroy()
- }
-
- internal inline fun <R> callWithPointer(block: (ptr: Pointer) -> R): R {
- // Check and increment the call counter, to keep the object alive.
- // This needs a compare-and-set retry loop in case of concurrent updates.
- do {
- val c = this.callCounter.get()
- if (c == 0L) {
- throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed")
- }
- if (c == Long.MAX_VALUE) {
- throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow")
- }
- } while (! this.callCounter.compareAndSet(c, c + 1L))
- // Now we can safely do the method call without the pointer being freed concurrently.
- try {
- return block(this.pointer)
- } finally {
- // This decrement always matches the increment we performed above.
- if (this.callCounter.decrementAndGet() == 0L) {
- this.freeRustArcPtr()
- }
- }
- }
-}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt
index 8ce27a5d04..62cac7a4d0 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt
@@ -1,125 +1,282 @@
+// This template implements a class for working with a Rust struct via a Pointer/Arc<T>
+// to the live Rust struct on the other side of the FFI.
+//
+// Each instance implements core operations for working with the Rust `Arc<T>` and the
+// Kotlin Pointer to work with the live Rust struct on the other side of the FFI.
+//
+// There's some subtlety here, because we have to be careful not to operate on a Rust
+// struct after it has been dropped, and because we must expose a public API for freeing
+// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are:
+//
+// * Each instance holds an opaque pointer to the underlying Rust struct.
+// Method calls need to read this pointer from the object's state and pass it in to
+// the Rust FFI.
+//
+// * When an instance is no longer needed, its pointer should be passed to a
+// special destructor function provided by the Rust FFI, which will drop the
+// underlying Rust struct.
+//
+// * Given an instance, calling code is expected to call the special
+// `destroy` method in order to free it after use, either by calling it explicitly
+// or by using a higher-level helper like the `use` method. Failing to do so risks
+// leaking the underlying Rust struct.
+//
+// * We can't assume that calling code will do the right thing, and must be prepared
+// to handle Kotlin method calls executing concurrently with or even after a call to
+// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`.
+//
+// * We must never allow Rust code to operate on the underlying Rust struct after
+// the destructor has been called, and must never call the destructor more than once.
+// Doing so may trigger memory unsafety.
+//
+// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner`
+// is implemented to call the destructor when the Kotlin object becomes unreachable.
+// This is done in a background thread. This is not a panacea, and client code should be aware that
+// 1. the thread may starve if some there are objects that have poorly performing
+// `drop` methods or do significant work in their `drop` methods.
+// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`,
+// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html).
+//
+// If we try to implement this with mutual exclusion on access to the pointer, there is the
+// possibility of a race between a method call and a concurrent call to `destroy`:
+//
+// * Thread A starts a method call, reads the value of the pointer, but is interrupted
+// before it can pass the pointer over the FFI to Rust.
+// * Thread B calls `destroy` and frees the underlying Rust struct.
+// * Thread A resumes, passing the already-read pointer value to Rust and triggering
+// a use-after-free.
+//
+// One possible solution would be to use a `ReadWriteLock`, with each method call taking
+// a read lock (and thus allowed to run concurrently) and the special `destroy` method
+// taking a write lock (and thus blocking on live method calls). However, we aim not to
+// generate methods with any hidden blocking semantics, and a `destroy` method that might
+// block if called incorrectly seems to meet that bar.
+//
+// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track
+// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy`
+// has been called. These are updated according to the following rules:
+//
+// * The initial value of the counter is 1, indicating a live object with no in-flight calls.
+// The initial value for the flag is false.
+//
+// * At the start of each method call, we atomically check the counter.
+// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted.
+// If it is nonzero them we atomically increment it by 1 and proceed with the method call.
+//
+// * At the end of each method call, we atomically decrement and check the counter.
+// If it has reached zero then we destroy the underlying Rust struct.
+//
+// * When `destroy` is called, we atomically flip the flag from false to true.
+// If the flag was already true we silently fail.
+// Otherwise we atomically decrement and check the counter.
+// If it has reached zero then we destroy the underlying Rust struct.
+//
+// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc<T>` works,
+// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`.
+//
+// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been
+// called *and* all in-flight method calls have completed, avoiding violating any of the expectations
+// of the underlying Rust code.
+//
+// This makes a cleaner a better alternative to _not_ calling `destroy()` as
+// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop`
+// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner
+// thread may be starved, and the app will leak memory.
+//
+// In this case, `destroy`ing manually may be a better solution.
+//
+// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects
+// with Rust peers are reclaimed:
+//
+// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen:
+// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then:
+// 3. The memory is reclaimed when the process terminates.
+//
+// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219
+//
+
+{{ self.add_import("java.util.concurrent.atomic.AtomicBoolean") }}
+{%- if self.include_once_check("interface-support") %}
+ {%- include "ObjectCleanerHelper.kt" %}
+{%- endif %}
+
{%- let obj = ci|get_object_definition(name) %}
-{%- if self.include_once_check("ObjectRuntime.kt") %}{% include "ObjectRuntime.kt" %}{% endif %}
-{{- self.add_import("java.util.concurrent.atomic.AtomicLong") }}
-{{- self.add_import("java.util.concurrent.atomic.AtomicBoolean") }}
+{%- let (interface_name, impl_class_name) = obj|object_names(ci) %}
+{%- let methods = obj.methods() %}
+{%- let interface_docstring = obj.docstring() %}
+{%- let is_error = ci.is_name_used_as_error(name) %}
+{%- let ffi_converter_name = obj|ffi_converter_name %}
-public interface {{ type_name }}Interface {
- {% for meth in obj.methods() -%}
- {%- match meth.throws_type() -%}
- {%- when Some with (throwable) -%}
- @Throws({{ throwable|type_name(ci) }}::class)
- {%- when None -%}
- {%- endmatch %}
- {% if meth.is_async() -%}
- suspend fun {{ meth.name()|fn_name }}({% call kt::arg_list_decl(meth) %})
- {%- else -%}
- fun {{ meth.name()|fn_name }}({% call kt::arg_list_decl(meth) %})
- {%- endif %}
- {%- match meth.return_type() -%}
- {%- when Some with (return_type) %}: {{ return_type|type_name(ci) -}}
- {%- when None -%}
- {%- endmatch -%}
+{%- include "Interface.kt" %}
- {% endfor %}
- companion object
-}
+{%- call kt::docstring(obj, 0) %}
+{% if (is_error) %}
+open class {{ impl_class_name }} : Exception, Disposable, AutoCloseable, {{ interface_name }} {
+{% else -%}
+open class {{ impl_class_name }}: Disposable, AutoCloseable, {{ interface_name }} {
+{%- endif %}
-class {{ type_name }}(
- pointer: Pointer
-) : FFIObject(pointer), {{ type_name }}Interface {
+ constructor(pointer: Pointer) {
+ this.pointer = pointer
+ this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer))
+ }
+
+ /**
+ * This constructor can be used to instantiate a fake object. Only used for tests. Any
+ * attempt to actually use an object constructed this way will fail as there is no
+ * connected Rust object.
+ */
+ @Suppress("UNUSED_PARAMETER")
+ constructor(noPointer: NoPointer) {
+ this.pointer = null
+ this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer))
+ }
{%- match obj.primary_constructor() %}
- {%- when Some with (cons) %}
- constructor({% call kt::arg_list_decl(cons) -%}) :
+ {%- when Some(cons) %}
+ {%- if cons.is_async() %}
+ // Note no constructor generated for this object as it is async.
+ {%- else %}
+ {%- call kt::docstring(cons, 4) %}
+ constructor({% call kt::arg_list(cons, true) -%}) :
this({% call kt::to_ffi_call(cons) %})
+ {%- endif %}
{%- when None %}
{%- endmatch %}
- /**
- * Disconnect the object from the underlying Rust object.
- *
- * It can be called more than once, but once called, interacting with the object
- * causes an `IllegalStateException`.
- *
- * Clients **must** call this method once done with the object, or cause a memory leak.
- */
- override protected fun freeRustArcPtr() {
- rustCall() { status ->
- _UniFFILib.INSTANCE.{{ obj.ffi_object_free().name() }}(this.pointer, status)
+ protected val pointer: Pointer?
+ protected val cleanable: UniffiCleaner.Cleanable
+
+ private val wasDestroyed = AtomicBoolean(false)
+ private val callCounter = AtomicLong(1)
+
+ override fun destroy() {
+ // Only allow a single call to this method.
+ // TODO: maybe we should log a warning if called more than once?
+ if (this.wasDestroyed.compareAndSet(false, true)) {
+ // This decrement always matches the initial count of 1 given at creation time.
+ if (this.callCounter.decrementAndGet() == 0L) {
+ cleanable.clean()
+ }
}
}
- {% for meth in obj.methods() -%}
- {%- match meth.throws_type() -%}
- {%- when Some with (throwable) %}
- @Throws({{ throwable|type_name(ci) }}::class)
- {%- else -%}
- {%- endmatch -%}
- {%- if meth.is_async() %}
- @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
- override suspend fun {{ meth.name()|fn_name }}({%- call kt::arg_list_decl(meth) -%}){% match meth.return_type() %}{% when Some with (return_type) %} : {{ return_type|type_name(ci) }}{% when None %}{%- endmatch %} {
- return uniffiRustCallAsync(
- callWithPointer { thisPtr ->
- _UniFFILib.INSTANCE.{{ meth.ffi_func().name() }}(
- thisPtr,
- {% call kt::arg_list_lowered(meth) %}
- )
- },
- {{ meth|async_poll(ci) }},
- {{ meth|async_complete(ci) }},
- {{ meth|async_free(ci) }},
- // lift function
- {%- match meth.return_type() %}
- {%- when Some(return_type) %}
- { {{ return_type|lift_fn }}(it) },
- {%- when None %}
- { Unit },
- {% endmatch %}
- // Error FFI converter
- {%- match meth.throws_type() %}
- {%- when Some(e) %}
- {{ e|type_name(ci) }}.ErrorHandler,
- {%- when None %}
- NullCallStatusErrorHandler,
- {%- endmatch %}
- )
- }
- {%- else -%}
- {%- match meth.return_type() -%}
- {%- when Some with (return_type) -%}
- override fun {{ meth.name()|fn_name }}({% call kt::arg_list_protocol(meth) %}): {{ return_type|type_name(ci) }} =
- callWithPointer {
- {%- call kt::to_ffi_call_with_prefix("it", meth) %}
- }.let {
- {{ return_type|lift_fn }}(it)
+ @Synchronized
+ override fun close() {
+ this.destroy()
+ }
+
+ internal inline fun <R> callWithPointer(block: (ptr: Pointer) -> R): R {
+ // Check and increment the call counter, to keep the object alive.
+ // This needs a compare-and-set retry loop in case of concurrent updates.
+ do {
+ val c = this.callCounter.get()
+ if (c == 0L) {
+ throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed")
+ }
+ if (c == Long.MAX_VALUE) {
+ throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow")
+ }
+ } while (! this.callCounter.compareAndSet(c, c + 1L))
+ // Now we can safely do the method call without the pointer being freed concurrently.
+ try {
+ return block(this.uniffiClonePointer())
+ } finally {
+ // This decrement always matches the increment we performed above.
+ if (this.callCounter.decrementAndGet() == 0L) {
+ cleanable.clean()
+ }
}
+ }
- {%- when None -%}
- override fun {{ meth.name()|fn_name }}({% call kt::arg_list_protocol(meth) %}) =
- callWithPointer {
- {%- call kt::to_ffi_call_with_prefix("it", meth) %}
+ // Use a static inner class instead of a closure so as not to accidentally
+ // capture `this` as part of the cleanable's action.
+ private class UniffiCleanAction(private val pointer: Pointer?) : Runnable {
+ override fun run() {
+ pointer?.let { ptr ->
+ uniffiRustCall { status ->
+ UniffiLib.INSTANCE.{{ obj.ffi_object_free().name() }}(ptr, status)
+ }
+ }
}
- {% endmatch %}
- {% endif %}
+ }
+
+ fun uniffiClonePointer(): Pointer {
+ return uniffiRustCall() { status ->
+ UniffiLib.INSTANCE.{{ obj.ffi_object_clone().name() }}(pointer!!, status)
+ }
+ }
+
+ {% for meth in obj.methods() -%}
+ {%- call kt::func_decl("override", meth, 4) %}
{% endfor %}
+ {%- for tm in obj.uniffi_traits() %}
+ {%- match tm %}
+ {% when UniffiTrait::Display { fmt } %}
+ override fun toString(): String {
+ return {{ fmt.return_type().unwrap()|lift_fn }}({% call kt::to_ffi_call(fmt) %})
+ }
+ {% when UniffiTrait::Eq { eq, ne } %}
+ {# only equals used #}
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is {{ impl_class_name}}) return false
+ return {{ eq.return_type().unwrap()|lift_fn }}({% call kt::to_ffi_call(eq) %})
+ }
+ {% when UniffiTrait::Hash { hash } %}
+ override fun hashCode(): Int {
+ return {{ hash.return_type().unwrap()|lift_fn }}({%- call kt::to_ffi_call(hash) %}).toInt()
+ }
+ {%- else %}
+ {%- endmatch %}
+ {%- endfor %}
+
+ {# XXX - "companion object" confusion? How to have alternate constructors *and* be an error? #}
{% if !obj.alternate_constructors().is_empty() -%}
companion object {
{% for cons in obj.alternate_constructors() -%}
- fun {{ cons.name()|fn_name }}({% call kt::arg_list_decl(cons) %}): {{ type_name }} =
- {{ type_name }}({% call kt::to_ffi_call(cons) %})
+ {% call kt::func_decl("", cons, 4) %}
{% endfor %}
}
+ {% else if is_error %}
+ companion object ErrorHandler : UniffiRustCallStatusErrorHandler<{{ impl_class_name }}> {
+ override fun lift(error_buf: RustBuffer.ByValue): {{ impl_class_name }} {
+ // Due to some mismatches in the ffi converter mechanisms, errors are a RustBuffer.
+ val bb = error_buf.asByteBuffer()
+ if (bb == null) {
+ throw InternalException("?")
+ }
+ return {{ ffi_converter_name }}.read(bb)
+ }
+ }
{% else %}
companion object
{% endif %}
}
-public object {{ obj|ffi_converter_name }}: FfiConverter<{{ type_name }}, Pointer> {
- override fun lower(value: {{ type_name }}): Pointer = value.callWithPointer { it }
+{%- if obj.has_callback_interface() %}
+{%- let vtable = obj.vtable().expect("trait interface should have a vtable") %}
+{%- let vtable_methods = obj.vtable_methods() %}
+{%- let ffi_init_callback = obj.ffi_init_callback() %}
+{% include "CallbackInterfaceImpl.kt" %}
+{%- endif %}
+
+public object {{ ffi_converter_name }}: FfiConverter<{{ type_name }}, Pointer> {
+ {%- if obj.has_callback_interface() %}
+ internal val handleMap = UniffiHandleMap<{{ type_name }}>()
+ {%- endif %}
+
+ override fun lower(value: {{ type_name }}): Pointer {
+ {%- if obj.has_callback_interface() %}
+ return Pointer(handleMap.insert(value))
+ {%- else %}
+ return value.uniffiClonePointer()
+ {%- endif %}
+ }
override fun lift(value: Pointer): {{ type_name }} {
- return {{ type_name }}(value)
+ return {{ impl_class_name }}(value)
}
override fun read(buf: ByteBuffer): {{ type_name }} {
@@ -128,7 +285,7 @@ public object {{ obj|ffi_converter_name }}: FfiConverter<{{ type_name }}, Pointe
return lift(Pointer(buf.getLong()))
}
- override fun allocationSize(value: {{ type_name }}) = 8
+ override fun allocationSize(value: {{ type_name }}) = 8UL
override fun write(value: {{ type_name }}, buf: ByteBuffer) {
// The Rust code always expects pointers written as 8 bytes,
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/OptionalTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/OptionalTemplate.kt
index 56cb5f87a5..98451e1451 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/OptionalTemplate.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/OptionalTemplate.kt
@@ -8,11 +8,11 @@ public object {{ ffi_converter_name }}: FfiConverterRustBuffer<{{ inner_type_nam
return {{ inner_type|read_fn }}(buf)
}
- override fun allocationSize(value: {{ inner_type_name }}?): Int {
+ override fun allocationSize(value: {{ inner_type_name }}?): ULong {
if (value == null) {
- return 1
+ return 1UL
} else {
- return 1 + {{ inner_type|allocation_size_fn }}(value)
+ return 1UL + {{ inner_type|allocation_size_fn }}(value)
}
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/README.md b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/README.md
new file mode 100644
index 0000000000..0e770cb829
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/README.md
@@ -0,0 +1,13 @@
+# Rules for the Kotlin template code
+
+## Naming
+
+Private variables, classes, functions, etc. should be prefixed with `uniffi`, `Uniffi`, or `UNIFFI`.
+This avoids naming collisions with user-defined items.
+Users will not get name collisions as long as they don't use "uniffi", which is reserved for us.
+
+In particular, make sure to use the `uniffi` prefix for any variable names in generated functions.
+If you name a variable something like `result` the code will probably work initially.
+Then it will break later on when a user decides to define a function with a parameter named `result`.
+
+Note: this doesn't apply to items that we want to expose, for example users may want to catch `InternalException` so doesn't get the `Uniffi` prefix.
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt
index b588ca1398..bc3028c736 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt
@@ -1,8 +1,11 @@
{%- let rec = ci|get_record_definition(name) %}
+{%- if rec.has_fields() %}
+{%- call kt::docstring(rec, 0) %}
data class {{ type_name }} (
{%- for field in rec.fields() %}
- var {{ field.name()|var_name }}: {{ field|type_name(ci) -}}
+ {%- call kt::docstring(field, 4) %}
+ {% if config.generate_immutable_records() %}val{% else %}var{% endif %} {{ field.name()|var_name }}: {{ field|type_name(ci) -}}
{%- match field.default_value() %}
{%- when Some with(literal) %} = {{ literal|render_literal(field, ci) }}
{%- else %}
@@ -18,21 +21,39 @@ data class {{ type_name }} (
{% endif %}
companion object
}
+{%- else -%}
+{%- call kt::docstring(rec, 0) %}
+class {{ type_name }} {
+ override fun equals(other: Any?): Boolean {
+ return other is {{ type_name }}
+ }
+
+ override fun hashCode(): Int {
+ return javaClass.hashCode()
+ }
+
+ companion object
+}
+{%- endif %}
public object {{ rec|ffi_converter_name }}: FfiConverterRustBuffer<{{ type_name }}> {
override fun read(buf: ByteBuffer): {{ type_name }} {
+ {%- if rec.has_fields() %}
return {{ type_name }}(
{%- for field in rec.fields() %}
{{ field|read_fn }}(buf),
{%- endfor %}
)
+ {%- else %}
+ return {{ type_name }}()
+ {%- endif %}
}
- override fun allocationSize(value: {{ type_name }}) = (
+ override fun allocationSize(value: {{ type_name }}) = {%- if rec.has_fields() %} (
{%- for field in rec.fields() %}
- {{ field|allocation_size_fn }}(value.{{ field.name()|var_name }}){% if !loop.last %} +{% endif%}
+ {{ field|allocation_size_fn }}(value.{{ field.name()|var_name }}){% if !loop.last %} +{% endif %}
{%- endfor %}
- )
+ ) {%- else %} 0UL {%- endif %}
override fun write(value: {{ type_name }}, buf: ByteBuffer) {
{%- for field in rec.fields() %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/RustBufferTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/RustBufferTemplate.kt
index dfbea24074..b28f25bfc3 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/RustBufferTemplate.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/RustBufferTemplate.kt
@@ -4,32 +4,41 @@
@Structure.FieldOrder("capacity", "len", "data")
open class RustBuffer : Structure() {
- @JvmField var capacity: Int = 0
- @JvmField var len: Int = 0
+ // Note: `capacity` and `len` are actually `ULong` values, but JVM only supports signed values.
+ // When dealing with these fields, make sure to call `toULong()`.
+ @JvmField var capacity: Long = 0
+ @JvmField var len: Long = 0
@JvmField var data: Pointer? = null
class ByValue: RustBuffer(), Structure.ByValue
class ByReference: RustBuffer(), Structure.ByReference
+ internal fun setValue(other: RustBuffer) {
+ capacity = other.capacity
+ len = other.len
+ data = other.data
+ }
+
companion object {
- internal fun alloc(size: Int = 0) = rustCall() { status ->
- _UniFFILib.INSTANCE.{{ ci.ffi_rustbuffer_alloc().name() }}(size, status)
+ internal fun alloc(size: ULong = 0UL) = uniffiRustCall() { status ->
+ // Note: need to convert the size to a `Long` value to make this work with JVM.
+ UniffiLib.INSTANCE.{{ ci.ffi_rustbuffer_alloc().name() }}(size.toLong(), status)
}.also {
if(it.data == null) {
throw RuntimeException("RustBuffer.alloc() returned null data pointer (size=${size})")
}
}
- internal fun create(capacity: Int, len: Int, data: Pointer?): RustBuffer.ByValue {
+ internal fun create(capacity: ULong, len: ULong, data: Pointer?): RustBuffer.ByValue {
var buf = RustBuffer.ByValue()
- buf.capacity = capacity
- buf.len = len
+ buf.capacity = capacity.toLong()
+ buf.len = len.toLong()
buf.data = data
return buf
}
- internal fun free(buf: RustBuffer.ByValue) = rustCall() { status ->
- _UniFFILib.INSTANCE.{{ ci.ffi_rustbuffer_free().name() }}(buf, status)
+ internal fun free(buf: RustBuffer.ByValue) = uniffiRustCall() { status ->
+ UniffiLib.INSTANCE.{{ ci.ffi_rustbuffer_free().name() }}(buf, status)
}
}
@@ -53,9 +62,9 @@ class RustBufferByReference : ByReference(16) {
fun setValue(value: RustBuffer.ByValue) {
// NOTE: The offsets are as they are in the C-like struct.
val pointer = getPointer()
- pointer.setInt(0, value.capacity)
- pointer.setInt(4, value.len)
- pointer.setPointer(8, value.data)
+ pointer.setLong(0, value.capacity)
+ pointer.setLong(8, value.len)
+ pointer.setPointer(16, value.data)
}
/**
@@ -64,9 +73,9 @@ class RustBufferByReference : ByReference(16) {
fun getValue(): RustBuffer.ByValue {
val pointer = getPointer()
val value = RustBuffer.ByValue()
- value.writeField("capacity", pointer.getInt(0))
- value.writeField("len", pointer.getInt(4))
- value.writeField("data", pointer.getPointer(8))
+ value.writeField("capacity", pointer.getLong(0))
+ value.writeField("len", pointer.getLong(8))
+ value.writeField("data", pointer.getLong(16))
return value
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/SequenceTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/SequenceTemplate.kt
index 876d1bc05e..61f911cb0c 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/SequenceTemplate.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/SequenceTemplate.kt
@@ -8,15 +8,15 @@ public object {{ ffi_converter_name }}: FfiConverterRustBuffer<List<{{ inner_typ
}
}
- override fun allocationSize(value: List<{{ inner_type_name }}>): Int {
- val sizeForLength = 4
+ override fun allocationSize(value: List<{{ inner_type_name }}>): ULong {
+ val sizeForLength = 4UL
val sizeForItems = value.map { {{ inner_type|allocation_size_fn }}(it) }.sum()
return sizeForLength + sizeForItems
}
override fun write(value: List<{{ inner_type_name }}>, buf: ByteBuffer) {
buf.putInt(value.size)
- value.forEach {
+ value.iterator().forEach {
{{ inner_type|write_fn }}(it, buf)
}
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/StringHelper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/StringHelper.kt
index 68324be4f9..b67435bd1a 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/StringHelper.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/StringHelper.kt
@@ -4,7 +4,7 @@ public object FfiConverterString: FfiConverter<String, RustBuffer.ByValue> {
// store our length and avoid writing it out to the buffer.
override fun lift(value: RustBuffer.ByValue): String {
try {
- val byteArr = ByteArray(value.len)
+ val byteArr = ByteArray(value.len.toInt())
value.asByteBuffer()!!.get(byteArr)
return byteArr.toString(Charsets.UTF_8)
} finally {
@@ -31,7 +31,7 @@ public object FfiConverterString: FfiConverter<String, RustBuffer.ByValue> {
val byteBuf = toUtf8(value)
// Ideally we'd pass these bytes to `ffi_bytebuffer_from_bytes`, but doing so would require us
// to copy them into a JNA `Memory`. So we might as well directly copy them into a `RustBuffer`.
- val rbuf = RustBuffer.alloc(byteBuf.limit())
+ val rbuf = RustBuffer.alloc(byteBuf.limit().toULong())
rbuf.asByteBuffer()!!.put(byteBuf)
return rbuf
}
@@ -39,9 +39,9 @@ public object FfiConverterString: FfiConverter<String, RustBuffer.ByValue> {
// We aren't sure exactly how many bytes our string will be once it's UTF-8
// encoded. Allocate 3 bytes per UTF-16 code unit which will always be
// enough.
- override fun allocationSize(value: String): Int {
- val sizeForLength = 4
- val sizeForString = value.length * 3
+ override fun allocationSize(value: String): ULong {
+ val sizeForLength = 4UL
+ val sizeForString = value.length.toULong() * 3UL
return sizeForLength + sizeForString
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/TimestampHelper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/TimestampHelper.kt
index 21069d7ce8..10a450a4bd 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/TimestampHelper.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/TimestampHelper.kt
@@ -14,7 +14,7 @@ public object FfiConverterTimestamp: FfiConverterRustBuffer<java.time.Instant> {
}
// 8 bytes for seconds, 4 bytes for nanoseconds
- override fun allocationSize(value: java.time.Instant) = 12
+ override fun allocationSize(value: java.time.Instant) = 12UL
override fun write(value: java.time.Instant, buf: ByteBuffer) {
var epochOffset = java.time.Duration.between(java.time.Instant.EPOCH, value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt
index 6a841d3484..681c48093a 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt
@@ -1,51 +1 @@
-{%- if func.is_async() %}
-{%- match func.throws_type() -%}
-{%- when Some with (throwable) %}
-@Throws({{ throwable|type_name(ci) }}::class)
-{%- else -%}
-{%- endmatch %}
-
-@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
-suspend fun {{ func.name()|fn_name }}({%- call kt::arg_list_decl(func) -%}){% match func.return_type() %}{% when Some with (return_type) %} : {{ return_type|type_name(ci) }}{% when None %}{%- endmatch %} {
- return uniffiRustCallAsync(
- _UniFFILib.INSTANCE.{{ func.ffi_func().name() }}({% call kt::arg_list_lowered(func) %}),
- {{ func|async_poll(ci) }},
- {{ func|async_complete(ci) }},
- {{ func|async_free(ci) }},
- // lift function
- {%- match func.return_type() %}
- {%- when Some(return_type) %}
- { {{ return_type|lift_fn }}(it) },
- {%- when None %}
- { Unit },
- {% endmatch %}
- // Error FFI converter
- {%- match func.throws_type() %}
- {%- when Some(e) %}
- {{ e|type_name(ci) }}.ErrorHandler,
- {%- when None %}
- NullCallStatusErrorHandler,
- {%- endmatch %}
- )
-}
-
-{%- else %}
-{%- match func.throws_type() -%}
-{%- when Some with (throwable) %}
-@Throws({{ throwable|type_name(ci) }}::class)
-{%- else -%}
-{%- endmatch -%}
-
-{%- match func.return_type() -%}
-{%- when Some with (return_type) %}
-
-fun {{ func.name()|fn_name }}({%- call kt::arg_list_decl(func) -%}): {{ return_type|type_name(ci) }} {
- return {{ return_type|lift_fn }}({% call kt::to_ffi_call(func) %})
-}
-{% when None %}
-
-fun {{ func.name()|fn_name }}({% call kt::arg_list_decl(func) %}) =
- {% call kt::to_ffi_call(func) %}
-
-{% endmatch %}
-{%- endif %}
+{%- call kt::func_decl("", func, 8) %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt
index 103d444ea3..c27121b701 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt
@@ -1,5 +1,38 @@
{%- import "macros.kt" as kt %}
+// Interface implemented by anything that can contain an object reference.
+//
+// Such types expose a `destroy()` method that must be called to cleanly
+// dispose of the contained objects. Failure to call this method may result
+// in memory leaks.
+//
+// The easiest way to ensure this method is called is to use the `.use`
+// helper method to execute a block and destroy the object at the end.
+interface Disposable {
+ fun destroy()
+ companion object {
+ fun destroy(vararg args: Any?) {
+ args.filterIsInstance<Disposable>()
+ .forEach(Disposable::destroy)
+ }
+ }
+}
+
+inline fun <T : Disposable?, R> T.use(block: (T) -> R) =
+ try {
+ block(this)
+ } finally {
+ try {
+ // N.B. our implementation is on the nullable type `Disposable?`.
+ this?.destroy()
+ } catch (e: Throwable) {
+ // swallow
+ }
+ }
+
+/** Used to instantiate an interface without an actual pointer, for fakes in tests, mostly. */
+object NoPointer
+
{%- for type_ in ci.iter_types() %}
{%- let type_name = type_|type_name(ci) %}
{%- let ffi_converter_name = type_|ffi_converter_name %}
@@ -82,9 +115,6 @@
{%- when Type::CallbackInterface { module_path, name } %}
{% include "CallbackInterfaceTemplate.kt" %}
-{%- when Type::ForeignExecutor %}
-{% include "ForeignExecutorTemplate.kt" %}
-
{%- when Type::Timestamp %}
{% include "TimestampHelper.kt" %}
@@ -104,6 +134,10 @@
{%- if ci.has_async_fns() %}
{# Import types needed for async support #}
{{ self.add_import("kotlin.coroutines.resume") }}
+{{ self.add_import("kotlinx.coroutines.launch") }}
{{ self.add_import("kotlinx.coroutines.suspendCancellableCoroutine") }}
{{ self.add_import("kotlinx.coroutines.CancellableContinuation") }}
+{{ self.add_import("kotlinx.coroutines.DelicateCoroutinesApi") }}
+{{ self.add_import("kotlinx.coroutines.Job") }}
+{{ self.add_import("kotlinx.coroutines.GlobalScope") }}
{%- endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt16Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt16Helper.kt
index 279a8fa91b..b179145b62 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt16Helper.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt16Helper.kt
@@ -11,7 +11,7 @@ public object FfiConverterUShort: FfiConverter<UShort, Short> {
return value.toShort()
}
- override fun allocationSize(value: UShort) = 2
+ override fun allocationSize(value: UShort) = 2UL
override fun write(value: UShort, buf: ByteBuffer) {
buf.putShort(value.toShort())
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt32Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt32Helper.kt
index da7b5b28d6..202d5bcd5b 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt32Helper.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt32Helper.kt
@@ -11,7 +11,7 @@ public object FfiConverterUInt: FfiConverter<UInt, Int> {
return value.toInt()
}
- override fun allocationSize(value: UInt) = 4
+ override fun allocationSize(value: UInt) = 4UL
override fun write(value: UInt, buf: ByteBuffer) {
buf.putInt(value.toInt())
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt64Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt64Helper.kt
index 44d27ad36b..9be2a5a69d 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt64Helper.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt64Helper.kt
@@ -11,7 +11,7 @@ public object FfiConverterULong: FfiConverter<ULong, Long> {
return value.toLong()
}
- override fun allocationSize(value: ULong) = 8
+ override fun allocationSize(value: ULong) = 8UL
override fun write(value: ULong, buf: ByteBuffer) {
buf.putLong(value.toLong())
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt8Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt8Helper.kt
index b6d176603e..ee360673e0 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt8Helper.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt8Helper.kt
@@ -11,7 +11,7 @@ public object FfiConverterUByte: FfiConverter<UByte, Byte> {
return value.toByte()
}
- override fun allocationSize(value: UByte) = 1
+ override fun allocationSize(value: UByte) = 1UL
override fun write(value: UByte, buf: ByteBuffer) {
buf.put(value.toByte())
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/macros.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/macros.kt
index 6a95d6a66d..7acfdc8861 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/macros.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/macros.kt
@@ -1,32 +1,91 @@
{#
// Template to call into rust. Used in several places.
-// Variable names in `arg_list_decl` should match up with arg lists
+// Variable names in `arg_list` should match up with arg lists
// passed to rust via `arg_list_lowered`
#}
{%- macro to_ffi_call(func) -%}
- {%- match func.throws_type() %}
- {%- when Some with (e) %}
- rustCallWithError({{ e|type_name(ci) }})
- {%- else %}
- rustCall()
- {%- endmatch %} { _status ->
- _UniFFILib.INSTANCE.{{ func.ffi_func().name() }}({% call arg_list_lowered(func) -%} _status)
-}
-{%- endmacro -%}
+ {%- if func.takes_self() %}
+ callWithPointer {
+ {%- call to_raw_ffi_call(func) %}
+ }
+ {% else %}
+ {%- call to_raw_ffi_call(func) %}
+ {% endif %}
+{%- endmacro %}
-{%- macro to_ffi_call_with_prefix(prefix, func) %}
+{%- macro to_raw_ffi_call(func) -%}
{%- match func.throws_type() %}
{%- when Some with (e) %}
- rustCallWithError({{ e|type_name(ci) }})
+ uniffiRustCallWithError({{ e|type_name(ci) }})
{%- else %}
- rustCall()
+ uniffiRustCall()
{%- endmatch %} { _status ->
- _UniFFILib.INSTANCE.{{ func.ffi_func().name() }}(
- {{- prefix }},
- {% call arg_list_lowered(func) %}
+ UniffiLib.INSTANCE.{{ func.ffi_func().name() }}(
+ {% if func.takes_self() %}it, {% endif -%}
+ {% call arg_list_lowered(func) -%}
_status)
}
+{%- endmacro -%}
+
+{%- macro func_decl(func_decl, callable, indent) %}
+ {%- call docstring(callable, indent) %}
+ {%- match callable.throws_type() -%}
+ {%- when Some(throwable) %}
+ @Throws({{ throwable|type_name(ci) }}::class)
+ {%- else -%}
+ {%- endmatch -%}
+ {%- if callable.is_async() %}
+ @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
+ {{ func_decl }} suspend fun {{ callable.name()|fn_name }}(
+ {%- call arg_list(callable, !callable.takes_self()) -%}
+ ){% match callable.return_type() %}{% when Some with (return_type) %} : {{ return_type|type_name(ci) }}{% when None %}{%- endmatch %} {
+ return {% call call_async(callable) %}
+ }
+ {%- else -%}
+ {{ func_decl }} fun {{ callable.name()|fn_name }}(
+ {%- call arg_list(callable, !callable.takes_self()) -%}
+ ){%- match callable.return_type() -%}
+ {%- when Some with (return_type) -%}
+ : {{ return_type|type_name(ci) }} {
+ return {{ return_type|lift_fn }}({% call to_ffi_call(callable) %})
+ }
+ {%- when None %}
+ = {% call to_ffi_call(callable) %}
+ {%- endmatch %}
+ {% endif %}
+{% endmacro %}
+
+{%- macro call_async(callable) -%}
+ uniffiRustCallAsync(
+{%- if callable.takes_self() %}
+ callWithPointer { thisPtr ->
+ UniffiLib.INSTANCE.{{ callable.ffi_func().name() }}(
+ thisPtr,
+ {% call arg_list_lowered(callable) %}
+ )
+ },
+{%- else %}
+ UniffiLib.INSTANCE.{{ callable.ffi_func().name() }}({% call arg_list_lowered(callable) %}),
+{%- endif %}
+ {{ callable|async_poll(ci) }},
+ {{ callable|async_complete(ci) }},
+ {{ callable|async_free(ci) }},
+ // lift function
+ {%- match callable.return_type() %}
+ {%- when Some(return_type) %}
+ { {{ return_type|lift_fn }}(it) },
+ {%- when None %}
+ { Unit },
+ {% endmatch %}
+ // Error FFI converter
+ {%- match callable.throws_type() %}
+ {%- when Some(e) %}
+ {{ e|type_name(ci) }}.ErrorHandler,
+ {%- when None %}
+ UniffiNullRustCallStatusErrorHandler,
+ {%- endmatch %}
+ )
{%- endmacro %}
{%- macro arg_list_lowered(func) %}
@@ -37,37 +96,42 @@
{#-
// Arglist as used in kotlin declarations of methods, functions and constructors.
+// If is_decl, then default values be specified.
// Note the var_name and type_name filters.
-#}
-{% macro arg_list_decl(func) %}
- {%- for arg in func.arguments() -%}
+{% macro arg_list(func, is_decl) %}
+{%- for arg in func.arguments() -%}
{{ arg.name()|var_name }}: {{ arg|type_name(ci) }}
- {%- match arg.default_value() %}
- {%- when Some with(literal) %} = {{ literal|render_literal(arg, ci) }}
- {%- else %}
- {%- endmatch %}
- {%- if !loop.last %}, {% endif -%}
- {%- endfor %}
+{%- if is_decl %}
+{%- match arg.default_value() %}
+{%- when Some with(literal) %} = {{ literal|render_literal(arg, ci) }}
+{%- else %}
+{%- endmatch %}
+{%- endif %}
+{%- if !loop.last %}, {% endif -%}
+{%- endfor %}
{%- endmacro %}
-{% macro arg_list_protocol(func) %}
- {%- for arg in func.arguments() -%}
- {{ arg.name()|var_name }}: {{ arg|type_name(ci) }}
- {%- if !loop.last %}, {% endif -%}
- {%- endfor %}
-{%- endmacro %}
{#-
-// Arglist as used in the _UniFFILib function declarations.
+// Arglist as used in the UniffiLib function declarations.
// Note unfiltered name but ffi_type_name filters.
-#}
{%- macro arg_list_ffi_decl(func) %}
{%- for arg in func.arguments() %}
{{- arg.name()|var_name }}: {{ arg.type_().borrow()|ffi_type_name_by_value -}},
{%- endfor %}
- {%- if func.has_rust_call_status_arg() %}_uniffi_out_err: RustCallStatus, {% endif %}
+ {%- if func.has_rust_call_status_arg() %}uniffi_out_err: UniffiRustCallStatus, {% endif %}
{%- endmacro -%}
+{% macro field_name(field, field_num) %}
+{%- if field.name().is_empty() -%}
+v{{- field_num -}}
+{%- else -%}
+{{ field.name()|var_name }}
+{%- endif -%}
+{%- endmacro %}
+
// Macro for destroying fields
{%- macro destroy_fields(member) %}
Disposable.destroy(
@@ -75,3 +139,15 @@
this.{{ field.name()|var_name }}{%- if !loop.last %}, {% endif -%}
{% endfor -%})
{%- endmacro -%}
+
+{%- macro docstring_value(maybe_docstring, indent_spaces) %}
+{%- match maybe_docstring %}
+{%- when Some(docstring) %}
+{{ docstring|docstring(indent_spaces) }}
+{%- else %}
+{%- endmatch %}
+{%- endmacro %}
+
+{%- macro docstring(defn, indent_spaces) %}
+{%- call docstring_value(defn.docstring(), indent_spaces) %}
+{%- endmacro %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt
index 9ee4229018..2cdc72a5e2 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt
@@ -1,6 +1,8 @@
// This file was autogenerated by some hot garbage in the `uniffi` crate.
// Trust me, you don't want to mess with it!
+{%- call kt::docstring_value(ci.namespace_docstring(), 0) %}
+
@file:Suppress("NAME_SHADOWING")
package {{ config.package_name() }};
@@ -28,6 +30,7 @@ import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.CharBuffer
import java.nio.charset.CodingErrorAction
+import java.util.concurrent.atomic.AtomicLong
import java.util.concurrent.ConcurrentHashMap
{%- for req in self.imports() %}
@@ -37,6 +40,7 @@ import java.util.concurrent.ConcurrentHashMap
{% include "RustBufferTemplate.kt" %}
{% include "FfiConverterTemplate.kt" %}
{% include "Helpers.kt" %}
+{% include "HandleMap.kt" %}
// Contains loading, initialization code,
// and the FFI Function declarations in a com.sun.jna.Library.
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/test.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/test.rs
index 7b78540741..0824015751 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/test.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/test.rs
@@ -2,10 +2,8 @@
License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use crate::{
- bindings::{RunScriptOptions, TargetLanguage},
- library_mode::generate_bindings,
-};
+use crate::bindings::TargetLanguage;
+use crate::{bindings::RunScriptOptions, library_mode::generate_bindings, BindingGeneratorDefault};
use anyhow::{bail, Context, Result};
use camino::{Utf8Path, Utf8PathBuf};
use std::env;
@@ -33,14 +31,18 @@ pub fn run_script(
args: Vec<String>,
options: &RunScriptOptions,
) -> Result<()> {
- let script_path = Utf8Path::new(".").join(script_file);
+ let script_path = Utf8Path::new(script_file);
let test_helper = UniFFITestHelper::new(crate_name)?;
- let out_dir = test_helper.create_out_dir(tmp_dir, &script_path)?;
+ let out_dir = test_helper.create_out_dir(tmp_dir, script_path)?;
let cdylib_path = test_helper.copy_cdylib_to_out_dir(&out_dir)?;
generate_bindings(
&cdylib_path,
None,
- &[TargetLanguage::Kotlin],
+ &BindingGeneratorDefault {
+ target_languages: vec![TargetLanguage::Kotlin],
+ try_format_code: false,
+ },
+ None,
&out_dir,
false,
)?;
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/compounds.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/compounds.rs
index b91bcbe18f..16adeca9a5 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/compounds.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/compounds.rs
@@ -3,7 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use super::CodeType;
-use crate::backend::{Literal, Type};
+use crate::{
+ backend::{Literal, Type},
+ bindings::python::gen_python::AsCodeType,
+};
#[derive(Debug)]
pub struct OptionalCodeType {
@@ -33,8 +36,9 @@ impl CodeType for OptionalCodeType {
fn literal(&self, literal: &Literal) -> String {
match literal {
- Literal::Null => "None".into(),
- _ => super::PythonCodeOracle.find(&self.inner).literal(literal),
+ Literal::None => "None".into(),
+ Literal::Some { inner } => super::PythonCodeOracle.find(&self.inner).literal(inner),
+ _ => panic!("Invalid literal for Optional type: {literal:?}"),
}
}
}
@@ -88,7 +92,11 @@ impl MapCodeType {
impl CodeType for MapCodeType {
fn type_label(&self) -> String {
- "dict".to_string()
+ format!(
+ "dict[{}, {}]",
+ self.key.as_codetype().type_label(),
+ self.value.as_codetype().type_label()
+ )
}
fn canonical_name(&self) -> String {
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/executor.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/executor.rs
deleted file mode 100644
index be3ba1d791..0000000000
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/executor.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use super::CodeType;
-
-#[derive(Debug)]
-pub struct ForeignExecutorCodeType;
-
-impl CodeType for ForeignExecutorCodeType {
- fn type_label(&self) -> String {
- "asyncio.BaseEventLoop".into()
- }
-
- fn canonical_name(&self) -> String {
- "ForeignExecutor".into()
- }
-}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/external.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/external.rs
index 0d19c4bb3c..0a46251d6d 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/external.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/external.rs
@@ -17,7 +17,7 @@ impl ExternalCodeType {
impl CodeType for ExternalCodeType {
fn type_label(&self) -> String {
- self.name.clone()
+ super::PythonCodeOracle.class_name(&self.name)
}
fn canonical_name(&self) -> String {
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/mod.rs
index 8178fcc102..6a10a38e7f 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/mod.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/mod.rs
@@ -2,8 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use anyhow::{Context, Result};
+use anyhow::{bail, Context, Result};
use askama::Template;
+use camino::Utf8Path;
use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
@@ -13,20 +14,43 @@ use std::collections::{BTreeSet, HashMap, HashSet};
use std::fmt::Debug;
use crate::backend::TemplateExpression;
+use crate::bindings::python;
use crate::interface::*;
-use crate::BindingsConfig;
+use crate::{BindingGenerator, BindingsConfig};
mod callback_interface;
mod compounds;
mod custom;
mod enum_;
-mod executor;
mod external;
mod miscellany;
mod object;
mod primitives;
mod record;
+pub struct PythonBindingGenerator;
+
+impl BindingGenerator for PythonBindingGenerator {
+ type Config = Config;
+
+ fn write_bindings(
+ &self,
+ ci: &ComponentInterface,
+ config: &Config,
+ out_dir: &Utf8Path,
+ try_format_code: bool,
+ ) -> Result<()> {
+ python::write_bindings(config, ci, out_dir, try_format_code)
+ }
+
+ fn check_library_path(&self, library_path: &Utf8Path, cdylib_name: Option<&str>) -> Result<()> {
+ if cdylib_name.is_none() {
+ bail!("Generate bindings for Python requires a cdylib, but {library_path} was given");
+ }
+ Ok(())
+ }
+}
+
/// A trait tor the implementation.
trait CodeType: Debug {
/// The language specific label used to reference this type. This will be used in
@@ -114,6 +138,8 @@ pub struct Config {
cdylib_name: Option<String>,
#[serde(default)]
custom_types: HashMap<String, CustomTypeConfig>,
+ #[serde(default)]
+ external_packages: HashMap<String, String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
@@ -133,6 +159,16 @@ impl Config {
"uniffi".into()
}
}
+
+ /// Get the package name for a given external namespace.
+ pub fn module_for_namespace(&self, ns: &str) -> String {
+ let ns = ns.to_string().to_snake_case();
+ match self.external_packages.get(&ns) {
+ None => format!(".{ns}"),
+ Some(value) if value.is_empty() => ns,
+ Some(value) => format!("{value}.{ns}"),
+ }
+ }
}
impl BindingsConfig for Config {
@@ -326,7 +362,19 @@ impl PythonCodeOracle {
fixup_keyword(nm.to_string().to_shouty_snake_case())
}
- fn ffi_type_label(ffi_type: &FfiType) -> String {
+ /// Get the idiomatic Python rendering of an FFI callback function name
+ fn ffi_callback_name(&self, nm: &str) -> String {
+ format!("UNIFFI_{}", nm.to_shouty_snake_case())
+ }
+
+ /// Get the idiomatic Python rendering of an FFI struct name
+ fn ffi_struct_name(&self, nm: &str) -> String {
+ // The ctypes docs use both SHOUTY_SNAKE_CASE AND UpperCamelCase for structs. Let's use
+ // UpperCamelCase and reserve shouting for global variables
+ format!("Uniffi{}", nm.to_upper_camel_case())
+ }
+
+ fn ffi_type_label(&self, ffi_type: &FfiType) -> String {
match ffi_type {
FfiType::Int8 => "ctypes.c_int8".to_string(),
FfiType::UInt8 => "ctypes.c_uint8".to_string(),
@@ -338,19 +386,64 @@ impl PythonCodeOracle {
FfiType::UInt64 => "ctypes.c_uint64".to_string(),
FfiType::Float32 => "ctypes.c_float".to_string(),
FfiType::Float64 => "ctypes.c_double".to_string(),
+ FfiType::Handle => "ctypes.c_uint64".to_string(),
FfiType::RustArcPtr(_) => "ctypes.c_void_p".to_string(),
FfiType::RustBuffer(maybe_suffix) => match maybe_suffix {
Some(suffix) => format!("_UniffiRustBuffer{suffix}"),
None => "_UniffiRustBuffer".to_string(),
},
+ FfiType::RustCallStatus => "_UniffiRustCallStatus".to_string(),
FfiType::ForeignBytes => "_UniffiForeignBytes".to_string(),
- FfiType::ForeignCallback => "_UNIFFI_FOREIGN_CALLBACK_T".to_string(),
+ FfiType::Callback(name) => self.ffi_callback_name(name),
+ FfiType::Struct(name) => self.ffi_struct_name(name),
// Pointer to an `asyncio.EventLoop` instance
- FfiType::ForeignExecutorHandle => "ctypes.c_size_t".to_string(),
- FfiType::ForeignExecutorCallback => "_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_T".to_string(),
- FfiType::RustFutureHandle => "ctypes.c_void_p".to_string(),
- FfiType::RustFutureContinuationCallback => "_UNIFFI_FUTURE_CONTINUATION_T".to_string(),
- FfiType::RustFutureContinuationData => "ctypes.c_size_t".to_string(),
+ FfiType::Reference(inner) => format!("ctypes.POINTER({})", self.ffi_type_label(inner)),
+ FfiType::VoidPointer => "ctypes.c_void_p".to_string(),
+ }
+ }
+
+ /// Default values for FFI types
+ ///
+ /// Used to set a default return value when returning an error
+ fn ffi_default_value(&self, return_type: Option<&FfiType>) -> String {
+ match return_type {
+ Some(t) => match t {
+ FfiType::UInt8
+ | FfiType::Int8
+ | FfiType::UInt16
+ | FfiType::Int16
+ | FfiType::UInt32
+ | FfiType::Int32
+ | FfiType::UInt64
+ | FfiType::Int64 => "0".to_owned(),
+ FfiType::Float32 | FfiType::Float64 => "0.0".to_owned(),
+ FfiType::RustArcPtr(_) => "ctypes.c_void_p()".to_owned(),
+ FfiType::RustBuffer(maybe_suffix) => match maybe_suffix {
+ Some(suffix) => format!("_UniffiRustBuffer{suffix}.default()"),
+ None => "_UniffiRustBuffer.default()".to_owned(),
+ },
+ _ => unimplemented!("FFI return type: {t:?}"),
+ },
+ // When we need to use a value for void returns, we use a `u8` placeholder
+ None => "0".to_owned(),
+ }
+ }
+
+ /// Get the name of the protocol and class name for an object.
+ ///
+ /// If we support callback interfaces, the protocol name is the object name, and the class name is derived from that.
+ /// Otherwise, the class name is the object name and the protocol name is derived from that.
+ ///
+ /// This split determines what types `FfiConverter.lower()` inputs. If we support callback
+ /// interfaces, `lower` must lower anything that implements the protocol. If not, then lower
+ /// only lowers the concrete class.
+ fn object_names(&self, obj: &Object) -> (String, String) {
+ let class_name = self.class_name(obj.name());
+ if obj.has_callback_interface() {
+ let impl_name = format!("{class_name}Impl");
+ (class_name, impl_name)
+ } else {
+ (format!("{class_name}Protocol"), class_name)
}
}
}
@@ -392,7 +485,6 @@ impl<T: AsType> AsCodeType for T {
Type::CallbackInterface { name, .. } => {
Box::new(callback_interface::CallbackInterfaceCodeType::new(name))
}
- Type::ForeignExecutor => Box::new(executor::ForeignExecutorCodeType),
Type::Optional { inner_type } => {
Box::new(compounds::OptionalCodeType::new(*inner_type))
}
@@ -429,6 +521,10 @@ pub mod filters {
Ok(format!("{}.lift", ffi_converter_name(as_ct)?))
}
+ pub(super) fn check_lower_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!("{}.check_lower", ffi_converter_name(as_ct)?))
+ }
+
pub(super) fn lower_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
Ok(format!("{}.lower", ffi_converter_name(as_ct)?))
}
@@ -448,8 +544,18 @@ pub mod filters {
Ok(as_ct.as_codetype().literal(literal))
}
+ // Get the idiomatic Python rendering of an individual enum variant's discriminant
+ pub fn variant_discr_literal(e: &Enum, index: &usize) -> Result<String, askama::Error> {
+ let literal = e.variant_discr(*index).expect("invalid index");
+ Ok(Type::UInt64.as_codetype().literal(&literal))
+ }
+
pub fn ffi_type_name(type_: &FfiType) -> Result<String, askama::Error> {
- Ok(PythonCodeOracle::ffi_type_label(type_))
+ Ok(PythonCodeOracle.ffi_type_label(type_))
+ }
+
+ pub fn ffi_default_value(return_type: Option<FfiType>) -> Result<String, askama::Error> {
+ Ok(PythonCodeOracle.ffi_default_value(return_type.as_ref()))
}
/// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc).
@@ -471,4 +577,51 @@ pub mod filters {
pub fn enum_variant_py(nm: &str) -> Result<String, askama::Error> {
Ok(PythonCodeOracle.enum_variant_name(nm))
}
+
+ /// Get the idiomatic Python rendering of an FFI callback function name
+ pub fn ffi_callback_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(PythonCodeOracle.ffi_callback_name(nm))
+ }
+
+ /// Get the idiomatic Python rendering of an FFI struct name
+ pub fn ffi_struct_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(PythonCodeOracle.ffi_struct_name(nm))
+ }
+
+ /// Get the idiomatic Python rendering of an individual enum variant.
+ pub fn object_names(obj: &Object) -> Result<(String, String), askama::Error> {
+ Ok(PythonCodeOracle.object_names(obj))
+ }
+
+ /// Get the idiomatic Python rendering of docstring
+ pub fn docstring(docstring: &str, spaces: &i32) -> Result<String, askama::Error> {
+ let docstring = textwrap::dedent(docstring);
+ // Escape triple quotes to avoid syntax error
+ let escaped = docstring.replace(r#"""""#, r#"\"\"\""#);
+
+ let wrapped = format!("\"\"\"\n{escaped}\n\"\"\"");
+
+ let spaces = usize::try_from(*spaces).unwrap_or_default();
+ Ok(textwrap::indent(&wrapped, &" ".repeat(spaces)))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn test_docstring_escape() {
+ let docstring = r#""""This is a docstring beginning with triple quotes.
+Contains "quotes" in it.
+It also has a triple quote: """
+And a even longer quote: """"""#;
+
+ let expected = r#""""
+\"\"\"This is a docstring beginning with triple quotes.
+Contains "quotes" in it.
+It also has a triple quote: \"\"\"
+And a even longer quote: \"\"\"""
+""""#;
+
+ assert_eq!(super::filters::docstring(docstring, &0).unwrap(), expected);
+ }
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Async.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Async.py
index 82aa534b46..26daa9ba5c 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Async.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Async.py
@@ -3,13 +3,37 @@ _UNIFFI_RUST_FUTURE_POLL_READY = 0
_UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1
# Stores futures for _uniffi_continuation_callback
-_UniffiContinuationPointerManager = _UniffiPointerManager()
+_UniffiContinuationHandleMap = _UniffiHandleMap()
+
+UNIFFI_GLOBAL_EVENT_LOOP = None
+
+"""
+Set the event loop to use for async functions
+
+This is needed if some async functions run outside of the eventloop, for example:
+ - A non-eventloop thread is spawned, maybe from `EventLoop.run_in_executor` or maybe from the
+ Rust code spawning its own thread.
+ - The Rust code calls an async callback method from a sync callback function, using something
+ like `pollster` to block on the async call.
+
+In this case, we need an event loop to run the Python async function, but there's no eventloop set
+for the thread. Use `uniffi_set_event_loop` to force an eventloop to be used in this case.
+"""
+def uniffi_set_event_loop(eventloop: asyncio.BaseEventLoop):
+ global UNIFFI_GLOBAL_EVENT_LOOP
+ UNIFFI_GLOBAL_EVENT_LOOP = eventloop
+
+def _uniffi_get_event_loop():
+ if UNIFFI_GLOBAL_EVENT_LOOP is not None:
+ return UNIFFI_GLOBAL_EVENT_LOOP
+ else:
+ return asyncio.get_running_loop()
# Continuation callback for async functions
# lift the return value or error and resolve the future, causing the async function to resume.
-@_UNIFFI_FUTURE_CONTINUATION_T
+@UNIFFI_RUST_FUTURE_CONTINUATION_CALLBACK
def _uniffi_continuation_callback(future_ptr, poll_code):
- (eventloop, future) = _UniffiContinuationPointerManager.release_pointer(future_ptr)
+ (eventloop, future) = _UniffiContinuationHandleMap.remove(future_ptr)
eventloop.call_soon_threadsafe(_uniffi_set_future_result, future, poll_code)
def _uniffi_set_future_result(future, poll_code):
@@ -18,14 +42,15 @@ def _uniffi_set_future_result(future, poll_code):
async def _uniffi_rust_call_async(rust_future, ffi_poll, ffi_complete, ffi_free, lift_func, error_ffi_converter):
try:
- eventloop = asyncio.get_running_loop()
+ eventloop = _uniffi_get_event_loop()
# Loop and poll until we see a _UNIFFI_RUST_FUTURE_POLL_READY value
while True:
future = eventloop.create_future()
ffi_poll(
rust_future,
- _UniffiContinuationPointerManager.new_pointer((eventloop, future)),
+ _uniffi_continuation_callback,
+ _UniffiContinuationHandleMap.insert((eventloop, future)),
)
poll_code = await future
if poll_code == _UNIFFI_RUST_FUTURE_POLL_READY:
@@ -37,4 +62,53 @@ async def _uniffi_rust_call_async(rust_future, ffi_poll, ffi_complete, ffi_free,
finally:
ffi_free(rust_future)
-_UniffiLib.{{ ci.ffi_rust_future_continuation_callback_set().name() }}(_uniffi_continuation_callback)
+{%- if ci.has_async_callback_interface_definition() %}
+def uniffi_trait_interface_call_async(make_call, handle_success, handle_error):
+ async def make_call_and_call_callback():
+ try:
+ handle_success(await make_call())
+ except Exception as e:
+ print("UniFFI: Unhandled exception in trait interface call", file=sys.stderr)
+ traceback.print_exc(file=sys.stderr)
+ handle_error(
+ _UniffiRustCallStatus.CALL_UNEXPECTED_ERROR,
+ {{ Type::String.borrow()|lower_fn }}(repr(e)),
+ )
+ eventloop = _uniffi_get_event_loop()
+ task = asyncio.run_coroutine_threadsafe(make_call_and_call_callback(), eventloop)
+ handle = UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.insert((eventloop, task))
+ return UniffiForeignFuture(handle, uniffi_foreign_future_free)
+
+def uniffi_trait_interface_call_async_with_error(make_call, handle_success, handle_error, error_type, lower_error):
+ async def make_call_and_call_callback():
+ try:
+ try:
+ handle_success(await make_call())
+ except error_type as e:
+ handle_error(
+ _UniffiRustCallStatus.CALL_ERROR,
+ lower_error(e),
+ )
+ except Exception as e:
+ print("UniFFI: Unhandled exception in trait interface call", file=sys.stderr)
+ traceback.print_exc(file=sys.stderr)
+ handle_error(
+ _UniffiRustCallStatus.CALL_UNEXPECTED_ERROR,
+ {{ Type::String.borrow()|lower_fn }}(repr(e)),
+ )
+ eventloop = _uniffi_get_event_loop()
+ task = asyncio.run_coroutine_threadsafe(make_call_and_call_callback(), eventloop)
+ handle = UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.insert((eventloop, task))
+ return UniffiForeignFuture(handle, uniffi_foreign_future_free)
+
+UNIFFI_FOREIGN_FUTURE_HANDLE_MAP = _UniffiHandleMap()
+
+@UNIFFI_FOREIGN_FUTURE_FREE
+def uniffi_foreign_future_free(handle):
+ (eventloop, task) = UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.remove(handle)
+ eventloop.call_soon(uniffi_foreign_future_do_free, task)
+
+def uniffi_foreign_future_do_free(task):
+ if not task.done():
+ task.cancel()
+{%- endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BooleanHelper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BooleanHelper.py
index 6775e9e132..3f8c5d1d4d 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BooleanHelper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BooleanHelper.py
@@ -1,16 +1,20 @@
-class _UniffiConverterBool(_UniffiConverterPrimitive):
+class _UniffiConverterBool:
@classmethod
- def check(cls, value):
+ def check_lower(cls, value):
return not not value
@classmethod
+ def lower(cls, value):
+ return 1 if value else 0
+
+ @staticmethod
+ def lift(value):
+ return value != 0
+
+ @classmethod
def read(cls, buf):
return cls.lift(buf.read_u8())
@classmethod
- def write_unchecked(cls, value, buf):
+ def write(cls, value, buf):
buf.write_u8(value)
-
- @staticmethod
- def lift(value):
- return value != 0
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BytesHelper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BytesHelper.py
index 196b5b29fa..4d09531322 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BytesHelper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BytesHelper.py
@@ -7,10 +7,13 @@ class _UniffiConverterBytes(_UniffiConverterRustBuffer):
return buf.read(size)
@staticmethod
- def write(value, buf):
+ def check_lower(value):
try:
memoryview(value)
except TypeError:
raise TypeError("a bytes-like object is required, not {!r}".format(type(value).__name__))
+
+ @staticmethod
+ def write(value, buf):
buf.write_i32(len(value))
buf.write(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py
new file mode 100644
index 0000000000..676f01177a
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py
@@ -0,0 +1,98 @@
+{% if self.include_once_check("CallbackInterfaceRuntime.py") %}{% include "CallbackInterfaceRuntime.py" %}{% endif %}
+{%- let trait_impl=format!("UniffiTraitImpl{}", name) %}
+
+# Put all the bits inside a class to keep the top-level namespace clean
+class {{ trait_impl }}:
+ # For each method, generate a callback function to pass to Rust
+ {%- for (ffi_callback, meth) in vtable_methods.iter() %}
+
+ @{{ ffi_callback.name()|ffi_callback_name }}
+ def {{ meth.name()|fn_name }}(
+ {%- for arg in ffi_callback.arguments() %}
+ {{ arg.name()|var_name }},
+ {%- endfor -%}
+ {%- if ffi_callback.has_rust_call_status_arg() %}
+ uniffi_call_status_ptr,
+ {%- endif %}
+ ):
+ uniffi_obj = {{ ffi_converter_name }}._handle_map.get(uniffi_handle)
+ def make_call():
+ args = ({% for arg in meth.arguments() %}{{ arg|lift_fn }}({{ arg.name()|var_name }}), {% endfor %})
+ method = uniffi_obj.{{ meth.name()|fn_name }}
+ return method(*args)
+
+ {% if !meth.is_async() %}
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ def write_return_value(v):
+ uniffi_out_return[0] = {{ return_type|lower_fn }}(v)
+ {%- when None %}
+ write_return_value = lambda v: None
+ {%- endmatch %}
+
+ {%- match meth.throws_type() %}
+ {%- when None %}
+ _uniffi_trait_interface_call(
+ uniffi_call_status_ptr.contents,
+ make_call,
+ write_return_value,
+ )
+ {%- when Some(error) %}
+ _uniffi_trait_interface_call_with_error(
+ uniffi_call_status_ptr.contents,
+ make_call,
+ write_return_value,
+ {{ error|type_name }},
+ {{ error|lower_fn }},
+ )
+ {%- endmatch %}
+ {%- else %}
+ def handle_success(return_value):
+ uniffi_future_callback(
+ uniffi_callback_data,
+ {{ meth.foreign_future_ffi_result_struct().name()|ffi_struct_name }}(
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ {{ return_type|lower_fn }}(return_value),
+ {%- when None %}
+ {%- endmatch %}
+ _UniffiRustCallStatus.default()
+ )
+ )
+
+ def handle_error(status_code, rust_buffer):
+ uniffi_future_callback(
+ uniffi_callback_data,
+ {{ meth.foreign_future_ffi_result_struct().name()|ffi_struct_name }}(
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ {{ meth.return_type().map(FfiType::from)|ffi_default_value }},
+ {%- when None %}
+ {%- endmatch %}
+ _UniffiRustCallStatus(status_code, rust_buffer),
+ )
+ )
+
+ {%- match meth.throws_type() %}
+ {%- when None %}
+ uniffi_out_return[0] = uniffi_trait_interface_call_async(make_call, handle_success, handle_error)
+ {%- when Some(error) %}
+ uniffi_out_return[0] = uniffi_trait_interface_call_async_with_error(make_call, handle_success, handle_error, {{ error|type_name }}, {{ error|lower_fn }})
+ {%- endmatch %}
+ {%- endif %}
+ {%- endfor %}
+
+ @{{ "CallbackInterfaceFree"|ffi_callback_name }}
+ def uniffi_free(uniffi_handle):
+ {{ ffi_converter_name }}._handle_map.remove(uniffi_handle)
+
+ # Generate the FFI VTable. This has a field for each callback interface method.
+ uniffi_vtable = {{ vtable|ffi_type_name }}(
+ {%- for (_, meth) in vtable_methods.iter() %}
+ {{ meth.name()|fn_name }},
+ {%- endfor %}
+ uniffi_free
+ )
+ # Send Rust a pointer to the VTable. Note: this means we need to keep the struct alive forever,
+ # or else bad things will happen when Rust tries to access it.
+ _UniffiLib.{{ ffi_init_callback.name() }}(ctypes.byref(uniffi_vtable))
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceRuntime.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceRuntime.py
index 0fe2ab8dc0..d802c90fde 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceRuntime.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceRuntime.py
@@ -1,42 +1,3 @@
-import threading
-
-class ConcurrentHandleMap:
- """
- A map where inserting, getting and removing data is synchronized with a lock.
- """
-
- def __init__(self):
- # type Handle = int
- self._left_map = {} # type: Dict[Handle, Any]
- self._right_map = {} # type: Dict[Any, Handle]
-
- self._lock = threading.Lock()
- self._current_handle = 0
- self._stride = 1
-
-
- def insert(self, obj):
- with self._lock:
- if obj in self._right_map:
- return self._right_map[obj]
- else:
- handle = self._current_handle
- self._current_handle += self._stride
- self._left_map[handle] = obj
- self._right_map[obj] = handle
- return handle
-
- def get(self, handle):
- with self._lock:
- return self._left_map.get(handle)
-
- def remove(self, handle):
- with self._lock:
- if handle in self._left_map:
- obj = self._left_map.pop(handle)
- del self._right_map[obj]
- return obj
-
# Magic number for the Rust proxy to call using the same mechanism as every other method,
# to free the callback once it's dropped by Rust.
IDX_CALLBACK_FREE = 0
@@ -45,22 +6,12 @@ _UNIFFI_CALLBACK_SUCCESS = 0
_UNIFFI_CALLBACK_ERROR = 1
_UNIFFI_CALLBACK_UNEXPECTED_ERROR = 2
-class _UniffiConverterCallbackInterface:
- _handle_map = ConcurrentHandleMap()
-
- def __init__(self, cb):
- self._foreign_callback = cb
-
- def drop(self, handle):
- self.__class__._handle_map.remove(handle)
+class UniffiCallbackInterfaceFfiConverter:
+ _handle_map = _UniffiHandleMap()
@classmethod
def lift(cls, handle):
- obj = cls._handle_map.get(handle)
- if not obj:
- raise InternalError("The object in the handle map has been dropped already")
-
- return obj
+ return cls._handle_map.get(handle)
@classmethod
def read(cls, buf):
@@ -68,6 +19,10 @@ class _UniffiConverterCallbackInterface:
cls.lift(handle)
@classmethod
+ def check_lower(cls, cb):
+ pass
+
+ @classmethod
def lower(cls, cb):
handle = cls._handle_map.insert(cb)
return handle
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceTemplate.py
index e0e926757a..a41e58e635 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceTemplate.py
@@ -1,105 +1,13 @@
-{%- let cbi = ci|get_callback_interface_definition(id) %}
-{%- let foreign_callback = format!("foreignCallback{}", canonical_type_name) %}
+{%- let cbi = ci|get_callback_interface_definition(name) %}
+{%- let ffi_init_callback = cbi.ffi_init_callback() %}
+{%- let protocol_name = type_name.clone() %}
+{%- let protocol_docstring = cbi.docstring() %}
+{%- let vtable = cbi.vtable() %}
+{%- let methods = cbi.methods() %}
+{%- let vtable_methods = cbi.vtable_methods() %}
-{% if self.include_once_check("CallbackInterfaceRuntime.py") %}{% include "CallbackInterfaceRuntime.py" %}{% endif %}
-
-# Declaration and _UniffiConverters for {{ type_name }} Callback Interface
-
-class {{ type_name }}:
- {% for meth in cbi.methods() -%}
- def {{ meth.name()|fn_name }}(self, {% call py::arg_list_decl(meth) %}):
- raise NotImplementedError
-
- {% endfor %}
-
-def py_{{ foreign_callback }}(handle, method, args_data, args_len, buf_ptr):
- {% for meth in cbi.methods() -%}
- {% let method_name = format!("invoke_{}", meth.name())|fn_name %}
- def {{ method_name }}(python_callback, args_stream, buf_ptr):
- {#- Unpacking args from the _UniffiRustBuffer #}
- def makeCall():
- {#- Calling the concrete callback object #}
- {%- if meth.arguments().len() != 0 -%}
- return python_callback.{{ meth.name()|fn_name }}(
- {% for arg in meth.arguments() -%}
- {{ arg|read_fn }}(args_stream)
- {%- if !loop.last %}, {% endif %}
- {% endfor -%}
- )
- {%- else %}
- return python_callback.{{ meth.name()|fn_name }}()
- {%- endif %}
-
- def makeCallAndHandleReturn():
- {%- match meth.return_type() %}
- {%- when Some(return_type) %}
- rval = makeCall()
- with _UniffiRustBuffer.alloc_with_builder() as builder:
- {{ return_type|write_fn }}(rval, builder)
- buf_ptr[0] = builder.finalize()
- {%- when None %}
- makeCall()
- {%- endmatch %}
- return _UNIFFI_CALLBACK_SUCCESS
-
- {%- match meth.throws_type() %}
- {%- when None %}
- return makeCallAndHandleReturn()
- {%- when Some(err) %}
- try:
- return makeCallAndHandleReturn()
- except {{ err|type_name }} as e:
- # Catch errors declared in the UDL file
- with _UniffiRustBuffer.alloc_with_builder() as builder:
- {{ err|write_fn }}(e, builder)
- buf_ptr[0] = builder.finalize()
- return _UNIFFI_CALLBACK_ERROR
- {%- endmatch %}
-
- {% endfor %}
-
- cb = {{ ffi_converter_name }}.lift(handle)
- if not cb:
- raise InternalError("No callback in handlemap; this is a uniffi bug")
-
- if method == IDX_CALLBACK_FREE:
- {{ ffi_converter_name }}.drop(handle)
- # Successfull return
- # See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
- return _UNIFFI_CALLBACK_SUCCESS
-
- {% for meth in cbi.methods() -%}
- {% let method_name = format!("invoke_{}", meth.name())|fn_name -%}
- if method == {{ loop.index }}:
- # Call the method and handle any errors
- # See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs` for details
- try:
- return {{ method_name }}(cb, _UniffiRustBufferStream(args_data, args_len), buf_ptr)
- except BaseException as e:
- # Catch unexpected errors
- try:
- # Try to serialize the exception into a String
- buf_ptr[0] = {{ Type::String.borrow()|lower_fn }}(repr(e))
- except:
- # If that fails, just give up
- pass
- return _UNIFFI_CALLBACK_UNEXPECTED_ERROR
- {% endfor %}
-
- # This should never happen, because an out of bounds method index won't
- # ever be used. Once we can catch errors, we should return an InternalException.
- # https://github.com/mozilla/uniffi-rs/issues/351
-
- # An unexpected error happened.
- # See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
- return _UNIFFI_CALLBACK_UNEXPECTED_ERROR
-
-# We need to keep this function reference alive:
-# if they get GC'd while in use then UniFFI internals could attempt to call a function
-# that is in freed memory.
-# That would be...uh...bad. Yeah, that's the word. Bad.
-{{ foreign_callback }} = _UNIFFI_FOREIGN_CALLBACK_T(py_{{ foreign_callback }})
-_rust_call(lambda err: _UniffiLib.{{ cbi.ffi_init_callback().name() }}({{ foreign_callback }}, err))
+{% include "Protocol.py" %}
+{% include "CallbackInterfaceImpl.py" %}
# The _UniffiConverter which transforms the Callbacks in to Handles to pass to Rust.
-{{ ffi_converter_name }} = _UniffiConverterCallbackInterface({{ foreign_callback }})
+{{ ffi_converter_name }} = UniffiCallbackInterfaceFfiConverter()
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CustomType.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CustomType.py
index 5be6155b84..f75a85dc27 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CustomType.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CustomType.py
@@ -18,6 +18,10 @@ class _UniffiConverterType{{ name }}:
return {{ builtin|ffi_converter_name }}.lift(value)
@staticmethod
+ def check_lower(value):
+ return {{ builtin|ffi_converter_name }}.check_lower(value)
+
+ @staticmethod
def lower(value):
return {{ builtin|ffi_converter_name }}.lower(value)
@@ -52,6 +56,11 @@ class _UniffiConverterType{{ name }}:
return {{ config.into_custom.render("builtin_value") }}
@staticmethod
+ def check_lower(value):
+ builtin_value = {{ config.from_custom.render("value") }}
+ return {{ builtin|check_lower_fn }}(builtin_value)
+
+ @staticmethod
def lower(value):
builtin_value = {{ config.from_custom.render("value") }}
return {{ builtin|lower_fn }}(builtin_value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/DurationHelper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/DurationHelper.py
index 10974e009d..ecb035b7f4 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/DurationHelper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/DurationHelper.py
@@ -12,10 +12,14 @@ class _UniffiConverterDuration(_UniffiConverterRustBuffer):
return datetime.timedelta(seconds=seconds, microseconds=microseconds)
@staticmethod
- def write(value, buf):
+ def check_lower(value):
seconds = value.seconds + value.days * 24 * 3600
- nanoseconds = value.microseconds * 1000
if seconds < 0:
raise ValueError("Invalid duration, must be non-negative")
+
+ @staticmethod
+ def write(value, buf):
+ seconds = value.seconds + value.days * 24 * 3600
+ nanoseconds = value.microseconds * 1000
buf.write_i64(seconds)
buf.write_u32(nanoseconds)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py
index 84d089baf9..d07dd1c44a 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py
@@ -7,31 +7,59 @@
{% if e.is_flat() %}
class {{ type_name }}(enum.Enum):
- {% for variant in e.variants() -%}
- {{ variant.name()|enum_variant_py }} = {{ loop.index }}
+ {%- call py::docstring(e, 4) %}
+ {%- for variant in e.variants() %}
+ {{ variant.name()|enum_variant_py }} = {{ e|variant_discr_literal(loop.index0) }}
+ {%- call py::docstring(variant, 4) %}
{% endfor %}
{% else %}
class {{ type_name }}:
+ {%- call py::docstring(e, 4) %}
def __init__(self):
raise RuntimeError("{{ type_name }} cannot be instantiated directly")
# Each enum variant is a nested class of the enum itself.
{% for variant in e.variants() -%}
class {{ variant.name()|enum_variant_py }}:
- {% for field in variant.fields() %}
- {{- field.name()|var_name }}: "{{- field|type_name }}";
+ {%- call py::docstring(variant, 8) %}
+
+ {%- if variant.has_nameless_fields() %}
+ def __init__(self, *values):
+ if len(values) != {{ variant.fields().len() }}:
+ raise TypeError(f"Expected a tuple of len {{ variant.fields().len() }}, found len {len(values)}")
+ {%- for field in variant.fields() %}
+ if not isinstance(values[{{ loop.index0 }}], {{ field|type_name }}):
+ raise TypeError(f"unexpected type for tuple element {{ loop.index0 }} - expected '{{ field|type_name }}', got '{type(values[{{ loop.index0 }}])}'")
+ {%- endfor %}
+ self._values = values
+
+ def __getitem__(self, index):
+ return self._values[index]
+
+ def __str__(self):
+ return f"{{ type_name }}.{{ variant.name()|enum_variant_py }}{self._values!r}"
+
+ def __eq__(self, other):
+ if not other.is_{{ variant.name()|var_name }}():
+ return False
+ return self._values == other._values
+
+ {%- else -%}
+ {%- for field in variant.fields() %}
+ {{ field.name()|var_name }}: "{{ field|type_name }}"
+ {%- call py::docstring(field, 8) %}
{%- endfor %}
@typing.no_type_check
def __init__(self,{% for field in variant.fields() %}{{ field.name()|var_name }}: "{{- field|type_name }}"{% if loop.last %}{% else %}, {% endif %}{% endfor %}):
- {% if variant.has_fields() %}
+ {%- if variant.has_fields() %}
{%- for field in variant.fields() %}
self.{{ field.name()|var_name }} = {{ field.name()|var_name }}
{%- endfor %}
- {% else %}
+ {%- else %}
pass
- {% endif %}
+ {%- endif %}
def __str__(self):
return "{{ type_name }}.{{ variant.name()|enum_variant_py }}({% for field in variant.fields() %}{{ field.name()|var_name }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in variant.fields() %}self.{{ field.name()|var_name }}{% if loop.last %}{% else %}, {% endif %}{% endfor %})
@@ -44,6 +72,7 @@ class {{ type_name }}:
return False
{%- endfor %}
return True
+ {% endif %}
{% endfor %}
# For each variant, we have an `is_NAME` method for easily checking
@@ -81,6 +110,30 @@ class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
{%- endfor %}
raise InternalError("Raw enum value doesn't match any cases")
+ @staticmethod
+ def check_lower(value):
+ {%- if e.variants().is_empty() %}
+ pass
+ {%- else %}
+ {%- for variant in e.variants() %}
+ {%- if e.is_flat() %}
+ if value == {{ type_name }}.{{ variant.name()|enum_variant_py }}:
+ {%- else %}
+ if value.is_{{ variant.name()|var_name }}():
+ {%- endif %}
+ {%- for field in variant.fields() %}
+ {%- if variant.has_nameless_fields() %}
+ {{ field|check_lower_fn }}(value._values[{{ loop.index0 }}])
+ {%- else %}
+ {{ field|check_lower_fn }}(value.{{ field.name()|var_name }})
+ {%- endif %}
+ {%- endfor %}
+ return
+ {%- endfor %}
+ raise ValueError(value)
+ {%- endif %}
+
+ @staticmethod
def write(value, buf):
{%- for variant in e.variants() %}
{%- if e.is_flat() %}
@@ -90,7 +143,11 @@ class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
if value.is_{{ variant.name()|var_name }}():
buf.write_i32({{ loop.index }})
{%- for field in variant.fields() %}
+ {%- if variant.has_nameless_fields() %}
+ {{ field|write_fn }}(value._values[{{ loop.index0 }}], buf)
+ {%- else %}
{{ field|write_fn }}(value.{{ field.name()|var_name }}, buf)
+ {%- endif %}
{%- endfor %}
{%- endif %}
{%- endfor %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py
index 26a1e6452a..0911ff559a 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py
@@ -5,6 +5,7 @@
# __dict__. All of this happens in dummy class to avoid polluting the module
# namespace.
class {{ type_name }}(Exception):
+ {%- call py::docstring(e, 4) %}
pass
_UniffiTemp{{ type_name }} = {{ type_name }}
@@ -14,10 +15,14 @@ class {{ type_name }}: # type: ignore
{%- let variant_type_name = variant.name()|class_name -%}
{%- if e.is_flat() %}
class {{ variant_type_name }}(_UniffiTemp{{ type_name }}):
+ {%- call py::docstring(variant, 8) %}
+
def __repr__(self):
return "{{ type_name }}.{{ variant_type_name }}({})".format(repr(str(self)))
{%- else %}
class {{ variant_type_name }}(_UniffiTemp{{ type_name }}):
+ {%- call py::docstring(variant, 8) %}
+
def __init__(self{% for field in variant.fields() %}, {{ field.name()|var_name }}{% endfor %}):
{%- if variant.has_fields() %}
super().__init__(", ".join([
@@ -60,6 +65,20 @@ class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
raise InternalError("Raw enum value doesn't match any cases")
@staticmethod
+ def check_lower(value):
+ {%- if e.variants().is_empty() %}
+ pass
+ {%- else %}
+ {%- for variant in e.variants() %}
+ if isinstance(value, {{ type_name }}.{{ variant.name()|class_name }}):
+ {%- for field in variant.fields() %}
+ {{ field|check_lower_fn }}(value.{{ field.name()|var_name }})
+ {%- endfor %}
+ return
+ {%- endfor %}
+ {%- endif %}
+
+ @staticmethod
def write(value, buf):
{%- for variant in e.variants() %}
if isinstance(value, {{ type_name }}.{{ variant.name()|class_name }}):
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py
index 71e05e8b06..6c0cee85ef 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py
@@ -1,9 +1,9 @@
-{%- let ns = namespace|fn_name %}
+{%- let module = python_config.module_for_namespace(namespace) -%}
# External type {{name}} is in namespace "{{namespace}}", crate {{module_path}}
{%- let ffi_converter_name = "_UniffiConverterType{}"|format(name) %}
-{{ self.add_import_of(ns, ffi_converter_name) }}
-{{ self.add_import_of(ns, name) }} {#- import the type alias itself -#}
+{{ self.add_import_of(module, ffi_converter_name) }}
+{{ self.add_import_of(module, name|class_name) }} {#- import the type alias itself -#}
{%- let rustbuffer_local_name = "_UniffiRustBuffer{}"|format(name) %}
-{{ self.add_import_of_as(ns, "_UniffiRustBuffer", rustbuffer_local_name) }}
+{{ self.add_import_of_as(module, "_UniffiRustBuffer", rustbuffer_local_name) }}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float32Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float32Helper.py
index a52107a638..49a1a7286e 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float32Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float32Helper.py
@@ -4,5 +4,5 @@ class _UniffiConverterFloat(_UniffiConverterPrimitiveFloat):
return buf.read_float()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_float(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float64Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float64Helper.py
index 772f5080e9..e2084c7b13 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float64Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float64Helper.py
@@ -4,5 +4,5 @@ class _UniffiConverterDouble(_UniffiConverterPrimitiveFloat):
return buf.read_double()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_double(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ForeignExecutorTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ForeignExecutorTemplate.py
deleted file mode 100644
index 6a6932fed0..0000000000
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ForeignExecutorTemplate.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# FFI code for the ForeignExecutor type
-
-{{ self.add_import("asyncio") }}
-
-_UNIFFI_RUST_TASK_CALLBACK_SUCCESS = 0
-_UNIFFI_RUST_TASK_CALLBACK_CANCELLED = 1
-_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS = 0
-_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_CANCELED = 1
-_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_ERROR = 2
-
-class {{ ffi_converter_name }}:
- _pointer_manager = _UniffiPointerManager()
-
- @classmethod
- def lower(cls, eventloop):
- if not isinstance(eventloop, asyncio.BaseEventLoop):
- raise TypeError("_uniffi_executor_callback: Expected EventLoop instance")
- return cls._pointer_manager.new_pointer(eventloop)
-
- @classmethod
- def write(cls, eventloop, buf):
- buf.write_c_size_t(cls.lower(eventloop))
-
- @classmethod
- def read(cls, buf):
- return cls.lift(buf.read_c_size_t())
-
- @classmethod
- def lift(cls, value):
- return cls._pointer_manager.lookup(value)
-
-@_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_T
-def _uniffi_executor_callback(eventloop_address, delay, task_ptr, task_data):
- if task_ptr is None:
- {{ ffi_converter_name }}._pointer_manager.release_pointer(eventloop_address)
- return _UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS
- else:
- eventloop = {{ ffi_converter_name }}._pointer_manager.lookup(eventloop_address)
- if eventloop.is_closed():
- return _UNIFFI_FOREIGN_EXECUTOR_CALLBACK_CANCELED
-
- callback = _UNIFFI_RUST_TASK(task_ptr)
- # FIXME: there's no easy way to get a callback when an eventloop is closed. This means that
- # if eventloop is called before the `call_soon_threadsafe()` calls are invoked, the call
- # will never happen and we will probably leak a resource.
- if delay == 0:
- # This can be called from any thread, so make sure to use `call_soon_threadsafe'
- eventloop.call_soon_threadsafe(callback, task_data,
- _UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS)
- else:
- # For delayed tasks, we use `call_soon_threadsafe()` + `call_later()` to make the
- # operation threadsafe
- eventloop.call_soon_threadsafe(eventloop.call_later, delay / 1000.0, callback,
- task_data, _UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS)
- return _UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS
-
-# Register the callback with the scaffolding
-{%- match ci.ffi_foreign_executor_callback_set() %}
-{%- when Some with (fn) %}
-_UniffiLib.{{ fn.name() }}(_uniffi_executor_callback)
-{%- when None %}
-{#- No foreign executor, we don't set anything #}
-{% endmatch %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/HandleMap.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/HandleMap.py
new file mode 100644
index 0000000000..f7c13cf745
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/HandleMap.py
@@ -0,0 +1,33 @@
+class _UniffiHandleMap:
+ """
+ A map where inserting, getting and removing data is synchronized with a lock.
+ """
+
+ def __init__(self):
+ # type Handle = int
+ self._map = {} # type: Dict[Handle, Any]
+ self._lock = threading.Lock()
+ self._counter = itertools.count()
+
+ def insert(self, obj):
+ with self._lock:
+ handle = next(self._counter)
+ self._map[handle] = obj
+ return handle
+
+ def get(self, handle):
+ try:
+ with self._lock:
+ return self._map[handle]
+ except KeyError:
+ raise InternalError("UniffiHandleMap.get: Invalid handle")
+
+ def remove(self, handle):
+ try:
+ with self._lock:
+ return self._map.pop(handle)
+ except KeyError:
+ raise InternalError("UniffiHandleMap.remove: Invalid handle")
+
+ def __len__(self):
+ return len(self._map)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Helpers.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Helpers.py
index dca962f176..5d4bcbba89 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Helpers.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Helpers.py
@@ -16,15 +16,19 @@ class _UniffiRustCallStatus(ctypes.Structure):
# These match the values from the uniffi::rustcalls module
CALL_SUCCESS = 0
CALL_ERROR = 1
- CALL_PANIC = 2
+ CALL_UNEXPECTED_ERROR = 2
+
+ @staticmethod
+ def default():
+ return _UniffiRustCallStatus(code=_UniffiRustCallStatus.CALL_SUCCESS, error_buf=_UniffiRustBuffer.default())
def __str__(self):
if self.code == _UniffiRustCallStatus.CALL_SUCCESS:
return "_UniffiRustCallStatus(CALL_SUCCESS)"
elif self.code == _UniffiRustCallStatus.CALL_ERROR:
return "_UniffiRustCallStatus(CALL_ERROR)"
- elif self.code == _UniffiRustCallStatus.CALL_PANIC:
- return "_UniffiRustCallStatus(CALL_PANIC)"
+ elif self.code == _UniffiRustCallStatus.CALL_UNEXPECTED_ERROR:
+ return "_UniffiRustCallStatus(CALL_UNEXPECTED_ERROR)"
else:
return "_UniffiRustCallStatus(<invalid code>)"
@@ -37,7 +41,7 @@ def _rust_call_with_error(error_ffi_converter, fn, *args):
#
# This function is used for rust calls that return Result<> and therefore can set the CALL_ERROR status code.
# error_ffi_converter must be set to the _UniffiConverter for the error class that corresponds to the result.
- call_status = _UniffiRustCallStatus(code=_UniffiRustCallStatus.CALL_SUCCESS, error_buf=_UniffiRustBuffer(0, 0, None))
+ call_status = _UniffiRustCallStatus.default()
args_with_error = args + (ctypes.byref(call_status),)
result = fn(*args_with_error)
@@ -53,7 +57,7 @@ def _uniffi_check_call_status(error_ffi_converter, call_status):
raise InternalError("_rust_call_with_error: CALL_ERROR, but error_ffi_converter is None")
else:
raise error_ffi_converter.lift(call_status.error_buf)
- elif call_status.code == _UniffiRustCallStatus.CALL_PANIC:
+ elif call_status.code == _UniffiRustCallStatus.CALL_UNEXPECTED_ERROR:
# When the rust code sees a panic, it tries to construct a _UniffiRustBuffer
# with the message. But if that code panics, then it just sends back
# an empty buffer.
@@ -66,10 +70,20 @@ def _uniffi_check_call_status(error_ffi_converter, call_status):
raise InternalError("Invalid _UniffiRustCallStatus code: {}".format(
call_status.code))
-# A function pointer for a callback as defined by UniFFI.
-# Rust definition `fn(handle: u64, method: u32, args: _UniffiRustBuffer, buf_ptr: *mut _UniffiRustBuffer) -> int`
-_UNIFFI_FOREIGN_CALLBACK_T = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_ulonglong, ctypes.c_ulong, ctypes.POINTER(ctypes.c_char), ctypes.c_int, ctypes.POINTER(_UniffiRustBuffer))
-
-# UniFFI future continuation
-_UNIFFI_FUTURE_CONTINUATION_T = ctypes.CFUNCTYPE(None, ctypes.c_size_t, ctypes.c_int8)
+def _uniffi_trait_interface_call(call_status, make_call, write_return_value):
+ try:
+ return write_return_value(make_call())
+ except Exception as e:
+ call_status.code = _UniffiRustCallStatus.CALL_UNEXPECTED_ERROR
+ call_status.error_buf = {{ Type::String.borrow()|lower_fn }}(repr(e))
+def _uniffi_trait_interface_call_with_error(call_status, make_call, write_return_value, error_type, lower_error):
+ try:
+ try:
+ return write_return_value(make_call())
+ except error_type as e:
+ call_status.code = _UniffiRustCallStatus.CALL_ERROR
+ call_status.error_buf = lower_error(e)
+ except Exception as e:
+ call_status.code = _UniffiRustCallStatus.CALL_UNEXPECTED_ERROR
+ call_status.error_buf = {{ Type::String.borrow()|lower_fn }}(repr(e))
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int16Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int16Helper.py
index 99f19dc1c0..befa563384 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int16Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int16Helper.py
@@ -8,5 +8,5 @@ class _UniffiConverterInt16(_UniffiConverterPrimitiveInt):
return buf.read_i16()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_i16(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int32Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int32Helper.py
index 3b142c8749..70644f6717 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int32Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int32Helper.py
@@ -8,5 +8,5 @@ class _UniffiConverterInt32(_UniffiConverterPrimitiveInt):
return buf.read_i32()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_i32(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int64Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int64Helper.py
index 6e94379cbf..232f127bd6 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int64Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int64Helper.py
@@ -8,5 +8,5 @@ class _UniffiConverterInt64(_UniffiConverterPrimitiveInt):
return buf.read_i64()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_i64(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int8Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int8Helper.py
index 732530e3cb..c1de1625e7 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int8Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int8Helper.py
@@ -8,5 +8,5 @@ class _UniffiConverterInt8(_UniffiConverterPrimitiveInt):
return buf.read_i8()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_i8(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/MapTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/MapTemplate.py
index 387227ed09..a09ca28a30 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/MapTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/MapTemplate.py
@@ -3,6 +3,12 @@
class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
@classmethod
+ def check_lower(cls, items):
+ for (key, value) in items.items():
+ {{ key_ffi_converter }}.check_lower(key)
+ {{ value_ffi_converter }}.check_lower(value)
+
+ @classmethod
def write(cls, items, buf):
buf.write_i32(len(items))
for (key, value) in items.items():
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py
index fac6cd5564..1929f9aad6 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py
@@ -1,22 +1,6 @@
# Define some ctypes FFI types that we use in the library
"""
-ctypes type for the foreign executor callback. This is a built-in interface for scheduling
-tasks
-
-Args:
- executor: opaque c_size_t value representing the eventloop
- delay: delay in ms
- task: function pointer to the task callback
- task_data: void pointer to the task callback data
-
-Normally we should call task(task_data) after the detail.
-However, when task is NULL this indicates that Rust has dropped the ForeignExecutor and we should
-decrease the EventLoop refcount.
-"""
-_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_T = ctypes.CFUNCTYPE(ctypes.c_int8, ctypes.c_size_t, ctypes.c_uint32, ctypes.c_void_p, ctypes.c_void_p)
-
-"""
Function pointer for a Rust task, which a callback function that takes a opaque pointer
"""
_UNIFFI_RUST_TASK = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_int8)
@@ -25,7 +9,7 @@ def _uniffi_future_callback_t(return_type):
"""
Factory function to create callback function types for async functions
"""
- return ctypes.CFUNCTYPE(None, ctypes.c_size_t, return_type, _UniffiRustCallStatus)
+ return ctypes.CFUNCTYPE(None, ctypes.c_uint64, return_type, _UniffiRustCallStatus)
def _uniffi_load_indirect():
"""
@@ -72,12 +56,37 @@ def _uniffi_check_api_checksums(lib):
# This is an implementation detail which will be called internally by the public API.
_UniffiLib = _uniffi_load_indirect()
-{%- for func in ci.iter_ffi_function_definitions() %}
+
+{%- for def in ci.ffi_definitions() %}
+{%- match def %}
+{%- when FfiDefinition::CallbackFunction(callback) %}
+{{ callback.name()|ffi_callback_name }} = ctypes.CFUNCTYPE(
+ {%- match callback.return_type() %}
+ {%- when Some(return_type) %}{{ return_type|ffi_type_name }},
+ {%- when None %}None,
+ {%- endmatch %}
+ {%- for arg in callback.arguments() -%}
+ {{ arg.type_().borrow()|ffi_type_name }},
+ {%- endfor -%}
+ {%- if callback.has_rust_call_status_arg() %}
+ ctypes.POINTER(_UniffiRustCallStatus),
+ {%- endif %}
+)
+{%- when FfiDefinition::Struct(ffi_struct) %}
+class {{ ffi_struct.name()|ffi_struct_name }}(ctypes.Structure):
+ _fields_ = [
+ {%- for field in ffi_struct.fields() %}
+ ("{{ field.name()|var_name }}", {{ field.type_().borrow()|ffi_type_name }}),
+ {%- endfor %}
+ ]
+{%- when FfiDefinition::Function(func) %}
_UniffiLib.{{ func.name() }}.argtypes = (
{%- call py::arg_list_ffi_decl(func) -%}
)
_UniffiLib.{{ func.name() }}.restype = {% match func.return_type() %}{% when Some with (type_) %}{{ type_|ffi_type_name }}{% when None %}None{% endmatch %}
+{%- endmatch %}
{%- endfor %}
+
{# Ensure to call the contract verification only after we defined all functions. -#}
_uniffi_check_contract_api_version(_UniffiLib)
_uniffi_check_api_checksums(_UniffiLib)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py
index 7e98f7c46f..18dca4743a 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py
@@ -1,14 +1,33 @@
{%- let obj = ci|get_object_definition(name) %}
-
-class {{ type_name }}:
+{%- let (protocol_name, impl_name) = obj|object_names %}
+{%- let methods = obj.methods() %}
+{%- let protocol_docstring = obj.docstring() %}
+
+{% include "Protocol.py" %}
+
+{% if ci.is_name_used_as_error(name) %}
+class {{ impl_name }}(Exception):
+{%- else %}
+class {{ impl_name }}:
+{%- endif %}
+ {%- call py::docstring(obj, 4) %}
_pointer: ctypes.c_void_p
{%- match obj.primary_constructor() %}
{%- when Some with (cons) %}
+{%- if cons.is_async() %}
+ def __init__(self, *args, **kw):
+ raise ValueError("async constructors not supported.")
+{%- else %}
def __init__(self, {% call py::arg_list_decl(cons) -%}):
+ {%- call py::docstring(cons, 8) %}
{%- call py::setup_args_extra_indent(cons) %}
self._pointer = {% call py::to_ffi_call(cons) %}
+{%- endif %}
{%- when None %}
+ {# no __init__ means simple construction without a pointer works, which can confuse #}
+ def __init__(self, *args, **kwargs):
+ raise ValueError("This class has no default constructor")
{%- endmatch %}
def __del__(self):
@@ -17,6 +36,9 @@ class {{ type_name }}:
if pointer is not None:
_rust_call(_UniffiLib.{{ obj.ffi_object_free().name() }}, pointer)
+ def _uniffi_clone_pointer(self):
+ return _rust_call(_UniffiLib.{{ obj.ffi_object_clone().name() }}, self._pointer)
+
# Used by alternative constructors or any methods which return this type.
@classmethod
def _make_instance_(cls, pointer):
@@ -29,17 +51,32 @@ class {{ type_name }}:
{%- for cons in obj.alternate_constructors() %}
@classmethod
+{%- if cons.is_async() %}
+ async def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}):
+ {%- call py::docstring(cons, 8) %}
+ {%- call py::setup_args_extra_indent(cons) %}
+
+ return await _uniffi_rust_call_async(
+ _UniffiLib.{{ cons.ffi_func().name() }}({% call py::arg_list_lowered(cons) %}),
+ _UniffiLib.{{ cons.ffi_rust_future_poll(ci) }},
+ _UniffiLib.{{ cons.ffi_rust_future_complete(ci) }},
+ _UniffiLib.{{ cons.ffi_rust_future_free(ci) }},
+ {{ ffi_converter_name }}.lift,
+ {% call py::error_ffi_converter(cons) %}
+ )
+{%- else %}
def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}):
+ {%- call py::docstring(cons, 8) %}
{%- call py::setup_args_extra_indent(cons) %}
# Call the (fallible) function before creating any half-baked object instances.
pointer = {% call py::to_ffi_call(cons) %}
return cls._make_instance_(pointer)
+{%- endif %}
{% endfor %}
{%- for meth in obj.methods() -%}
{%- call py::method_decl(meth.name()|fn_name, meth) %}
-{% endfor %}
-
+{%- endfor %}
{%- for tm in obj.uniffi_traits() -%}
{%- match tm %}
{%- when UniffiTrait::Debug { fmt } %}
@@ -51,37 +88,84 @@ class {{ type_name }}:
if not isinstance(other, {{ type_name }}):
return NotImplemented
- return {{ eq.return_type().unwrap()|lift_fn }}({% call py::to_ffi_call_with_prefix("self._pointer", eq) %})
+ return {{ eq.return_type().unwrap()|lift_fn }}({% call py::to_ffi_call_with_prefix("self._uniffi_clone_pointer()", eq) %})
def __ne__(self, other: object) -> {{ ne.return_type().unwrap()|type_name }}:
if not isinstance(other, {{ type_name }}):
return NotImplemented
- return {{ ne.return_type().unwrap()|lift_fn }}({% call py::to_ffi_call_with_prefix("self._pointer", ne) %})
+ return {{ ne.return_type().unwrap()|lift_fn }}({% call py::to_ffi_call_with_prefix("self._uniffi_clone_pointer()", ne) %})
{%- when UniffiTrait::Hash { hash } %}
{%- call py::method_decl("__hash__", hash) %}
-{% endmatch %}
-{% endfor %}
-
-
-class {{ ffi_converter_name }}:
+{%- endmatch %}
+{%- endfor %}
+
+{%- if obj.has_callback_interface() %}
+{%- let ffi_init_callback = obj.ffi_init_callback() %}
+{%- let vtable = obj.vtable().expect("trait interface should have a vtable") %}
+{%- let vtable_methods = obj.vtable_methods() %}
+{% include "CallbackInterfaceImpl.py" %}
+{%- endif %}
+
+{# Objects as error #}
+{%- if ci.is_name_used_as_error(name) %}
+{# Due to some mismatches in the ffi converter mechanisms, errors are forced to be a RustBuffer #}
+class {{ ffi_converter_name }}__as_error(_UniffiConverterRustBuffer):
@classmethod
def read(cls, buf):
- ptr = buf.read_u64()
- if ptr == 0:
- raise InternalError("Raw pointer value was null")
- return cls.lift(ptr)
+ raise NotImplementedError()
@classmethod
def write(cls, value, buf):
- if not isinstance(value, {{ type_name }}):
- raise TypeError("Expected {{ type_name }} instance, {} found".format(type(value).__name__))
- buf.write_u64(cls.lower(value))
+ raise NotImplementedError()
@staticmethod
def lift(value):
- return {{ type_name }}._make_instance_(value)
+ # Errors are always a rust buffer holding a pointer - which is a "read"
+ with value.consume_with_stream() as stream:
+ return {{ ffi_converter_name }}.read(stream)
@staticmethod
def lower(value):
- return value._pointer
+ raise NotImplementedError()
+
+{%- endif %}
+
+class {{ ffi_converter_name }}:
+ {%- if obj.has_callback_interface() %}
+ _handle_map = _UniffiHandleMap()
+ {%- endif %}
+
+ @staticmethod
+ def lift(value: int):
+ return {{ impl_name }}._make_instance_(value)
+
+ @staticmethod
+ def check_lower(value: {{ type_name }}):
+ {%- if obj.has_callback_interface() %}
+ pass
+ {%- else %}
+ if not isinstance(value, {{ impl_name }}):
+ raise TypeError("Expected {{ impl_name }} instance, {} found".format(type(value).__name__))
+ {%- endif %}
+
+ @staticmethod
+ def lower(value: {{ protocol_name }}):
+ {%- if obj.has_callback_interface() %}
+ return {{ ffi_converter_name }}._handle_map.insert(value)
+ {%- else %}
+ if not isinstance(value, {{ impl_name }}):
+ raise TypeError("Expected {{ impl_name }} instance, {} found".format(type(value).__name__))
+ return value._uniffi_clone_pointer()
+ {%- endif %}
+
+ @classmethod
+ def read(cls, buf: _UniffiRustBuffer):
+ ptr = buf.read_u64()
+ if ptr == 0:
+ raise InternalError("Raw pointer value was null")
+ return cls.lift(ptr)
+
+ @classmethod
+ def write(cls, value: {{ protocol_name }}, buf: _UniffiRustBuffer):
+ buf.write_u64(cls.lower(value))
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/OptionalTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/OptionalTemplate.py
index e406c51d49..4c07ae3e34 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/OptionalTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/OptionalTemplate.py
@@ -2,6 +2,11 @@
class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
@classmethod
+ def check_lower(cls, value):
+ if value is not None:
+ {{ inner_ffi_converter }}.check_lower(value)
+
+ @classmethod
def write(cls, value, buf):
if value is None:
buf.write_u8(0)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/PointerManager.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/PointerManager.py
deleted file mode 100644
index 23aa28eab4..0000000000
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/PointerManager.py
+++ /dev/null
@@ -1,68 +0,0 @@
-class _UniffiPointerManagerCPython:
- """
- Manage giving out pointers to Python objects on CPython
-
- This class is used to generate opaque pointers that reference Python objects to pass to Rust.
- It assumes a CPython platform. See _UniffiPointerManagerGeneral for the alternative.
- """
-
- def new_pointer(self, obj):
- """
- Get a pointer for an object as a ctypes.c_size_t instance
-
- Each call to new_pointer() must be balanced with exactly one call to release_pointer()
-
- This returns a ctypes.c_size_t. This is always the same size as a pointer and can be
- interchanged with pointers for FFI function arguments and return values.
- """
- # IncRef the object since we're going to pass a pointer to Rust
- ctypes.pythonapi.Py_IncRef(ctypes.py_object(obj))
- # id() is the object address on CPython
- # (https://docs.python.org/3/library/functions.html#id)
- return id(obj)
-
- def release_pointer(self, address):
- py_obj = ctypes.cast(address, ctypes.py_object)
- obj = py_obj.value
- ctypes.pythonapi.Py_DecRef(py_obj)
- return obj
-
- def lookup(self, address):
- return ctypes.cast(address, ctypes.py_object).value
-
-class _UniffiPointerManagerGeneral:
- """
- Manage giving out pointers to Python objects on non-CPython platforms
-
- This has the same API as _UniffiPointerManagerCPython, but doesn't assume we're running on
- CPython and is slightly slower.
-
- Instead of using real pointers, it maps integer values to objects and returns the keys as
- c_size_t values.
- """
-
- def __init__(self):
- self._map = {}
- self._lock = threading.Lock()
- self._current_handle = 0
-
- def new_pointer(self, obj):
- with self._lock:
- handle = self._current_handle
- self._current_handle += 1
- self._map[handle] = obj
- return handle
-
- def release_pointer(self, handle):
- with self._lock:
- return self._map.pop(handle)
-
- def lookup(self, handle):
- with self._lock:
- return self._map[handle]
-
-# Pick an pointer manager implementation based on the platform
-if platform.python_implementation() == 'CPython':
- _UniffiPointerManager = _UniffiPointerManagerCPython # type: ignore
-else:
- _UniffiPointerManager = _UniffiPointerManagerGeneral # type: ignore
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Protocol.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Protocol.py
new file mode 100644
index 0000000000..3b7e93596a
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Protocol.py
@@ -0,0 +1,9 @@
+class {{ protocol_name }}(typing.Protocol):
+ {%- call py::docstring_value(protocol_docstring, 4) %}
+ {%- for meth in methods.iter() %}
+ def {{ meth.name()|fn_name }}(self, {% call py::arg_list_decl(meth) %}):
+ {%- call py::docstring(meth, 8) %}
+ raise NotImplementedError
+ {%- else %}
+ pass
+ {%- endfor %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py
index 99a30e120f..0b5634eb52 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py
@@ -1,11 +1,14 @@
{%- let rec = ci|get_record_definition(name) %}
class {{ type_name }}:
- {% for field in rec.fields() %}
- {{- field.name()|var_name }}: "{{- field|type_name }}";
+ {%- call py::docstring(rec, 4) %}
+ {%- for field in rec.fields() %}
+ {{ field.name()|var_name }}: "{{ field|type_name }}"
+ {%- call py::docstring(field, 4) %}
{%- endfor %}
+ {%- if rec.has_fields() %}
@typing.no_type_check
- def __init__(self, {% for field in rec.fields() %}
+ def __init__(self, *, {% for field in rec.fields() %}
{{- field.name()|var_name }}: "{{- field|type_name }}"
{%- if field.default_value().is_some() %} = _DEFAULT{% endif %}
{%- if !loop.last %}, {% endif %}
@@ -22,6 +25,7 @@ class {{ type_name }}:
self.{{ field_name }} = {{ field_name }}
{%- endmatch %}
{%- endfor %}
+ {%- endif %}
def __str__(self):
return "{{ type_name }}({% for field in rec.fields() %}{{ field.name()|var_name }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in rec.fields() %}self.{{ field.name()|var_name }}{% if loop.last %}{% else %}, {% endif %}{% endfor %})
@@ -43,7 +47,21 @@ class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
)
@staticmethod
+ def check_lower(value):
+ {%- if rec.fields().is_empty() %}
+ pass
+ {%- else %}
+ {%- for field in rec.fields() %}
+ {{ field|check_lower_fn }}(value.{{ field.name()|var_name }})
+ {%- endfor %}
+ {%- endif %}
+
+ @staticmethod
def write(value, buf):
+ {%- if rec.has_fields() %}
{%- for field in rec.fields() %}
{{ field|write_fn }}(value.{{ field.name()|var_name }}, buf)
{%- endfor %}
+ {%- else %}
+ pass
+ {%- endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferHelper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferHelper.py
index daabd5b4b9..4db74fb157 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferHelper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferHelper.py
@@ -1,28 +1,16 @@
# Types conforming to `_UniffiConverterPrimitive` pass themselves directly over the FFI.
class _UniffiConverterPrimitive:
@classmethod
- def check(cls, value):
- return value
-
- @classmethod
def lift(cls, value):
return value
@classmethod
def lower(cls, value):
- return cls.lowerUnchecked(cls.check(value))
-
- @classmethod
- def lowerUnchecked(cls, value):
return value
- @classmethod
- def write(cls, value, buf):
- cls.write_unchecked(cls.check(value), buf)
-
class _UniffiConverterPrimitiveInt(_UniffiConverterPrimitive):
@classmethod
- def check(cls, value):
+ def check_lower(cls, value):
try:
value = value.__index__()
except Exception:
@@ -31,18 +19,16 @@ class _UniffiConverterPrimitiveInt(_UniffiConverterPrimitive):
raise TypeError("__index__ returned non-int (type {})".format(type(value).__name__))
if not cls.VALUE_MIN <= value < cls.VALUE_MAX:
raise ValueError("{} requires {} <= value < {}".format(cls.CLASS_NAME, cls.VALUE_MIN, cls.VALUE_MAX))
- return super().check(value)
class _UniffiConverterPrimitiveFloat(_UniffiConverterPrimitive):
@classmethod
- def check(cls, value):
+ def check_lower(cls, value):
try:
value = value.__float__()
except Exception:
raise TypeError("must be real number, not {}".format(type(value).__name__))
if not isinstance(value, float):
raise TypeError("__float__ returned non-float (type {})".format(type(value).__name__))
- return super().check(value)
# Helper class for wrapper types that will always go through a _UniffiRustBuffer.
# Classes should inherit from this and implement the `read` and `write` static methods.
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferTemplate.py
index c317a632fc..44e0ba1001 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferTemplate.py
@@ -1,12 +1,16 @@
class _UniffiRustBuffer(ctypes.Structure):
_fields_ = [
- ("capacity", ctypes.c_int32),
- ("len", ctypes.c_int32),
+ ("capacity", ctypes.c_uint64),
+ ("len", ctypes.c_uint64),
("data", ctypes.POINTER(ctypes.c_char)),
]
@staticmethod
+ def default():
+ return _UniffiRustBuffer(0, 0, None)
+
+ @staticmethod
def alloc(size):
return _rust_call(_UniffiLib.{{ ci.ffi_rustbuffer_alloc().name() }}, size)
@@ -137,9 +141,6 @@ class _UniffiRustBufferStream:
def read_double(self):
return self._unpack_from(8, ">d")
- def read_c_size_t(self):
- return self._unpack_from(ctypes.sizeof(ctypes.c_size_t) , "@N")
-
class _UniffiRustBufferBuilder:
"""
Helper for structured writing of bytes into a _UniffiRustBuffer.
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/SequenceTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/SequenceTemplate.py
index 3c9f5a4596..3c30d9f9f5 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/SequenceTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/SequenceTemplate.py
@@ -2,6 +2,11 @@
class {{ ffi_converter_name}}(_UniffiConverterRustBuffer):
@classmethod
+ def check_lower(cls, value):
+ for item in value:
+ {{ inner_ffi_converter }}.check_lower(item)
+
+ @classmethod
def write(cls, value, buf):
items = len(value)
buf.write_i32(items)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/StringHelper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/StringHelper.py
index 40890b6abc..d574edd09f 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/StringHelper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/StringHelper.py
@@ -1,6 +1,6 @@
class _UniffiConverterString:
@staticmethod
- def check(value):
+ def check_lower(value):
if not isinstance(value, str):
raise TypeError("argument must be str, not {}".format(type(value).__name__))
return value
@@ -15,7 +15,6 @@ class _UniffiConverterString:
@staticmethod
def write(value, buf):
- value = _UniffiConverterString.check(value)
utf8_bytes = value.encode("utf-8")
buf.write_i32(len(utf8_bytes))
buf.write(utf8_bytes)
@@ -27,7 +26,6 @@ class _UniffiConverterString:
@staticmethod
def lower(value):
- value = _UniffiConverterString.check(value)
with _UniffiRustBuffer.alloc_with_builder() as builder:
builder.write(value.encode("utf-8"))
return builder.finalize()
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TimestampHelper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TimestampHelper.py
index 8402f6095d..76d7f8bcdb 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TimestampHelper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TimestampHelper.py
@@ -18,6 +18,10 @@ class _UniffiConverterTimestamp(_UniffiConverterRustBuffer):
return datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc) - datetime.timedelta(seconds=-seconds, microseconds=microseconds)
@staticmethod
+ def check_lower(value):
+ pass
+
+ @staticmethod
def write(value, buf):
if value >= datetime.datetime.fromtimestamp(0, datetime.timezone.utc):
sign = 1
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py
index f258b60a1c..230b9e853f 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py
@@ -1,7 +1,15 @@
{%- if func.is_async() %}
-def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}):
- return _uniffi_rust_call_async(
+{%- match func.return_type() -%}
+{%- when Some with (return_type) %}
+async def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}":
+{% when None %}
+async def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> None:
+{% endmatch %}
+
+ {%- call py::docstring(func, 4) %}
+ {%- call py::setup_args(func) %}
+ return await _uniffi_rust_call_async(
_UniffiLib.{{ func.ffi_func().name() }}({% call py::arg_list_lowered(func) %}),
_UniffiLib.{{func.ffi_rust_future_poll(ci) }},
_UniffiLib.{{func.ffi_rust_future_complete(ci) }},
@@ -13,13 +21,7 @@ def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}):
{%- when None %}
lambda val: None,
{% endmatch %}
- # Error FFI converter
- {%- match func.throws_type() %}
- {%- when Some(e) %}
- {{ e|ffi_converter_name }},
- {%- when None %}
- None,
- {%- endmatch %}
+ {% call py::error_ffi_converter(func) %}
)
{%- else %}
@@ -27,11 +29,13 @@ def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}):
{%- when Some with (return_type) %}
def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}":
+ {%- call py::docstring(func, 4) %}
{%- call py::setup_args(func) %}
return {{ return_type|lift_fn }}({% call py::to_ffi_call(func) %})
{% when None %}
-def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}):
+def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> None:
+ {%- call py::docstring(func, 4) %}
{%- call py::setup_args(func) %}
{% call py::to_ffi_call(func) %}
{% endmatch %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Types.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Types.py
index 5e05314c37..4aaed253e0 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Types.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Types.py
@@ -85,7 +85,7 @@
{%- when Type::Map { key_type, value_type } %}
{%- include "MapTemplate.py" %}
-{%- when Type::CallbackInterface { name: id, module_path } %}
+{%- when Type::CallbackInterface { name, module_path } %}
{%- include "CallbackInterfaceTemplate.py" %}
{%- when Type::Custom { name, module_path, builtin } %}
@@ -94,9 +94,6 @@
{%- when Type::External { name, module_path, namespace, kind, tagged } %}
{%- include "ExternalTemplate.py" %}
-{%- when Type::ForeignExecutor %}
-{%- include "ForeignExecutorTemplate.py" %}
-
{%- else %}
{%- endmatch %}
{%- endfor %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt16Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt16Helper.py
index 081c6731ce..039bf76162 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt16Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt16Helper.py
@@ -8,5 +8,5 @@ class _UniffiConverterUInt16(_UniffiConverterPrimitiveInt):
return buf.read_u16()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_u16(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt32Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt32Helper.py
index b80e75177d..1650bf9b60 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt32Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt32Helper.py
@@ -8,5 +8,5 @@ class _UniffiConverterUInt32(_UniffiConverterPrimitiveInt):
return buf.read_u32()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_u32(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt64Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt64Helper.py
index 4b87e58547..f354545e26 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt64Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt64Helper.py
@@ -8,5 +8,5 @@ class _UniffiConverterUInt64(_UniffiConverterPrimitiveInt):
return buf.read_u64()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_u64(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt8Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt8Helper.py
index 33026706f2..cee130b4d9 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt8Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt8Helper.py
@@ -8,5 +8,5 @@ class _UniffiConverterUInt8(_UniffiConverterPrimitiveInt):
return buf.read_u8()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_u8(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/macros.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/macros.py
index ef3b1bb94d..6818a8c107 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/macros.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/macros.py
@@ -5,27 +5,29 @@
#}
{%- macro to_ffi_call(func) -%}
- {%- match func.throws_type() -%}
- {%- when Some with (e) -%}
-_rust_call_with_error({{ e|ffi_converter_name }},
- {%- else -%}
-_rust_call(
- {%- endmatch -%}
- _UniffiLib.{{ func.ffi_func().name() }},
- {%- call arg_list_lowered(func) -%}
-)
+{%- call _to_ffi_call_with_prefix_arg("", func) %}
{%- endmacro -%}
{%- macro to_ffi_call_with_prefix(prefix, func) -%}
- {%- match func.throws_type() -%}
- {%- when Some with (e) -%}
-_rust_call_with_error(
- {{ e|ffi_converter_name }},
- {%- else -%}
+{%- call _to_ffi_call_with_prefix_arg(format!("{},", prefix), func) %}
+{%- endmacro -%}
+
+{%- macro _to_ffi_call_with_prefix_arg(prefix, func) -%}
+{%- match func.throws_type() -%}
+{%- when Some with (e) -%}
+{%- match e -%}
+{%- when Type::Enum { name, module_path } -%}
+_rust_call_with_error({{ e|ffi_converter_name }},
+{%- when Type::Object { name, module_path, imp } -%}
+_rust_call_with_error({{ e|ffi_converter_name }}__as_error,
+{%- else %}
+# unsupported error type!
+{%- endmatch %}
+{%- else -%}
_rust_call(
- {%- endmatch -%}
+{%- endmatch -%}
_UniffiLib.{{ func.ffi_func().name() }},
- {{- prefix }},
+ {{- prefix }}
{%- call arg_list_lowered(func) -%}
)
{%- endmacro -%}
@@ -37,6 +39,19 @@ _rust_call(
{%- endfor %}
{%- endmacro -%}
+{%- macro docstring_value(maybe_docstring, indent_spaces) %}
+{%- match maybe_docstring %}
+{%- when Some(docstring) %}
+{{ docstring|docstring(indent_spaces) }}
+{{ "" }}
+{%- else %}
+{%- endmatch %}
+{%- endmacro %}
+
+{%- macro docstring(defn, indent_spaces) %}
+{%- call docstring_value(defn.docstring(), indent_spaces) %}
+{%- endmacro %}
+
{#-
// Arglist as used in Python declarations of methods, functions and constructors.
// Note the var_name and type_name filters.
@@ -76,6 +91,7 @@ _rust_call(
if {{ arg.name()|var_name }} is _DEFAULT:
{{ arg.name()|var_name }} = {{ literal|literal_py(arg.as_type().borrow()) }}
{%- endmatch %}
+ {{ arg|check_lower_fn }}({{ arg.name()|var_name }})
{% endfor -%}
{%- endmacro -%}
@@ -91,6 +107,7 @@ _rust_call(
if {{ arg.name()|var_name }} is _DEFAULT:
{{ arg.name()|var_name }} = {{ literal|literal_py(arg.as_type().borrow()) }}
{%- endmatch %}
+ {{ arg|check_lower_fn }}({{ arg.name()|var_name }})
{% endfor -%}
{%- endmacro -%}
@@ -100,11 +117,18 @@ _rust_call(
{%- macro method_decl(py_method_name, meth) %}
{% if meth.is_async() %}
- def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}):
+{%- match meth.return_type() %}
+{%- when Some with (return_type) %}
+ async def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}) -> "{{ return_type|type_name }}":
+{%- when None %}
+ async def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}) -> None:
+{% endmatch %}
+
+ {%- call docstring(meth, 8) %}
{%- call setup_args_extra_indent(meth) %}
- return _uniffi_rust_call_async(
+ return await _uniffi_rust_call_async(
_UniffiLib.{{ meth.ffi_func().name() }}(
- self._pointer, {% call arg_list_lowered(meth) %}
+ self._uniffi_clone_pointer(), {% call arg_list_lowered(meth) %}
),
_UniffiLib.{{ meth.ffi_rust_future_poll(ci) }},
_UniffiLib.{{ meth.ffi_rust_future_complete(ci) }},
@@ -116,13 +140,7 @@ _rust_call(
{%- when None %}
lambda val: None,
{% endmatch %}
- # Error FFI converter
- {%- match meth.throws_type() %}
- {%- when Some(e) %}
- {{ e|ffi_converter_name }},
- {%- when None %}
- None,
- {%- endmatch %}
+ {% call error_ffi_converter(meth) %}
)
{%- else -%}
@@ -131,17 +149,36 @@ _rust_call(
{%- when Some with (return_type) %}
def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}) -> "{{ return_type|type_name }}":
+ {%- call docstring(meth, 8) %}
{%- call setup_args_extra_indent(meth) %}
return {{ return_type|lift_fn }}(
- {% call to_ffi_call_with_prefix("self._pointer", meth) %}
+ {% call to_ffi_call_with_prefix("self._uniffi_clone_pointer()", meth) %}
)
{%- when None %}
- def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}):
+ def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}) -> None:
+ {%- call docstring(meth, 8) %}
{%- call setup_args_extra_indent(meth) %}
- {% call to_ffi_call_with_prefix("self._pointer", meth) %}
+ {% call to_ffi_call_with_prefix("self._uniffi_clone_pointer()", meth) %}
{% endmatch %}
{% endif %}
{% endmacro %}
+
+{%- macro error_ffi_converter(func) %}
+ # Error FFI converter
+{% match func.throws_type() %}
+{%- when Some(e) %}
+{%- match e -%}
+{%- when Type::Enum { name, module_path } -%}
+ {{ e|ffi_converter_name }},
+{%- when Type::Object { name, module_path, imp } -%}
+ {{ e|ffi_converter_name }}__as_error,
+{%- else %}
+ # unsupported error type!
+{%- endmatch %}
+{%- when None %}
+ None,
+{%- endmatch %}
+{% endmacro %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/wrapper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/wrapper.py
index 24c3290ff7..1ccd6821c0 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/wrapper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/wrapper.py
@@ -1,3 +1,5 @@
+{%- call py::docstring_value(ci.namespace_docstring(), 0) %}
+
# This file was autogenerated by some hot garbage in the `uniffi` crate.
# Trust me, you don't want to mess with it!
@@ -13,6 +15,7 @@
# compile the rust component. The easiest way to ensure this is to bundle the Python
# helpers directly inline like we're doing here.
+from __future__ import annotations
import os
import sys
import ctypes
@@ -20,6 +23,9 @@ import enum
import struct
import contextlib
import datetime
+import threading
+import itertools
+import traceback
import typing
{%- if ci.has_async_fns() %}
import asyncio
@@ -34,20 +40,20 @@ _DEFAULT = object()
{% include "RustBufferTemplate.py" %}
{% include "Helpers.py" %}
-{% include "PointerManager.py" %}
+{% include "HandleMap.py" %}
{% include "RustBufferHelper.py" %}
# Contains loading, initialization code, and the FFI Function declarations.
{% include "NamespaceLibraryTemplate.py" %}
+# Public interface members begin here.
+{{ type_helper_code }}
+
# Async support
{%- if ci.has_async_fns() %}
{%- include "Async.py" %}
{%- endif %}
-# Public interface members begin here.
-{{ type_helper_code }}
-
{%- for func in ci.function_definitions() %}
{%- include "TopLevelFunctionTemplate.py" %}
{%- endfor %}
@@ -69,6 +75,9 @@ __all__ = [
{%- for c in ci.callback_interface_definitions() %}
"{{ c.name()|class_name }}",
{%- endfor %}
+ {%- if ci.has_async_fns() %}
+ "uniffi_set_event_loop",
+ {%- endif %}
]
{% import "macros.py" as py %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/test.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/test.rs
index 0fcf09996f..0c23140b33 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/test.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/test.rs
@@ -2,10 +2,8 @@
License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use crate::{
- bindings::{RunScriptOptions, TargetLanguage},
- library_mode::generate_bindings,
-};
+use crate::bindings::TargetLanguage;
+use crate::{bindings::RunScriptOptions, library_mode::generate_bindings, BindingGeneratorDefault};
use anyhow::{Context, Result};
use camino::Utf8Path;
use std::env;
@@ -34,14 +32,18 @@ pub fn run_script(
args: Vec<String>,
_options: &RunScriptOptions,
) -> Result<()> {
- let script_path = Utf8Path::new(".").join(script_file).canonicalize_utf8()?;
+ let script_path = Utf8Path::new(script_file).canonicalize_utf8()?;
let test_helper = UniFFITestHelper::new(crate_name)?;
let out_dir = test_helper.create_out_dir(tmp_dir, &script_path)?;
let cdylib_path = test_helper.copy_cdylib_to_out_dir(&out_dir)?;
generate_bindings(
&cdylib_path,
None,
- &[TargetLanguage::Python],
+ &BindingGeneratorDefault {
+ target_languages: vec![TargetLanguage::Python],
+ try_format_code: false,
+ },
+ None,
&out_dir,
false,
)?;
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs b/third_party/rust/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs
index 1f1bf8e299..04841b459c 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs
@@ -2,15 +2,39 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use anyhow::Result;
+use anyhow::{bail, Result};
use askama::Template;
+use camino::Utf8Path;
use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
use serde::{Deserialize, Serialize};
use std::borrow::Borrow;
use std::collections::HashMap;
+use crate::bindings::ruby;
use crate::interface::*;
-use crate::BindingsConfig;
+use crate::{BindingGenerator, BindingsConfig};
+
+pub struct RubyBindingGenerator;
+impl BindingGenerator for RubyBindingGenerator {
+ type Config = Config;
+
+ fn write_bindings(
+ &self,
+ ci: &ComponentInterface,
+ config: &Config,
+ out_dir: &Utf8Path,
+ try_format_code: bool,
+ ) -> Result<()> {
+ ruby::write_bindings(config, ci, out_dir, try_format_code)
+ }
+
+ fn check_library_path(&self, library_path: &Utf8Path, cdylib_name: Option<&str>) -> Result<()> {
+ if cdylib_name.is_none() {
+ bail!("Generate bindings for Ruby requires a cdylib, but {library_path} was given");
+ }
+ Ok(())
+ }
+}
const RESERVED_WORDS: &[&str] = &[
"alias", "and", "BEGIN", "begin", "break", "case", "class", "def", "defined?", "do", "else",
@@ -57,7 +81,6 @@ pub fn canonical_name(t: &Type) -> String {
Type::CallbackInterface { name, .. } => format!("CallbackInterface{name}"),
Type::Timestamp => "Timestamp".into(),
Type::Duration => "Duration".into(),
- Type::ForeignExecutor => "ForeignExecutor".into(),
// Recursive types.
// These add a prefix to the name of the underlying type.
// The component API definition cannot give names to recursive types, so as long as the
@@ -150,20 +173,20 @@ mod filters {
FfiType::UInt64 => ":uint64".to_string(),
FfiType::Float32 => ":float".to_string(),
FfiType::Float64 => ":double".to_string(),
+ FfiType::Handle => ":uint64".to_string(),
FfiType::RustArcPtr(_) => ":pointer".to_string(),
FfiType::RustBuffer(_) => "RustBuffer.by_value".to_string(),
+ FfiType::RustCallStatus => "RustCallStatus".to_string(),
FfiType::ForeignBytes => "ForeignBytes".to_string(),
- FfiType::ForeignCallback => unimplemented!("Callback interfaces are not implemented"),
- FfiType::ForeignExecutorCallback => {
- unimplemented!("Foreign executors are not implemented")
- }
- FfiType::ForeignExecutorHandle => {
- unimplemented!("Foreign executors are not implemented")
- }
- FfiType::RustFutureHandle
- | FfiType::RustFutureContinuationCallback
- | FfiType::RustFutureContinuationData => {
- unimplemented!("Async functions are not implemented")
+ FfiType::Callback(_) => unimplemented!("FFI Callbacks not implemented"),
+ // Note: this can't just be `unimplemented!()` because some of the FFI function
+ // definitions use references. Those FFI functions aren't actually used, so we just
+ // pick something that runs and makes some sense. Revisit this once the references
+ // are actually implemented.
+ FfiType::Reference(_) => ":pointer".to_string(),
+ FfiType::VoidPointer => ":pointer".to_string(),
+ FfiType::Struct(_) => {
+ unimplemented!("Structs are not implemented")
}
})
}
@@ -179,7 +202,8 @@ mod filters {
}
// use the double-quote form to match with the other languages, and quote escapes.
Literal::String(s) => format!("\"{s}\""),
- Literal::Null => "nil".into(),
+ Literal::None => "nil".into(),
+ Literal::Some { inner } => literal_rb(inner)?,
Literal::EmptySequence => "[]".into(),
Literal::EmptyMap => "{}".into(),
Literal::Enum(v, type_) => match type_ {
@@ -264,7 +288,24 @@ mod filters {
}
Type::External { .. } => panic!("No support for external types, yet"),
Type::Custom { .. } => panic!("No support for custom types, yet"),
- Type::ForeignExecutor => unimplemented!("Foreign executors are not implemented"),
+ })
+ }
+
+ pub fn check_lower_rb(nm: &str, type_: &Type) -> Result<String, askama::Error> {
+ Ok(match type_ {
+ Type::Object { name, .. } => {
+ format!("({}.uniffi_check_lower {nm})", class_name_rb(name)?)
+ }
+ Type::Enum { .. }
+ | Type::Record { .. }
+ | Type::Optional { .. }
+ | Type::Sequence { .. }
+ | Type::Map { .. } => format!(
+ "RustBuffer.check_lower_{}({})",
+ class_name_rb(&canonical_name(type_))?,
+ nm
+ ),
+ _ => "".to_owned(),
})
}
@@ -283,7 +324,7 @@ mod filters {
Type::Boolean => format!("({nm} ? 1 : 0)"),
Type::String => format!("RustBuffer.allocFromString({nm})"),
Type::Bytes => format!("RustBuffer.allocFromBytes({nm})"),
- Type::Object { name, .. } => format!("({}._uniffi_lower {nm})", class_name_rb(name)?),
+ Type::Object { name, .. } => format!("({}.uniffi_lower {nm})", class_name_rb(name)?),
Type::CallbackInterface { .. } => {
panic!("No support for lowering callback interfaces yet")
}
@@ -300,7 +341,6 @@ mod filters {
),
Type::External { .. } => panic!("No support for lowering external types, yet"),
Type::Custom { .. } => panic!("No support for lowering custom types, yet"),
- Type::ForeignExecutor => unimplemented!("Foreign executors are not implemented"),
})
}
@@ -318,7 +358,7 @@ mod filters {
Type::Boolean => format!("1 == {nm}"),
Type::String => format!("{nm}.consumeIntoString"),
Type::Bytes => format!("{nm}.consumeIntoBytes"),
- Type::Object { name, .. } => format!("{}._uniffi_allocate({nm})", class_name_rb(name)?),
+ Type::Object { name, .. } => format!("{}.uniffi_allocate({nm})", class_name_rb(name)?),
Type::CallbackInterface { .. } => {
panic!("No support for lifting callback interfaces, yet")
}
@@ -341,7 +381,6 @@ mod filters {
),
Type::External { .. } => panic!("No support for lifting external types, yet"),
Type::Custom { .. } => panic!("No support for lifting custom types, yet"),
- Type::ForeignExecutor => unimplemented!("Foreign executors are not implemented"),
})
}
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb
index 677c5c729b..ba2caf7380 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb
@@ -2,18 +2,18 @@ class {{ obj.name()|class_name_rb }}
# A private helper for initializing instances of the class from a raw pointer,
# bypassing any initialization logic and ensuring they are GC'd properly.
- def self._uniffi_allocate(pointer)
+ def self.uniffi_allocate(pointer)
pointer.autorelease = false
inst = allocate
inst.instance_variable_set :@pointer, pointer
- ObjectSpace.define_finalizer(inst, _uniffi_define_finalizer_by_pointer(pointer, inst.object_id))
+ ObjectSpace.define_finalizer(inst, uniffi_define_finalizer_by_pointer(pointer, inst.object_id))
return inst
end
# A private helper for registering an object finalizer.
# N.B. it's important that this does not capture a reference
# to the actual instance, only its underlying pointer.
- def self._uniffi_define_finalizer_by_pointer(pointer, object_id)
+ def self.uniffi_define_finalizer_by_pointer(pointer, object_id)
Proc.new do |_id|
{{ ci.namespace()|class_name_rb }}.rust_call(
:{{ obj.ffi_object_free().name() }},
@@ -25,31 +25,41 @@ class {{ obj.name()|class_name_rb }}
# A private helper for lowering instances into a raw pointer.
# This does an explicit typecheck, because accidentally lowering a different type of
# object in a place where this type is expected, could lead to memory unsafety.
- def self._uniffi_lower(inst)
+ def self.uniffi_check_lower(inst)
if not inst.is_a? self
raise TypeError.new "Expected a {{ obj.name()|class_name_rb }} instance, got #{inst}"
end
- return inst.instance_variable_get :@pointer
+ end
+
+ def uniffi_clone_pointer()
+ return {{ ci.namespace()|class_name_rb }}.rust_call(
+ :{{ obj.ffi_object_clone().name() }},
+ @pointer
+ )
+ end
+
+ def self.uniffi_lower(inst)
+ return inst.uniffi_clone_pointer()
end
{%- match obj.primary_constructor() %}
{%- when Some with (cons) %}
def initialize({% call rb::arg_list_decl(cons) -%})
- {%- call rb::coerce_args_extra_indent(cons) %}
+ {%- call rb::setup_args_extra_indent(cons) %}
pointer = {% call rb::to_ffi_call(cons) %}
@pointer = pointer
- ObjectSpace.define_finalizer(self, self.class._uniffi_define_finalizer_by_pointer(pointer, self.object_id))
+ ObjectSpace.define_finalizer(self, self.class.uniffi_define_finalizer_by_pointer(pointer, self.object_id))
end
{%- when None %}
{%- endmatch %}
{% for cons in obj.alternate_constructors() -%}
def self.{{ cons.name()|fn_name_rb }}({% call rb::arg_list_decl(cons) %})
- {%- call rb::coerce_args_extra_indent(cons) %}
+ {%- call rb::setup_args_extra_indent(cons) %}
# Call the (fallible) function before creating any half-baked object instances.
# Lightly yucky way to bypass the usual "initialize" logic
# and just create a new instance with the required pointer.
- return _uniffi_allocate({% call rb::to_ffi_call(cons) %})
+ return uniffi_allocate({% call rb::to_ffi_call(cons) %})
end
{% endfor %}
@@ -58,15 +68,15 @@ class {{ obj.name()|class_name_rb }}
{%- when Some with (return_type) -%}
def {{ meth.name()|fn_name_rb }}({% call rb::arg_list_decl(meth) %})
- {%- call rb::coerce_args_extra_indent(meth) %}
- result = {% call rb::to_ffi_call_with_prefix("@pointer", meth) %}
+ {%- call rb::setup_args_extra_indent(meth) %}
+ result = {% call rb::to_ffi_call_with_prefix("uniffi_clone_pointer()", meth) %}
return {{ "result"|lift_rb(return_type) }}
end
{%- when None -%}
def {{ meth.name()|fn_name_rb }}({% call rb::arg_list_decl(meth) %})
- {%- call rb::coerce_args_extra_indent(meth) %}
- {% call rb::to_ffi_call_with_prefix("@pointer", meth) %}
+ {%- call rb::setup_args_extra_indent(meth) %}
+ {% call rb::to_ffi_call_with_prefix("uniffi_clone_pointer()", meth) %}
end
{% endmatch %}
{% endfor %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb
index c940b31060..b5a201b248 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb
@@ -2,7 +2,12 @@
class {{ rec.name()|class_name_rb }}
attr_reader {% for field in rec.fields() %}:{{ field.name()|var_name_rb }}{% if loop.last %}{% else %}, {% endif %}{%- endfor %}
- def initialize({% for field in rec.fields() %}{{ field.name()|var_name_rb }}{% if loop.last %}{% else %}, {% endif %}{% endfor %})
+ def initialize({% for field in rec.fields() %}{{ field.name()|var_name_rb -}}:
+ {%- match field.default_value() %}
+ {%- when Some with(literal) %} {{ literal|literal_rb }}
+ {%- else %}
+ {%- endmatch %}
+ {%- if loop.last %}{% else %}, {% endif -%}{% endfor %})
{%- for field in rec.fields() %}
@{{ field.name()|var_name_rb }} = {{ field.name()|var_name_rb }}
{%- endfor %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferBuilder.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferBuilder.rb
index 8749139116..d15c0bbe76 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferBuilder.rb
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferBuilder.rb
@@ -163,7 +163,7 @@ class RustBufferBuilder
# The Object type {{ object_name }}.
def write_{{ canonical_type_name }}(obj)
- pointer = {{ object_name|class_name_rb}}._uniffi_lower obj
+ pointer = {{ object_name|class_name_rb}}.uniffi_lower obj
pack_into(8, 'Q>', pointer.address)
end
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferStream.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferStream.rb
index b085dddf15..f9b0806abc 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferStream.rb
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferStream.rb
@@ -155,7 +155,7 @@ class RustBufferStream
def read{{ canonical_type_name }}
pointer = FFI::Pointer.new unpack_from 8, 'Q>'
- return {{ object_name|class_name_rb }}._uniffi_allocate(pointer)
+ return {{ object_name|class_name_rb }}.uniffi_allocate(pointer)
end
{% when Type::Enum { name, module_path } -%}
@@ -237,7 +237,7 @@ class RustBufferStream
def read{{ canonical_type_name }}
{{ rec.name()|class_name_rb }}.new(
{%- for field in rec.fields() %}
- read{{ canonical_name(field.as_type().borrow()).borrow()|class_name_rb }}{% if loop.last %}{% else %},{% endif %}
+ {{ field.name()|var_name_rb }}: read{{ canonical_name(field.as_type().borrow()).borrow()|class_name_rb }}{% if loop.last %}{% else %},{% endif %}
{%- endfor %}
)
end
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferTemplate.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferTemplate.rb
index 0194c9666d..452d9831cd 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferTemplate.rb
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferTemplate.rb
@@ -1,6 +1,6 @@
class RustBuffer < FFI::Struct
- layout :capacity, :int32,
- :len, :int32,
+ layout :capacity, :uint64,
+ :len, :uint64,
:data, :pointer
def self.alloc(size)
@@ -128,6 +128,12 @@ class RustBuffer < FFI::Struct
{%- let rec = ci|get_record_definition(record_name) -%}
# The Record type {{ record_name }}.
+ def self.check_lower_{{ canonical_type_name }}(v)
+ {%- for field in rec.fields() %}
+ {{ "v.{}"|format(field.name()|var_name_rb)|check_lower_rb(field.as_type().borrow()) }}
+ {%- endfor %}
+ end
+
def self.alloc_from_{{ canonical_type_name }}(v)
RustBuffer.allocWithBuilder do |builder|
builder.write_{{ canonical_type_name }}(v)
@@ -146,6 +152,19 @@ class RustBuffer < FFI::Struct
{%- let e = ci|get_enum_definition(enum_name) -%}
# The Enum type {{ enum_name }}.
+ def self.check_lower_{{ canonical_type_name }}(v)
+ {%- if !e.is_flat() %}
+ {%- for variant in e.variants() %}
+ if v.{{ variant.name()|var_name_rb }}?
+ {%- for field in variant.fields() %}
+ {{ "v.{}"|format(field.name())|check_lower_rb(field.as_type().borrow()) }}
+ {%- endfor %}
+ return
+ end
+ {%- endfor %}
+ {%- endif %}
+ end
+
def self.alloc_from_{{ canonical_type_name }}(v)
RustBuffer.allocWithBuilder do |builder|
builder.write_{{ canonical_type_name }}(v)
@@ -163,6 +182,12 @@ class RustBuffer < FFI::Struct
{% when Type::Optional { inner_type } -%}
# The Optional<T> type for {{ canonical_name(inner_type) }}.
+ def self.check_lower_{{ canonical_type_name }}(v)
+ if not v.nil?
+ {{ "v"|check_lower_rb(inner_type.borrow()) }}
+ end
+ end
+
def self.alloc_from_{{ canonical_type_name }}(v)
RustBuffer.allocWithBuilder do |builder|
builder.write_{{ canonical_type_name }}(v)
@@ -179,6 +204,12 @@ class RustBuffer < FFI::Struct
{% when Type::Sequence { inner_type } -%}
# The Sequence<T> type for {{ canonical_name(inner_type) }}.
+ def self.check_lower_{{ canonical_type_name }}(v)
+ v.each do |item|
+ {{ "item"|check_lower_rb(inner_type.borrow()) }}
+ end
+ end
+
def self.alloc_from_{{ canonical_type_name }}(v)
RustBuffer.allocWithBuilder do |builder|
builder.write_{{ canonical_type_name }}(v)
@@ -195,6 +226,13 @@ class RustBuffer < FFI::Struct
{% when Type::Map { key_type: k, value_type: inner_type } -%}
# The Map<T> type for {{ canonical_name(inner_type) }}.
+ def self.check_lower_{{ canonical_type_name }}(v)
+ v.each do |k, v|
+ {{ "k"|check_lower_rb(k.borrow()) }}
+ {{ "v"|check_lower_rb(inner_type.borrow()) }}
+ end
+ end
+
def self.alloc_from_{{ canonical_type_name }}(v)
RustBuffer.allocWithBuilder do |builder|
builder.write_{{ canonical_type_name }}(v)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb
index 13214cf31b..b6dce0effa 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb
@@ -2,7 +2,7 @@
{%- when Some with (return_type) %}
def self.{{ func.name()|fn_name_rb }}({%- call rb::arg_list_decl(func) -%})
- {%- call rb::coerce_args(func) %}
+ {%- call rb::setup_args(func) %}
result = {% call rb::to_ffi_call(func) %}
return {{ "result"|lift_rb(return_type) }}
end
@@ -10,7 +10,7 @@ end
{% when None %}
def self.{{ func.name()|fn_name_rb }}({%- call rb::arg_list_decl(func) -%})
- {%- call rb::coerce_args(func) %}
+ {%- call rb::setup_args(func) %}
{% call rb::to_ffi_call(func) %}
end
{% endmatch %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/macros.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/macros.rb
index 8dc3e5e613..59fa4ef4cc 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/macros.rb
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/macros.rb
@@ -60,14 +60,16 @@
[{%- for arg in func.arguments() -%}{{ arg.type_().borrow()|type_ffi }}, {% endfor -%} RustCallStatus.by_ref]
{%- endmacro -%}
-{%- macro coerce_args(func) %}
+{%- macro setup_args(func) %}
{%- for arg in func.arguments() %}
- {{ arg.name() }} = {{ arg.name()|coerce_rb(ci.namespace()|class_name_rb, arg.as_type().borrow()) -}}
+ {{ arg.name() }} = {{ arg.name()|coerce_rb(ci.namespace()|class_name_rb, arg.as_type().borrow()) }}
+ {{ arg.name()|check_lower_rb(arg.as_type().borrow()) }}
{% endfor -%}
{%- endmacro -%}
-{%- macro coerce_args_extra_indent(func) %}
- {%- for arg in func.arguments() %}
+{%- macro setup_args_extra_indent(meth) %}
+ {%- for arg in meth.arguments() %}
{{ arg.name() }} = {{ arg.name()|coerce_rb(ci.namespace()|class_name_rb, arg.as_type().borrow()) }}
+ {{ arg.name()|check_lower_rb(arg.as_type().borrow()) }}
{%- endfor %}
{%- endmacro -%}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/test.rs b/third_party/rust/uniffi_bindgen/src/bindings/ruby/test.rs
index 03da37d567..88f770b9dc 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/ruby/test.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/test.rs
@@ -4,6 +4,7 @@ License, v. 2.0. If a copy of the MPL was not distributed with this
use crate::bindings::TargetLanguage;
use crate::library_mode::generate_bindings;
+use crate::BindingGeneratorDefault;
use anyhow::{bail, Context, Result};
use camino::Utf8Path;
use std::env;
@@ -30,11 +31,21 @@ pub fn test_script_command(
fixture_name: &str,
script_file: &str,
) -> Result<Command> {
- let script_path = Utf8Path::new(".").join(script_file).canonicalize_utf8()?;
+ let script_path = Utf8Path::new(script_file).canonicalize_utf8()?;
let test_helper = UniFFITestHelper::new(fixture_name)?;
let out_dir = test_helper.create_out_dir(tmp_dir, &script_path)?;
let cdylib_path = test_helper.copy_cdylib_to_out_dir(&out_dir)?;
- generate_bindings(&cdylib_path, None, &[TargetLanguage::Ruby], &out_dir, false)?;
+ generate_bindings(
+ &cdylib_path,
+ None,
+ &BindingGeneratorDefault {
+ target_languages: vec![TargetLanguage::Ruby],
+ try_format_code: false,
+ },
+ None,
+ &out_dir,
+ false,
+ )?;
let rubypath = env::var_os("RUBYLIB").unwrap_or_else(|| OsString::from(""));
let rubypath = env::join_paths(
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/callback_interface.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/callback_interface.rs
index 5d8b37e0af..dab89e0259 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/callback_interface.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/callback_interface.rs
@@ -6,21 +6,25 @@ use super::CodeType;
#[derive(Debug)]
pub struct CallbackInterfaceCodeType {
- id: String,
+ name: String,
}
impl CallbackInterfaceCodeType {
- pub fn new(id: String) -> Self {
- Self { id }
+ pub fn new(name: String) -> Self {
+ Self { name }
}
}
impl CodeType for CallbackInterfaceCodeType {
fn type_label(&self) -> String {
- super::SwiftCodeOracle.class_name(&self.id)
+ super::SwiftCodeOracle.class_name(&self.name)
}
fn canonical_name(&self) -> String {
format!("CallbackInterface{}", self.type_label())
}
+
+ fn initialization_fn(&self) -> Option<String> {
+ Some(format!("uniffiCallbackInit{}", self.name))
+ }
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/compounds.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/compounds.rs
index 8e6dddf3f9..d89fdfd386 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/compounds.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/compounds.rs
@@ -30,8 +30,9 @@ impl CodeType for OptionalCodeType {
fn literal(&self, literal: &Literal) -> String {
match literal {
- Literal::Null => "nil".into(),
- _ => super::SwiftCodeOracle.find(&self.inner).literal(literal),
+ Literal::None => "nil".into(),
+ Literal::Some { inner } => super::SwiftCodeOracle.find(&self.inner).literal(inner),
+ _ => panic!("Invalid literal for Optional type: {literal:?}"),
}
}
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/executor.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/executor.rs
deleted file mode 100644
index b488b004cf..0000000000
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/executor.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use super::CodeType;
-
-#[derive(Debug)]
-pub struct ForeignExecutorCodeType;
-
-impl CodeType for ForeignExecutorCodeType {
- fn type_label(&self) -> String {
- // On Swift, we define a struct to represent a ForeignExecutor
- "UniFfiForeignExecutor".into()
- }
-
- fn canonical_name(&self) -> String {
- "ForeignExecutor".into()
- }
-
- fn initialization_fn(&self) -> Option<String> {
- Some("uniffiInitForeignExecutor".into())
- }
-}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/external.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/external.rs
index 0b6728ba84..3960b7aae1 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/external.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/external.rs
@@ -17,7 +17,7 @@ impl ExternalCodeType {
impl CodeType for ExternalCodeType {
fn type_label(&self) -> String {
- self.name.clone()
+ super::SwiftCodeOracle.class_name(&self.name)
}
fn canonical_name(&self) -> String {
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs
index 12db4afc66..16c1625123 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs
@@ -10,25 +10,49 @@ use std::fmt::Debug;
use anyhow::{Context, Result};
use askama::Template;
-use heck::{ToLowerCamelCase, ToUpperCamelCase};
+use camino::Utf8Path;
+use heck::{ToLowerCamelCase, ToShoutySnakeCase, ToUpperCamelCase};
use serde::{Deserialize, Serialize};
use super::Bindings;
use crate::backend::TemplateExpression;
+use crate::bindings::swift;
use crate::interface::*;
-use crate::BindingsConfig;
+use crate::{BindingGenerator, BindingsConfig};
mod callback_interface;
mod compounds;
mod custom;
mod enum_;
-mod executor;
mod external;
mod miscellany;
mod object;
mod primitives;
mod record;
+pub struct SwiftBindingGenerator;
+impl BindingGenerator for SwiftBindingGenerator {
+ type Config = Config;
+
+ fn write_bindings(
+ &self,
+ ci: &ComponentInterface,
+ config: &Config,
+ out_dir: &Utf8Path,
+ try_format_code: bool,
+ ) -> Result<()> {
+ swift::write_bindings(config, ci, out_dir, try_format_code)
+ }
+
+ fn check_library_path(
+ &self,
+ _library_path: &Utf8Path,
+ _cdylib_name: Option<&str>,
+ ) -> Result<()> {
+ Ok(())
+ }
+}
+
/// A trait tor the implementation.
trait CodeType: Debug {
/// The language specific label used to reference this type. This will be used in
@@ -196,6 +220,8 @@ pub struct Config {
ffi_module_filename: Option<String>,
generate_module_map: Option<bool>,
omit_argument_labels: Option<bool>,
+ generate_immutable_records: Option<bool>,
+ experimental_sendable_value_types: Option<bool>,
#[serde(default)]
custom_types: HashMap<String, CustomTypeConfig>,
}
@@ -261,6 +287,16 @@ impl Config {
pub fn omit_argument_labels(&self) -> bool {
self.omit_argument_labels.unwrap_or(false)
}
+
+ /// Whether to generate immutable records (`let` instead of `var`)
+ pub fn generate_immutable_records(&self) -> bool {
+ self.generate_immutable_records.unwrap_or(false)
+ }
+
+ /// Whether to mark value types as 'Sendable'
+ pub fn experimental_sendable_value_types(&self) -> bool {
+ self.experimental_sendable_value_types.unwrap_or(false)
+ }
}
impl BindingsConfig for Config {
@@ -400,7 +436,6 @@ pub struct SwiftWrapper<'a> {
ci: &'a ComponentInterface,
type_helper_code: String,
type_imports: BTreeSet<String>,
- has_async_fns: bool,
}
impl<'a> SwiftWrapper<'a> {
pub fn new(config: Config, ci: &'a ComponentInterface) -> Self {
@@ -412,7 +447,6 @@ impl<'a> SwiftWrapper<'a> {
ci,
type_helper_code,
type_imports,
- has_async_fns: ci.has_async_fns(),
}
}
@@ -425,10 +459,6 @@ impl<'a> SwiftWrapper<'a> {
.iter_types()
.map(|t| SwiftCodeOracle.find(t))
.filter_map(|ct| ct.initialization_fn())
- .chain(
- self.has_async_fns
- .then(|| "uniffiInitContinuationCallback".into()),
- )
.collect()
}
}
@@ -464,12 +494,11 @@ impl SwiftCodeOracle {
Type::Duration => Box::new(miscellany::DurationCodeType),
Type::Enum { name, .. } => Box::new(enum_::EnumCodeType::new(name)),
- Type::Object { name, .. } => Box::new(object::ObjectCodeType::new(name)),
+ Type::Object { name, imp, .. } => Box::new(object::ObjectCodeType::new(name, imp)),
Type::Record { name, .. } => Box::new(record::RecordCodeType::new(name)),
Type::CallbackInterface { name, .. } => {
Box::new(callback_interface::CallbackInterfaceCodeType::new(name))
}
- Type::ForeignExecutor => Box::new(executor::ForeignExecutorCodeType),
Type::Optional { inner_type } => {
Box::new(compounds::OptionalCodeType::new(*inner_type))
}
@@ -509,7 +538,22 @@ impl SwiftCodeOracle {
nm.to_string().to_lower_camel_case()
}
- fn ffi_type_label_raw(&self, ffi_type: &FfiType) -> String {
+ /// Get the idiomatic Swift rendering of an FFI callback function name
+ fn ffi_callback_name(&self, nm: &str) -> String {
+ format!("Uniffi{}", nm.to_upper_camel_case())
+ }
+
+ /// Get the idiomatic Swift rendering of an FFI struct name
+ fn ffi_struct_name(&self, nm: &str) -> String {
+ format!("Uniffi{}", nm.to_upper_camel_case())
+ }
+
+ /// Get the idiomatic Swift rendering of an if guard name
+ fn if_guard_name(&self, nm: &str) -> String {
+ format!("UNIFFI_FFIDEF_{}", nm.to_shouty_snake_case())
+ }
+
+ fn ffi_type_label(&self, ffi_type: &FfiType) -> String {
match ffi_type {
FfiType::Int8 => "Int8".into(),
FfiType::UInt8 => "UInt8".into(),
@@ -521,40 +565,74 @@ impl SwiftCodeOracle {
FfiType::UInt64 => "UInt64".into(),
FfiType::Float32 => "Float".into(),
FfiType::Float64 => "Double".into(),
+ FfiType::Handle => "UInt64".into(),
FfiType::RustArcPtr(_) => "UnsafeMutableRawPointer".into(),
FfiType::RustBuffer(_) => "RustBuffer".into(),
+ FfiType::RustCallStatus => "RustCallStatus".into(),
FfiType::ForeignBytes => "ForeignBytes".into(),
- FfiType::ForeignCallback => "ForeignCallback".into(),
- FfiType::ForeignExecutorHandle => "Int".into(),
- FfiType::ForeignExecutorCallback => "ForeignExecutorCallback".into(),
- FfiType::RustFutureContinuationCallback => "UniFfiRustFutureContinuation".into(),
- FfiType::RustFutureHandle | FfiType::RustFutureContinuationData => {
- "UnsafeMutableRawPointer".into()
+ // Note: @escaping is required for Swift versions before 5.7 for callbacks passed into
+ // async functions. Swift 5.7 and later does not require it. We should probably remove
+ // it once we upgrade our minimum requirement to 5.7 or later.
+ FfiType::Callback(name) => format!("@escaping {}", self.ffi_callback_name(name)),
+ FfiType::Struct(name) => self.ffi_struct_name(name),
+ FfiType::Reference(inner) => {
+ format!("UnsafeMutablePointer<{}>", self.ffi_type_label(inner))
}
+ FfiType::VoidPointer => "UnsafeMutableRawPointer".into(),
}
}
- fn ffi_type_label(&self, ffi_type: &FfiType) -> String {
- match ffi_type {
- FfiType::ForeignCallback
- | FfiType::ForeignExecutorCallback
- | FfiType::RustFutureHandle
- | FfiType::RustFutureContinuationCallback
- | FfiType::RustFutureContinuationData => {
- format!("{} _Nonnull", self.ffi_type_label_raw(ffi_type))
- }
- _ => self.ffi_type_label_raw(ffi_type),
+ /// Default values for FFI types
+ ///
+ /// Used to set a default return value when returning an error
+ fn ffi_default_value(&self, return_type: Option<&FfiType>) -> String {
+ match return_type {
+ Some(t) => match t {
+ FfiType::UInt8
+ | FfiType::Int8
+ | FfiType::UInt16
+ | FfiType::Int16
+ | FfiType::UInt32
+ | FfiType::Int32
+ | FfiType::UInt64
+ | FfiType::Int64 => "0".to_owned(),
+ FfiType::Float32 | FfiType::Float64 => "0.0".to_owned(),
+ FfiType::RustArcPtr(_) => "nil".to_owned(),
+ FfiType::RustBuffer(_) => "RustBuffer.empty()".to_owned(),
+ _ => unimplemented!("FFI return type: {t:?}"),
+ },
+ // When we need to use a value for void returns, we use a `u8` placeholder
+ None => "0".to_owned(),
}
}
fn ffi_canonical_name(&self, ffi_type: &FfiType) -> String {
- self.ffi_type_label_raw(ffi_type)
+ self.ffi_type_label(ffi_type)
+ }
+
+ /// Get the name of the protocol and class name for an object.
+ ///
+ /// If we support callback interfaces, the protocol name is the object name, and the class name is derived from that.
+ /// Otherwise, the class name is the object name and the protocol name is derived from that.
+ ///
+ /// This split determines what types `FfiConverter.lower()` inputs. If we support callback
+ /// interfaces, `lower` must lower anything that implements the protocol. If not, then lower
+ /// only lowers the concrete class.
+ fn object_names(&self, obj: &Object) -> (String, String) {
+ let class_name = self.class_name(obj.name());
+ if obj.has_callback_interface() {
+ let impl_name = format!("{class_name}Impl");
+ (class_name, impl_name)
+ } else {
+ (format!("{class_name}Protocol"), class_name)
+ }
}
}
pub mod filters {
use super::*;
pub use crate::backend::filters::*;
+ use uniffi_meta::LiteralMetadata;
fn oracle() -> &'static SwiftCodeOracle {
&SwiftCodeOracle
@@ -564,6 +642,13 @@ pub mod filters {
Ok(oracle().find(&as_type.as_type()).type_label())
}
+ pub fn return_type_name(as_type: Option<&impl AsType>) -> Result<String, askama::Error> {
+ Ok(match as_type {
+ Some(as_type) => oracle().find(&as_type.as_type()).type_label(),
+ None => "()".to_owned(),
+ })
+ }
+
pub fn canonical_name(as_type: &impl AsType) -> Result<String, askama::Error> {
Ok(oracle().find(&as_type.as_type()).canonical_name())
}
@@ -572,6 +657,15 @@ pub mod filters {
Ok(oracle().find(&as_type.as_type()).ffi_converter_name())
}
+ pub fn ffi_error_converter_name(as_type: &impl AsType) -> Result<String, askama::Error> {
+ // special handling for types used as errors.
+ let mut name = oracle().find(&as_type.as_type()).ffi_converter_name();
+ if matches!(&as_type.as_type(), Type::Object { .. }) {
+ name.push_str("__as_error")
+ }
+ Ok(name)
+ }
+
pub fn lower_fn(as_type: &impl AsType) -> Result<String, askama::Error> {
Ok(oracle().find(&as_type.as_type()).lower())
}
@@ -595,6 +689,16 @@ pub mod filters {
Ok(oracle().find(&as_type.as_type()).literal(literal))
}
+ // Get the idiomatic Swift rendering of an individual enum variant's discriminant
+ pub fn variant_discr_literal(e: &Enum, index: &usize) -> Result<String, askama::Error> {
+ let literal = e.variant_discr(*index).expect("invalid index");
+ match literal {
+ LiteralMetadata::UInt(v, _, _) => Ok(v.to_string()),
+ LiteralMetadata::Int(v, _, _) => Ok(v.to_string()),
+ _ => unreachable!("expected an UInt!"),
+ }
+ }
+
/// Get the Swift type for an FFIType
pub fn ffi_type_name(ffi_type: &FfiType) -> Result<String, askama::Error> {
Ok(oracle().ffi_type_label(ffi_type))
@@ -604,6 +708,10 @@ pub mod filters {
Ok(oracle().ffi_canonical_name(ffi_type))
}
+ pub fn ffi_default_value(return_type: Option<FfiType>) -> Result<String, askama::Error> {
+ Ok(oracle().ffi_default_value(return_type.as_ref()))
+ }
+
/// Like `ffi_type_name`, but used in `BridgingHeaderTemplate.h` which uses a slightly different
/// names.
pub fn header_ffi_type_name(ffi_type: &FfiType) -> Result<String, askama::Error> {
@@ -618,18 +726,17 @@ pub mod filters {
FfiType::UInt64 => "uint64_t".into(),
FfiType::Float32 => "float".into(),
FfiType::Float64 => "double".into(),
+ FfiType::Handle => "uint64_t".into(),
FfiType::RustArcPtr(_) => "void*_Nonnull".into(),
FfiType::RustBuffer(_) => "RustBuffer".into(),
+ FfiType::RustCallStatus => "RustCallStatus".into(),
FfiType::ForeignBytes => "ForeignBytes".into(),
- FfiType::ForeignCallback => "ForeignCallback _Nonnull".into(),
- FfiType::ForeignExecutorCallback => "UniFfiForeignExecutorCallback _Nonnull".into(),
- FfiType::ForeignExecutorHandle => "size_t".into(),
- FfiType::RustFutureContinuationCallback => {
- "UniFfiRustFutureContinuation _Nonnull".into()
- }
- FfiType::RustFutureHandle | FfiType::RustFutureContinuationData => {
- "void* _Nonnull".into()
+ FfiType::Callback(name) => {
+ format!("{} _Nonnull", SwiftCodeOracle.ffi_callback_name(name))
}
+ FfiType::Struct(name) => SwiftCodeOracle.ffi_struct_name(name),
+ FfiType::Reference(inner) => format!("{}* _Nonnull", header_ffi_type_name(inner)?),
+ FfiType::VoidPointer => "void* _Nonnull".into(),
})
}
@@ -664,6 +771,30 @@ pub mod filters {
Ok(oracle().enum_variant_name(nm))
}
+ /// Get the idiomatic Swift rendering of an FFI callback function name
+ pub fn ffi_callback_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(oracle().ffi_callback_name(nm))
+ }
+
+ /// Get the idiomatic Swift rendering of an FFI struct name
+ pub fn ffi_struct_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(oracle().ffi_struct_name(nm))
+ }
+
+ /// Get the idiomatic Swift rendering of an if guard name
+ pub fn if_guard_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(oracle().if_guard_name(nm))
+ }
+
+ /// Get the idiomatic Swift rendering of docstring
+ pub fn docstring(docstring: &str, spaces: &i32) -> Result<String, askama::Error> {
+ let middle = textwrap::indent(&textwrap::dedent(docstring), " * ");
+ let wrapped = format!("/**\n{middle}\n */");
+
+ let spaces = usize::try_from(*spaces).unwrap_or_default();
+ Ok(textwrap::indent(&wrapped, &" ".repeat(spaces)))
+ }
+
pub fn error_handler(result: &ResultType) -> Result<String, askama::Error> {
Ok(match &result.throws_type {
Some(t) => format!("{}.lift", ffi_converter_name(t)?),
@@ -685,4 +816,8 @@ pub mod filters {
}
))
}
+
+ pub fn object_names(obj: &Object) -> Result<(String, String), askama::Error> {
+ Ok(SwiftCodeOracle.object_names(obj))
+ }
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/object.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/object.rs
index ea140c998d..d4497a7b19 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/object.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/object.rs
@@ -3,24 +3,32 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use super::CodeType;
+use crate::interface::ObjectImpl;
#[derive(Debug)]
pub struct ObjectCodeType {
- id: String,
+ name: String,
+ imp: ObjectImpl,
}
impl ObjectCodeType {
- pub fn new(id: String) -> Self {
- Self { id }
+ pub fn new(name: String, imp: ObjectImpl) -> Self {
+ Self { name, imp }
}
}
impl CodeType for ObjectCodeType {
fn type_label(&self) -> String {
- super::SwiftCodeOracle.class_name(&self.id)
+ super::SwiftCodeOracle.class_name(&self.name)
}
fn canonical_name(&self) -> String {
- format!("Type{}", self.id)
+ format!("Type{}", self.name)
+ }
+
+ fn initialization_fn(&self) -> Option<String> {
+ self.imp
+ .has_callback_interface()
+ .then(|| format!("uniffiCallbackInit{}", self.name))
}
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/primitives.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/primitives.rs
index 86424658a3..e0c670520e 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/primitives.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/primitives.rs
@@ -9,7 +9,11 @@ use paste::paste;
fn render_literal(literal: &Literal) -> String {
fn typed_number(type_: &Type, num_str: String) -> String {
- match type_ {
+ let unwrapped_type = match type_ {
+ Type::Optional { inner_type } => inner_type,
+ t => t,
+ };
+ match unwrapped_type {
// special case Int32.
Type::Int32 => num_str,
// otherwise use constructor e.g. UInt8(x)
@@ -29,7 +33,7 @@ fn render_literal(literal: &Literal) -> String {
super::SwiftCodeOracle.find(type_).type_label()
)
}
- _ => panic!("Unexpected literal: {num_str} is not a number"),
+ _ => panic!("Unexpected literal: {num_str} for type: {type_:?}"),
}
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Async.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Async.swift
index 695208861d..e16f3108e1 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Async.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Async.swift
@@ -1,11 +1,13 @@
private let UNIFFI_RUST_FUTURE_POLL_READY: Int8 = 0
private let UNIFFI_RUST_FUTURE_POLL_MAYBE_READY: Int8 = 1
+fileprivate let uniffiContinuationHandleMap = UniffiHandleMap<UnsafeContinuation<Int8, Never>>()
+
fileprivate func uniffiRustCallAsync<F, T>(
- rustFutureFunc: () -> UnsafeMutableRawPointer,
- pollFunc: (UnsafeMutableRawPointer, UnsafeMutableRawPointer) -> (),
- completeFunc: (UnsafeMutableRawPointer, UnsafeMutablePointer<RustCallStatus>) -> F,
- freeFunc: (UnsafeMutableRawPointer) -> (),
+ rustFutureFunc: () -> UInt64,
+ pollFunc: (UInt64, @escaping UniffiRustFutureContinuationCallback, UInt64) -> (),
+ completeFunc: (UInt64, UnsafeMutablePointer<RustCallStatus>) -> F,
+ freeFunc: (UInt64) -> (),
liftFunc: (F) throws -> T,
errorHandler: ((RustBuffer) throws -> Error)?
) async throws -> T {
@@ -19,7 +21,11 @@ fileprivate func uniffiRustCallAsync<F, T>(
var pollResult: Int8;
repeat {
pollResult = await withUnsafeContinuation {
- pollFunc(rustFuture, ContinuationHolder($0).toOpaque())
+ pollFunc(
+ rustFuture,
+ uniffiFutureContinuationCallback,
+ uniffiContinuationHandleMap.insert(obj: $0)
+ )
}
} while pollResult != UNIFFI_RUST_FUTURE_POLL_READY
@@ -31,32 +37,80 @@ fileprivate func uniffiRustCallAsync<F, T>(
// Callback handlers for an async calls. These are invoked by Rust when the future is ready. They
// lift the return value or error and resume the suspended function.
-fileprivate func uniffiFutureContinuationCallback(ptr: UnsafeMutableRawPointer, pollResult: Int8) {
- ContinuationHolder.fromOpaque(ptr).resume(pollResult)
+fileprivate func uniffiFutureContinuationCallback(handle: UInt64, pollResult: Int8) {
+ if let continuation = try? uniffiContinuationHandleMap.remove(handle: handle) {
+ continuation.resume(returning: pollResult)
+ } else {
+ print("uniffiFutureContinuationCallback invalid handle")
+ }
}
-// Wraps UnsafeContinuation in a class so that we can use reference counting when passing it across
-// the FFI
-fileprivate class ContinuationHolder {
- let continuation: UnsafeContinuation<Int8, Never>
-
- init(_ continuation: UnsafeContinuation<Int8, Never>) {
- self.continuation = continuation
+{%- if ci.has_async_callback_interface_definition() %}
+private func uniffiTraitInterfaceCallAsync<T>(
+ makeCall: @escaping () async throws -> T,
+ handleSuccess: @escaping (T) -> (),
+ handleError: @escaping (Int8, RustBuffer) -> ()
+) -> UniffiForeignFuture {
+ let task = Task {
+ do {
+ handleSuccess(try await makeCall())
+ } catch {
+ handleError(CALL_UNEXPECTED_ERROR, {{ Type::String.borrow()|lower_fn }}(String(describing: error)))
+ }
}
+ let handle = UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.insert(obj: task)
+ return UniffiForeignFuture(handle: handle, free: uniffiForeignFutureFree)
- func resume(_ pollResult: Int8) {
- self.continuation.resume(returning: pollResult)
- }
+}
- func toOpaque() -> UnsafeMutableRawPointer {
- return Unmanaged<ContinuationHolder>.passRetained(self).toOpaque()
+private func uniffiTraitInterfaceCallAsyncWithError<T, E>(
+ makeCall: @escaping () async throws -> T,
+ handleSuccess: @escaping (T) -> (),
+ handleError: @escaping (Int8, RustBuffer) -> (),
+ lowerError: @escaping (E) -> RustBuffer
+) -> UniffiForeignFuture {
+ let task = Task {
+ do {
+ handleSuccess(try await makeCall())
+ } catch let error as E {
+ handleError(CALL_ERROR, lowerError(error))
+ } catch {
+ handleError(CALL_UNEXPECTED_ERROR, {{ Type::String.borrow()|lower_fn }}(String(describing: error)))
+ }
}
+ let handle = UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.insert(obj: task)
+ return UniffiForeignFuture(handle: handle, free: uniffiForeignFutureFree)
+}
+
+// Borrow the callback handle map implementation to store foreign future handles
+// TODO: consolidate the handle-map code (https://github.com/mozilla/uniffi-rs/pull/1823)
+fileprivate var UNIFFI_FOREIGN_FUTURE_HANDLE_MAP = UniffiHandleMap<UniffiForeignFutureTask>()
+
+// Protocol for tasks that handle foreign futures.
+//
+// Defining a protocol allows all tasks to be stored in the same handle map. This can't be done
+// with the task object itself, since has generic parameters.
+protocol UniffiForeignFutureTask {
+ func cancel()
+}
+
+extension Task: UniffiForeignFutureTask {}
- static func fromOpaque(_ ptr: UnsafeRawPointer) -> ContinuationHolder {
- return Unmanaged<ContinuationHolder>.fromOpaque(ptr).takeRetainedValue()
+private func uniffiForeignFutureFree(handle: UInt64) {
+ do {
+ let task = try UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.remove(handle: handle)
+ // Set the cancellation flag on the task. If it's still running, the code can check the
+ // cancellation flag or call `Task.checkCancellation()`. If the task has completed, this is
+ // a no-op.
+ task.cancel()
+ } catch {
+ print("uniffiForeignFutureFree: handle missing from handlemap")
}
}
-fileprivate func uniffiInitContinuationCallback() {
- {{ ci.ffi_rust_future_continuation_callback_set().name() }}(uniffiFutureContinuationCallback)
+// For testing
+public func uniffiForeignFutureHandleCount{{ ci.namespace()|class_name }}() -> Int {
+ UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.count
}
+
+{%- endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BridgingHeaderTemplate.h b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BridgingHeaderTemplate.h
index 87698e359f..89d98594d3 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BridgingHeaderTemplate.h
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BridgingHeaderTemplate.h
@@ -24,25 +24,11 @@
typedef struct RustBuffer
{
- int32_t capacity;
- int32_t len;
+ uint64_t capacity;
+ uint64_t len;
uint8_t *_Nullable data;
} RustBuffer;
-typedef int32_t (*ForeignCallback)(uint64_t, int32_t, const uint8_t *_Nonnull, int32_t, RustBuffer *_Nonnull);
-
-// Task defined in Rust that Swift executes
-typedef void (*UniFfiRustTaskCallback)(const void * _Nullable, int8_t);
-
-// Callback to execute Rust tasks using a Swift Task
-//
-// Args:
-// executor: ForeignExecutor lowered into a size_t value
-// delay: Delay in MS
-// task: UniFfiRustTaskCallback to call
-// task_data: data to pass the task callback
-typedef int8_t (*UniFfiForeignExecutorCallback)(size_t, uint32_t, UniFfiRustTaskCallback _Nullable, const void * _Nullable);
-
typedef struct ForeignBytes
{
int32_t len;
@@ -59,11 +45,29 @@ typedef struct RustCallStatus {
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️
#endif // def UNIFFI_SHARED_H
-// Continuation callback for UniFFI Futures
-typedef void (*UniFfiRustFutureContinuation)(void * _Nonnull, int8_t);
-
-// Scaffolding functions
-{%- for func in ci.iter_ffi_function_definitions() %}
+{%- for def in ci.ffi_definitions() %}
+#ifndef {{ def.name()|if_guard_name }}
+#define {{ def.name()|if_guard_name }}
+{%- match def %}
+{% when FfiDefinition::CallbackFunction(callback) %}
+typedef
+ {%- match callback.return_type() %}{% when Some(return_type) %} {{ return_type|header_ffi_type_name }} {% when None %} void {% endmatch -%}
+ (*{{ callback.name()|ffi_callback_name }})(
+ {%- for arg in callback.arguments() -%}
+ {{ arg.type_().borrow()|header_ffi_type_name }}
+ {%- if !loop.last || callback.has_rust_call_status_arg() %}, {% endif %}
+ {%- endfor -%}
+ {%- if callback.has_rust_call_status_arg() %}
+ RustCallStatus *_Nonnull uniffiCallStatus
+ {%- endif %}
+ );
+{% when FfiDefinition::Struct(struct) %}
+typedef struct {{ struct.name()|ffi_struct_name }} {
+ {%- for field in struct.fields() %}
+ {{ field.type_().borrow()|header_ffi_type_name }} {{ field.name()|var_name }};
+ {%- endfor %}
+} {{ struct.name()|ffi_struct_name }};
+{% when FfiDefinition::Function(func) %}
{% match func.return_type() -%}{%- when Some with (type_) %}{{ type_|header_ffi_type_name }}{% when None %}void{% endmatch %} {{ func.name() }}(
{%- if func.arguments().len() > 0 %}
{%- for arg in func.arguments() %}
@@ -74,6 +78,8 @@ typedef void (*UniFfiRustFutureContinuation)(void * _Nonnull, int8_t);
{%- if func.has_rust_call_status_arg() %}RustCallStatus *_Nonnull out_status{%- else %}void{% endif %}
{% endif %}
);
+{%- endmatch %}
+#endif
{%- endfor %}
{% import "macros.swift" as swift %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceImpl.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceImpl.swift
new file mode 100644
index 0000000000..74ee372642
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceImpl.swift
@@ -0,0 +1,113 @@
+{%- if self.include_once_check("CallbackInterfaceRuntime.swift") %}{%- include "CallbackInterfaceRuntime.swift" %}{%- endif %}
+{%- let trait_impl=format!("UniffiCallbackInterface{}", name) %}
+
+// Put the implementation in a struct so we don't pollute the top-level namespace
+fileprivate struct {{ trait_impl }} {
+
+ // Create the VTable using a series of closures.
+ // Swift automatically converts these into C callback functions.
+ static var vtable: {{ vtable|ffi_type_name }} = {{ vtable|ffi_type_name }}(
+ {%- for (ffi_callback, meth) in vtable_methods %}
+ {{ meth.name()|fn_name }}: { (
+ {%- for arg in ffi_callback.arguments() %}
+ {{ arg.name()|var_name }}: {{ arg.type_().borrow()|ffi_type_name }}{% if !loop.last || ffi_callback.has_rust_call_status_arg() %},{% endif %}
+ {%- endfor -%}
+ {%- if ffi_callback.has_rust_call_status_arg() %}
+ uniffiCallStatus: UnsafeMutablePointer<RustCallStatus>
+ {%- endif %}
+ ) in
+ let makeCall = {
+ () {% if meth.is_async() %}async {% endif %}throws -> {% match meth.return_type() %}{% when Some(t) %}{{ t|type_name }}{% when None %}(){% endmatch %} in
+ guard let uniffiObj = try? {{ ffi_converter_name }}.handleMap.get(handle: uniffiHandle) else {
+ throw UniffiInternalError.unexpectedStaleHandle
+ }
+ return {% if meth.throws() %}try {% endif %}{% if meth.is_async() %}await {% endif %}uniffiObj.{{ meth.name()|fn_name }}(
+ {%- for arg in meth.arguments() %}
+ {% if !config.omit_argument_labels() %} {{ arg.name()|arg_name }}: {% endif %}try {{ arg|lift_fn }}({{ arg.name()|var_name }}){% if !loop.last %},{% endif %}
+ {%- endfor %}
+ )
+ }
+ {%- if !meth.is_async() %}
+
+ {% match meth.return_type() %}
+ {%- when Some(t) %}
+ let writeReturn = { uniffiOutReturn.pointee = {{ t|lower_fn }}($0) }
+ {%- when None %}
+ let writeReturn = { () }
+ {%- endmatch %}
+
+ {%- match meth.throws_type() %}
+ {%- when None %}
+ uniffiTraitInterfaceCall(
+ callStatus: uniffiCallStatus,
+ makeCall: makeCall,
+ writeReturn: writeReturn
+ )
+ {%- when Some(error_type) %}
+ uniffiTraitInterfaceCallWithError(
+ callStatus: uniffiCallStatus,
+ makeCall: makeCall,
+ writeReturn: writeReturn,
+ lowerError: {{ error_type|lower_fn }}
+ )
+ {%- endmatch %}
+ {%- else %}
+
+ let uniffiHandleSuccess = { (returnValue: {{ meth.return_type()|return_type_name }}) in
+ uniffiFutureCallback(
+ uniffiCallbackData,
+ {{ meth.foreign_future_ffi_result_struct().name()|ffi_struct_name }}(
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ returnValue: {{ return_type|lower_fn }}(returnValue),
+ {%- when None %}
+ {%- endmatch %}
+ callStatus: RustCallStatus()
+ )
+ )
+ }
+ let uniffiHandleError = { (statusCode, errorBuf) in
+ uniffiFutureCallback(
+ uniffiCallbackData,
+ {{ meth.foreign_future_ffi_result_struct().name()|ffi_struct_name }}(
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ returnValue: {{ meth.return_type().map(FfiType::from)|ffi_default_value }},
+ {%- when None %}
+ {%- endmatch %}
+ callStatus: RustCallStatus(code: statusCode, errorBuf: errorBuf)
+ )
+ )
+ }
+
+ {%- match meth.throws_type() %}
+ {%- when None %}
+ let uniffiForeignFuture = uniffiTraitInterfaceCallAsync(
+ makeCall: makeCall,
+ handleSuccess: uniffiHandleSuccess,
+ handleError: uniffiHandleError
+ )
+ {%- when Some(error_type) %}
+ let uniffiForeignFuture = uniffiTraitInterfaceCallAsyncWithError(
+ makeCall: makeCall,
+ handleSuccess: uniffiHandleSuccess,
+ handleError: uniffiHandleError,
+ lowerError: {{ error_type|lower_fn }}
+ )
+ {%- endmatch %}
+ uniffiOutReturn.pointee = uniffiForeignFuture
+ {%- endif %}
+ },
+ {%- endfor %}
+ uniffiFree: { (uniffiHandle: UInt64) -> () in
+ let result = try? {{ ffi_converter_name }}.handleMap.remove(handle: uniffiHandle)
+ if result == nil {
+ print("Uniffi callback interface {{ name }}: handle missing in uniffiFree")
+ }
+ }
+ )
+}
+
+private func {{ callback_init }}() {
+ {{ ffi_init_callback.name() }}(&{{ trait_impl }}.vtable)
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceRuntime.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceRuntime.swift
index 9ae62d1667..5863c2ad41 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceRuntime.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceRuntime.swift
@@ -1,60 +1,3 @@
-fileprivate extension NSLock {
- func withLock<T>(f: () throws -> T) rethrows -> T {
- self.lock()
- defer { self.unlock() }
- return try f()
- }
-}
-
-fileprivate typealias UniFFICallbackHandle = UInt64
-fileprivate class UniFFICallbackHandleMap<T> {
- private var leftMap: [UniFFICallbackHandle: T] = [:]
- private var counter: [UniFFICallbackHandle: UInt64] = [:]
- private var rightMap: [ObjectIdentifier: UniFFICallbackHandle] = [:]
-
- private let lock = NSLock()
- private var currentHandle: UniFFICallbackHandle = 0
- private let stride: UniFFICallbackHandle = 1
-
- func insert(obj: T) -> UniFFICallbackHandle {
- lock.withLock {
- let id = ObjectIdentifier(obj as AnyObject)
- let handle = rightMap[id] ?? {
- currentHandle += stride
- let handle = currentHandle
- leftMap[handle] = obj
- rightMap[id] = handle
- return handle
- }()
- counter[handle] = (counter[handle] ?? 0) + 1
- return handle
- }
- }
-
- func get(handle: UniFFICallbackHandle) -> T? {
- lock.withLock {
- leftMap[handle]
- }
- }
-
- func delete(handle: UniFFICallbackHandle) {
- remove(handle: handle)
- }
-
- @discardableResult
- func remove(handle: UniFFICallbackHandle) -> T? {
- lock.withLock {
- defer { counter[handle] = (counter[handle] ?? 1) - 1 }
- guard counter[handle] == 1 else { return leftMap[handle] }
- let obj = leftMap.removeValue(forKey: handle)
- if let obj = obj {
- rightMap.removeValue(forKey: ObjectIdentifier(obj as AnyObject))
- }
- return obj
- }
- }
-}
-
// Magic number for the Rust proxy to call using the same mechanism as every other method,
// to free the callback once it's dropped by Rust.
private let IDX_CALLBACK_FREE: Int32 = 0
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift
index aec8ded930..7aa1cca9b2 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift
@@ -1,150 +1,39 @@
{%- let cbi = ci|get_callback_interface_definition(name) %}
-{%- let foreign_callback = format!("foreignCallback{}", canonical_type_name) %}
-{%- if self.include_once_check("CallbackInterfaceRuntime.swift") %}{%- include "CallbackInterfaceRuntime.swift" %}{%- endif %}
-
-// Declaration and FfiConverters for {{ type_name }} Callback Interface
-
-public protocol {{ type_name }} : AnyObject {
- {% for meth in cbi.methods() -%}
- func {{ meth.name()|fn_name }}({% call swift::arg_list_protocol(meth) %}) {% call swift::throws(meth) -%}
- {%- match meth.return_type() -%}
- {%- when Some with (return_type) %} -> {{ return_type|type_name -}}
- {%- else -%}
- {%- endmatch %}
- {% endfor %}
-}
-
-// The ForeignCallback that is passed to Rust.
-fileprivate let {{ foreign_callback }} : ForeignCallback =
- { (handle: UniFFICallbackHandle, method: Int32, argsData: UnsafePointer<UInt8>, argsLen: Int32, out_buf: UnsafeMutablePointer<RustBuffer>) -> Int32 in
- {% for meth in cbi.methods() -%}
- {%- let method_name = format!("invoke_{}", meth.name())|fn_name %}
-
- func {{ method_name }}(_ swiftCallbackInterface: {{ type_name }}, _ argsData: UnsafePointer<UInt8>, _ argsLen: Int32, _ out_buf: UnsafeMutablePointer<RustBuffer>) throws -> Int32 {
- {%- if meth.arguments().len() > 0 %}
- var reader = createReader(data: Data(bytes: argsData, count: Int(argsLen)))
- {%- endif %}
-
- {%- match meth.return_type() %}
- {%- when Some(return_type) %}
- func makeCall() throws -> Int32 {
- let result = {% if meth.throws() %} try{% endif %} swiftCallbackInterface.{{ meth.name()|fn_name }}(
- {% for arg in meth.arguments() -%}
- {% if !config.omit_argument_labels() %}{{ arg.name()|var_name }}: {% endif %} try {{ arg|read_fn }}(from: &reader)
- {%- if !loop.last %}, {% endif %}
- {% endfor -%}
- )
- var writer = [UInt8]()
- {{ return_type|write_fn }}(result, into: &writer)
- out_buf.pointee = RustBuffer(bytes: writer)
- return UNIFFI_CALLBACK_SUCCESS
- }
- {%- when None %}
- func makeCall() throws -> Int32 {
- try swiftCallbackInterface.{{ meth.name()|fn_name }}(
- {% for arg in meth.arguments() -%}
- {% if !config.omit_argument_labels() %}{{ arg.name()|var_name }}: {% endif %} try {{ arg|read_fn }}(from: &reader)
- {%- if !loop.last %}, {% endif %}
- {% endfor -%}
- )
- return UNIFFI_CALLBACK_SUCCESS
- }
- {%- endmatch %}
-
- {%- match meth.throws_type() %}
- {%- when None %}
- return try makeCall()
- {%- when Some(error_type) %}
- do {
- return try makeCall()
- } catch let error as {{ error_type|type_name }} {
- out_buf.pointee = {{ error_type|lower_fn }}(error)
- return UNIFFI_CALLBACK_ERROR
- }
- {%- endmatch %}
- }
- {%- endfor %}
-
-
- switch method {
- case IDX_CALLBACK_FREE:
- {{ ffi_converter_name }}.drop(handle: handle)
- // Sucessful return
- // See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
- return UNIFFI_CALLBACK_SUCCESS
- {% for meth in cbi.methods() -%}
- {% let method_name = format!("invoke_{}", meth.name())|fn_name -%}
- case {{ loop.index }}:
- let cb: {{ cbi|type_name }}
- do {
- cb = try {{ ffi_converter_name }}.lift(handle)
- } catch {
- out_buf.pointee = {{ Type::String.borrow()|lower_fn }}("{{ cbi.name() }}: Invalid handle")
- return UNIFFI_CALLBACK_UNEXPECTED_ERROR
- }
- do {
- return try {{ method_name }}(cb, argsData, argsLen, out_buf)
- } catch let error {
- out_buf.pointee = {{ Type::String.borrow()|lower_fn }}(String(describing: error))
- return UNIFFI_CALLBACK_UNEXPECTED_ERROR
- }
- {% endfor %}
- // This should never happen, because an out of bounds method index won't
- // ever be used. Once we can catch errors, we should return an InternalError.
- // https://github.com/mozilla/uniffi-rs/issues/351
- default:
- // An unexpected error happened.
- // See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
- return UNIFFI_CALLBACK_UNEXPECTED_ERROR
- }
-}
+{%- let callback_handler = format!("uniffiCallbackHandler{}", name) %}
+{%- let callback_init = format!("uniffiCallbackInit{}", name) %}
+{%- let methods = cbi.methods() %}
+{%- let protocol_name = type_name.clone() %}
+{%- let protocol_docstring = cbi.docstring() %}
+{%- let vtable = cbi.vtable() %}
+{%- let vtable_methods = cbi.vtable_methods() %}
+{%- let ffi_init_callback = cbi.ffi_init_callback() %}
+
+{% include "Protocol.swift" %}
+{% include "CallbackInterfaceImpl.swift" %}
// FfiConverter protocol for callback interfaces
fileprivate struct {{ ffi_converter_name }} {
- private static let initCallbackOnce: () = {
- // Swift ensures this initializer code will once run once, even when accessed by multiple threads.
- try! rustCall { (err: UnsafeMutablePointer<RustCallStatus>) in
- {{ cbi.ffi_init_callback().name() }}({{ foreign_callback }}, err)
- }
- }()
-
- private static func ensureCallbackinitialized() {
- _ = initCallbackOnce
- }
-
- static func drop(handle: UniFFICallbackHandle) {
- handleMap.remove(handle: handle)
- }
-
- private static var handleMap = UniFFICallbackHandleMap<{{ type_name }}>()
+ fileprivate static var handleMap = UniffiHandleMap<{{ type_name }}>()
}
extension {{ ffi_converter_name }} : FfiConverter {
typealias SwiftType = {{ type_name }}
- // We can use Handle as the FfiType because it's a typealias to UInt64
- typealias FfiType = UniFFICallbackHandle
+ typealias FfiType = UInt64
- public static func lift(_ handle: UniFFICallbackHandle) throws -> SwiftType {
- ensureCallbackinitialized();
- guard let callback = handleMap.get(handle: handle) else {
- throw UniffiInternalError.unexpectedStaleHandle
- }
- return callback
+ public static func lift(_ handle: UInt64) throws -> SwiftType {
+ try handleMap.get(handle: handle)
}
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
- ensureCallbackinitialized();
- let handle: UniFFICallbackHandle = try readInt(&buf)
+ let handle: UInt64 = try readInt(&buf)
return try lift(handle)
}
- public static func lower(_ v: SwiftType) -> UniFFICallbackHandle {
- ensureCallbackinitialized();
+ public static func lower(_ v: SwiftType) -> UInt64 {
return handleMap.insert(obj: v)
}
public static func write(_ v: SwiftType, into buf: inout [UInt8]) {
- ensureCallbackinitialized();
writeInt(&buf, lower(v))
}
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift
index 99f45290cc..1d8b3cf500 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift
@@ -1,10 +1,26 @@
// Note that we don't yet support `indirect` for enums.
// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
+{%- call swift::docstring(e, 0) %}
+{% match e.variant_discr_type() %}
+{% when None %}
public enum {{ type_name }} {
{% for variant in e.variants() %}
- case {{ variant.name()|enum_variant_swift_quoted }}{% if variant.fields().len() > 0 %}({% call swift::field_list_decl(variant) %}){% endif -%}
+ {%- call swift::docstring(variant, 4) %}
+ case {{ variant.name()|enum_variant_swift_quoted }}{% if variant.fields().len() > 0 %}(
+ {%- call swift::field_list_decl(variant, variant.has_nameless_fields()) %}
+ ){% endif -%}
{% endfor %}
}
+{% when Some with (variant_discr_type) %}
+public enum {{ type_name }} : {{ variant_discr_type|type_name }} {
+ {% for variant in e.variants() %}
+ {%- call swift::docstring(variant, 4) %}
+ case {{ variant.name()|enum_variant_swift_quoted }} = {{ e|variant_discr_literal(loop.index0) }}{% if variant.fields().len() > 0 %}(
+ {%- call swift::field_list_decl(variant, variant.has_nameless_fields()) %}
+ ){% endif -%}
+ {% endfor %}
+}
+{% endmatch %}
public struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
typealias SwiftType = {{ type_name }}
@@ -15,7 +31,11 @@ public struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
{% for variant in e.variants() %}
case {{ loop.index }}: return .{{ variant.name()|enum_variant_swift_quoted }}{% if variant.has_fields() %}(
{%- for field in variant.fields() %}
+ {%- if variant.has_nameless_fields() -%}
+ try {{ field|read_fn }}(from: &buf)
+ {%- else -%}
{{ field.name()|arg_name }}: try {{ field|read_fn }}(from: &buf)
+ {%- endif -%}
{%- if !loop.last %}, {% endif %}
{%- endfor %}
){%- endif %}
@@ -28,10 +48,10 @@ public struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
switch value {
{% for variant in e.variants() %}
{% if variant.has_fields() %}
- case let .{{ variant.name()|enum_variant_swift_quoted }}({% for field in variant.fields() %}{{ field.name()|var_name }}{%- if loop.last -%}{%- else -%},{%- endif -%}{% endfor %}):
+ case let .{{ variant.name()|enum_variant_swift_quoted }}({% for field in variant.fields() %}{%- call swift::field_name(field, loop.index) -%}{%- if loop.last -%}{%- else -%},{%- endif -%}{% endfor %}):
writeInt(&buf, Int32({{ loop.index }}))
{% for field in variant.fields() -%}
- {{ field|write_fn }}({{ field.name()|var_name }}, into: &buf)
+ {{ field|write_fn }}({% call swift::field_name(field, loop.index) %}, into: &buf)
{% endfor -%}
{% else %}
case .{{ variant.name()|enum_variant_swift_quoted }}:
@@ -55,5 +75,6 @@ public func {{ ffi_converter_name }}_lower(_ value: {{ type_name }}) -> RustBuff
}
{% if !contains_object_references %}
+{% if config.experimental_sendable_value_types() %}extension {{ type_name }}: Sendable {} {% endif %}
extension {{ type_name }}: Equatable, Hashable {}
{% endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift
index 786091395b..0702c477e9 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift
@@ -1,21 +1,21 @@
+{%- call swift::docstring(e, 0) %}
public enum {{ type_name }} {
{% if e.is_flat() %}
{% for variant in e.variants() %}
- // Simple error enums only carry a message
+ {%- call swift::docstring(variant, 4) %}
case {{ variant.name()|class_name }}(message: String)
{% endfor %}
{%- else %}
{% for variant in e.variants() %}
- case {{ variant.name()|class_name }}{% if variant.fields().len() > 0 %}({% call swift::field_list_decl(variant) %}){% endif -%}
+ {%- call swift::docstring(variant, 4) %}
+ case {{ variant.name()|class_name }}{% if variant.fields().len() > 0 %}(
+ {%- call swift::field_list_decl(variant, variant.has_nameless_fields()) %}
+ ){% endif -%}
{% endfor %}
{%- endif %}
-
- fileprivate static func uniffiErrorHandler(_ error: RustBuffer) throws -> Error {
- return try {{ ffi_converter_name }}.lift(error)
- }
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ForeignExecutorTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ForeignExecutorTemplate.swift
deleted file mode 100644
index 167e4c7546..0000000000
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ForeignExecutorTemplate.swift
+++ /dev/null
@@ -1,69 +0,0 @@
-private let UNIFFI_RUST_TASK_CALLBACK_SUCCESS: Int8 = 0
-private let UNIFFI_RUST_TASK_CALLBACK_CANCELLED: Int8 = 1
-private let UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS: Int8 = 0
-private let UNIFFI_FOREIGN_EXECUTOR_CALLBACK_CANCELED: Int8 = 1
-private let UNIFFI_FOREIGN_EXECUTOR_CALLBACK_ERROR: Int8 = 2
-
-// Encapsulates an executor that can run Rust tasks
-//
-// On Swift, `Task.detached` can handle this we just need to know what priority to send it.
-public struct UniFfiForeignExecutor {
- var priority: TaskPriority
-
- public init(priority: TaskPriority) {
- self.priority = priority
- }
-
- public init() {
- self.priority = Task.currentPriority
- }
-}
-
-fileprivate struct FfiConverterForeignExecutor: FfiConverter {
- typealias SwiftType = UniFfiForeignExecutor
- // Rust uses a pointer to represent the FfiConverterForeignExecutor, but we only need a u8.
- // let's use `Int`, which is equivalent to `size_t`
- typealias FfiType = Int
-
- public static func lift(_ value: FfiType) throws -> SwiftType {
- UniFfiForeignExecutor(priority: TaskPriority(rawValue: numericCast(value)))
- }
- public static func lower(_ value: SwiftType) -> FfiType {
- numericCast(value.priority.rawValue)
- }
-
- public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
- fatalError("FfiConverterForeignExecutor.read not implemented yet")
- }
- public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
- fatalError("FfiConverterForeignExecutor.read not implemented yet")
- }
-}
-
-
-fileprivate func uniffiForeignExecutorCallback(executorHandle: Int, delayMs: UInt32, rustTask: UniFfiRustTaskCallback?, taskData: UnsafeRawPointer?) -> Int8 {
- if let rustTask = rustTask {
- let executor = try! FfiConverterForeignExecutor.lift(executorHandle)
- Task.detached(priority: executor.priority) {
- if delayMs != 0 {
- let nanoseconds: UInt64 = numericCast(delayMs * 1000000)
- try! await Task.sleep(nanoseconds: nanoseconds)
- }
- rustTask(taskData, UNIFFI_RUST_TASK_CALLBACK_SUCCESS)
- }
- return UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS
- } else {
- // When rustTask is null, we should drop the foreign executor.
- // However, since its just a value type, we don't need to do anything here.
- return UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS
- }
-}
-
-fileprivate func uniffiInitForeignExecutor() {
- {%- match ci.ffi_foreign_executor_callback_set() %}
- {%- when Some with (fn) %}
- {{ fn.name() }}(uniffiForeignExecutorCallback)
- {%- when None %}
- {#- No foreign executor, we don't set anything #}
- {% endmatch %}
-}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/HandleMap.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/HandleMap.swift
new file mode 100644
index 0000000000..6de9f085d6
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/HandleMap.swift
@@ -0,0 +1,40 @@
+fileprivate class UniffiHandleMap<T> {
+ private var map: [UInt64: T] = [:]
+ private let lock = NSLock()
+ private var currentHandle: UInt64 = 1
+
+ func insert(obj: T) -> UInt64 {
+ lock.withLock {
+ let handle = currentHandle
+ currentHandle += 1
+ map[handle] = obj
+ return handle
+ }
+ }
+
+ func get(handle: UInt64) throws -> T {
+ try lock.withLock {
+ guard let obj = map[handle] else {
+ throw UniffiInternalError.unexpectedStaleHandle
+ }
+ return obj
+ }
+ }
+
+ @discardableResult
+ func remove(handle: UInt64) throws -> T {
+ try lock.withLock {
+ guard let obj = map.removeValue(forKey: handle) else {
+ throw UniffiInternalError.unexpectedStaleHandle
+ }
+ return obj
+ }
+ }
+
+ var count: Int {
+ get {
+ map.count
+ }
+ }
+}
+
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift
index a34b128e23..cfddf7b313 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift
@@ -26,9 +26,17 @@ fileprivate enum UniffiInternalError: LocalizedError {
}
}
+fileprivate extension NSLock {
+ func withLock<T>(f: () throws -> T) rethrows -> T {
+ self.lock()
+ defer { self.unlock() }
+ return try f()
+ }
+}
+
fileprivate let CALL_SUCCESS: Int8 = 0
fileprivate let CALL_ERROR: Int8 = 1
-fileprivate let CALL_PANIC: Int8 = 2
+fileprivate let CALL_UNEXPECTED_ERROR: Int8 = 2
fileprivate let CALL_CANCELLED: Int8 = 3
fileprivate extension RustCallStatus {
@@ -81,7 +89,7 @@ private func uniffiCheckCallStatus(
throw UniffiInternalError.unexpectedRustCallError
}
- case CALL_PANIC:
+ case CALL_UNEXPECTED_ERROR:
// When the rust code sees a panic, it tries to construct a RustBuffer
// with the message. But if that code panics, then it just sends back
// an empty buffer.
@@ -93,9 +101,39 @@ private func uniffiCheckCallStatus(
}
case CALL_CANCELLED:
- throw CancellationError()
+ fatalError("Cancellation not supported yet")
default:
throw UniffiInternalError.unexpectedRustCallStatusCode
}
}
+
+private func uniffiTraitInterfaceCall<T>(
+ callStatus: UnsafeMutablePointer<RustCallStatus>,
+ makeCall: () throws -> T,
+ writeReturn: (T) -> ()
+) {
+ do {
+ try writeReturn(makeCall())
+ } catch let error {
+ callStatus.pointee.code = CALL_UNEXPECTED_ERROR
+ callStatus.pointee.errorBuf = {{ Type::String.borrow()|lower_fn }}(String(describing: error))
+ }
+}
+
+private func uniffiTraitInterfaceCallWithError<T, E>(
+ callStatus: UnsafeMutablePointer<RustCallStatus>,
+ makeCall: () throws -> T,
+ writeReturn: (T) -> (),
+ lowerError: (E) -> RustBuffer
+) {
+ do {
+ try writeReturn(makeCall())
+ } catch let error as E {
+ callStatus.pointee.code = CALL_ERROR
+ callStatus.pointee.errorBuf = lowerError(error)
+ } catch {
+ callStatus.pointee.code = CALL_UNEXPECTED_ERROR
+ callStatus.pointee.errorBuf = {{ Type::String.borrow()|lower_fn }}(String(describing: error))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift
index 57a77ca6df..0c28bc4c09 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift
@@ -1,104 +1,146 @@
{%- let obj = ci|get_object_definition(name) %}
-public protocol {{ obj.name() }}Protocol {
- {% for meth in obj.methods() -%}
- func {{ meth.name()|fn_name }}({% call swift::arg_list_protocol(meth) %}) {% call swift::async(meth) %} {% call swift::throws(meth) -%}
- {%- match meth.return_type() -%}
- {%- when Some with (return_type) %} -> {{ return_type|type_name -}}
- {%- else -%}
- {%- endmatch %}
- {% endfor %}
-}
-
-public class {{ type_name }}: {{ obj.name() }}Protocol {
- fileprivate let pointer: UnsafeMutableRawPointer
+{%- let (protocol_name, impl_class_name) = obj|object_names %}
+{%- let methods = obj.methods() %}
+{%- let protocol_docstring = obj.docstring() %}
+
+{%- let is_error = ci.is_name_used_as_error(name) %}
+
+{% include "Protocol.swift" %}
+
+{%- call swift::docstring(obj, 0) %}
+open class {{ impl_class_name }}:
+ {%- for tm in obj.uniffi_traits() %}
+ {%- match tm %}
+ {%- when UniffiTrait::Display { fmt } %}
+ CustomStringConvertible,
+ {%- when UniffiTrait::Debug { fmt } %}
+ CustomDebugStringConvertible,
+ {%- when UniffiTrait::Eq { eq, ne } %}
+ Equatable,
+ {%- when UniffiTrait::Hash { hash } %}
+ Hashable,
+ {%- else %}
+ {%- endmatch %}
+ {%- endfor %}
+ {%- if is_error %}
+ Error,
+ {% endif %}
+ {{ protocol_name }} {
+ fileprivate let pointer: UnsafeMutableRawPointer!
+
+ /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly.
+ public struct NoPointer {
+ public init() {}
+ }
// TODO: We'd like this to be `private` but for Swifty reasons,
// we can't implement `FfiConverter` without making this `required` and we can't
// make it `required` without making it `public`.
- required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) {
+ required public init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) {
self.pointer = pointer
}
+ /// This constructor can be used to instantiate a fake object.
+ /// - Parameter noPointer: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject].
+ ///
+ /// - Warning:
+ /// Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing [Pointer] the FFI lower functions will crash.
+ public init(noPointer: NoPointer) {
+ self.pointer = nil
+ }
+
+ public func uniffiClonePointer() -> UnsafeMutableRawPointer {
+ return try! rustCall { {{ obj.ffi_object_clone().name() }}(self.pointer, $0) }
+ }
+
{%- match obj.primary_constructor() %}
{%- when Some with (cons) %}
- public convenience init({% call swift::arg_list_decl(cons) -%}) {% call swift::throws(cons) %} {
- self.init(unsafeFromRawPointer: {% call swift::to_ffi_call(cons) %})
- }
+ {%- call swift::ctor_decl(cons, 4) %}
{%- when None %}
+ // No primary constructor declared for this class.
{%- endmatch %}
deinit {
+ guard let pointer = pointer else {
+ return
+ }
+
try! rustCall { {{ obj.ffi_object_free().name() }}(pointer, $0) }
}
{% for cons in obj.alternate_constructors() %}
-
- public static func {{ cons.name()|fn_name }}({% call swift::arg_list_decl(cons) %}) {% call swift::throws(cons) %} -> {{ type_name }} {
- return {{ type_name }}(unsafeFromRawPointer: {% call swift::to_ffi_call(cons) %})
- }
-
+ {%- call swift::func_decl("public static func", cons, 4) %}
{% endfor %}
- {# // TODO: Maybe merge the two templates (i.e the one with a return type and the one without) #}
{% for meth in obj.methods() -%}
- {%- if meth.is_async() %}
-
- public func {{ meth.name()|fn_name }}({%- call swift::arg_list_decl(meth) -%}) async {% call swift::throws(meth) %}{% match meth.return_type() %}{% when Some with (return_type) %} -> {{ return_type|type_name }}{% when None %}{% endmatch %} {
- return {% call swift::try(meth) %} await uniffiRustCallAsync(
- rustFutureFunc: {
- {{ meth.ffi_func().name() }}(
- self.pointer
- {%- for arg in meth.arguments() -%}
- ,
- {{ arg|lower_fn }}({{ arg.name()|var_name }})
- {%- endfor %}
- )
- },
- pollFunc: {{ meth.ffi_rust_future_poll(ci) }},
- completeFunc: {{ meth.ffi_rust_future_complete(ci) }},
- freeFunc: {{ meth.ffi_rust_future_free(ci) }},
- {%- match meth.return_type() %}
- {%- when Some(return_type) %}
- liftFunc: {{ return_type|lift_fn }},
- {%- when None %}
- liftFunc: { $0 },
- {%- endmatch %}
- {%- match meth.throws_type() %}
- {%- when Some with (e) %}
- errorHandler: {{ e|ffi_converter_name }}.lift
- {%- else %}
- errorHandler: nil
- {% endmatch %}
+ {%- call swift::func_decl("open func", meth, 4) %}
+ {% endfor %}
+
+ {%- for tm in obj.uniffi_traits() %}
+ {%- match tm %}
+ {%- when UniffiTrait::Display { fmt } %}
+ open var description: String {
+ return {% call swift::try(fmt) %} {{ fmt.return_type().unwrap()|lift_fn }}(
+ {% call swift::to_ffi_call(fmt) %}
)
}
-
- {% else -%}
-
- {%- match meth.return_type() -%}
-
- {%- when Some with (return_type) %}
-
- public func {{ meth.name()|fn_name }}({% call swift::arg_list_decl(meth) %}) {% call swift::throws(meth) %} -> {{ return_type|type_name }} {
- return {% call swift::try(meth) %} {{ return_type|lift_fn }}(
- {% call swift::to_ffi_call_with_prefix("self.pointer", meth) %}
+ {%- when UniffiTrait::Debug { fmt } %}
+ open var debugDescription: String {
+ return {% call swift::try(fmt) %} {{ fmt.return_type().unwrap()|lift_fn }}(
+ {% call swift::to_ffi_call(fmt) %}
)
}
-
- {%- when None %}
-
- public func {{ meth.name()|fn_name }}({% call swift::arg_list_decl(meth) %}) {% call swift::throws(meth) %} {
- {% call swift::to_ffi_call_with_prefix("self.pointer", meth) %}
+ {%- when UniffiTrait::Eq { eq, ne } %}
+ public static func == (self: {{ impl_class_name }}, other: {{ impl_class_name }}) -> Bool {
+ return {% call swift::try(eq) %} {{ eq.return_type().unwrap()|lift_fn }}(
+ {% call swift::to_ffi_call(eq) %}
+ )
+ }
+ {%- when UniffiTrait::Hash { hash } %}
+ open func hash(into hasher: inout Hasher) {
+ let val = {% call swift::try(hash) %} {{ hash.return_type().unwrap()|lift_fn }}(
+ {% call swift::to_ffi_call(hash) %}
+ )
+ hasher.combine(val)
}
+ {%- else %}
+ {%- endmatch %}
+ {%- endfor %}
- {%- endmatch -%}
- {%- endif -%}
- {% endfor %}
}
+{%- if obj.has_callback_interface() %}
+{%- let callback_handler = format!("uniffiCallbackInterface{}", name) %}
+{%- let callback_init = format!("uniffiCallbackInit{}", name) %}
+{%- let vtable = obj.vtable().expect("trait interface should have a vtable") %}
+{%- let vtable_methods = obj.vtable_methods() %}
+{%- let ffi_init_callback = obj.ffi_init_callback() %}
+{% include "CallbackInterfaceImpl.swift" %}
+{%- endif %}
+
public struct {{ ffi_converter_name }}: FfiConverter {
+ {%- if obj.has_callback_interface() %}
+ fileprivate static var handleMap = UniffiHandleMap<{{ type_name }}>()
+ {%- endif %}
+
typealias FfiType = UnsafeMutableRawPointer
typealias SwiftType = {{ type_name }}
+ public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> {{ type_name }} {
+ return {{ impl_class_name }}(unsafeFromRawPointer: pointer)
+ }
+
+ public static func lower(_ value: {{ type_name }}) -> UnsafeMutableRawPointer {
+ {%- if obj.has_callback_interface() %}
+ guard let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: handleMap.insert(obj: value))) else {
+ fatalError("Cast to UnsafeMutableRawPointer failed")
+ }
+ return ptr
+ {%- else %}
+ return value.uniffiClonePointer()
+ {%- endif %}
+ }
+
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ type_name }} {
let v: UInt64 = try readInt(&buf)
// The Rust code won't compile if a pointer won't fit in a UInt64.
@@ -115,15 +157,30 @@ public struct {{ ffi_converter_name }}: FfiConverter {
// The Rust code won't compile if a pointer won't fit in a `UInt64`.
writeInt(&buf, UInt64(bitPattern: Int64(Int(bitPattern: lower(value)))))
}
+}
- public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> {{ type_name }} {
- return {{ type_name}}(unsafeFromRawPointer: pointer)
+{# Objects as error #}
+{%- if is_error %}
+{# Due to some mismatches in the ffi converter mechanisms, errors are a RustBuffer holding a pointer #}
+public struct {{ ffi_converter_name }}__as_error: FfiConverterRustBuffer {
+ public static func lift(_ buf: RustBuffer) throws -> {{ type_name }} {
+ var reader = createReader(data: Data(rustBuffer: buf))
+ return try {{ ffi_converter_name }}.read(from: &reader)
}
- public static func lower(_ value: {{ type_name }}) -> UnsafeMutableRawPointer {
- return value.pointer
+ public static func lower(_ value: {{ type_name }}) -> RustBuffer {
+ fatalError("not implemented")
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ type_name }} {
+ fatalError("not implemented")
+ }
+
+ public static func write(_ value: {{ type_name }}, into buf: inout [UInt8]) {
+ fatalError("not implemented")
}
}
+{%- endif %}
{#
We always write these public functions just in case the enum is used as
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Protocol.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Protocol.swift
new file mode 100644
index 0000000000..7df953558a
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Protocol.swift
@@ -0,0 +1,12 @@
+{%- call swift::docstring_value(protocol_docstring, 0) %}
+public protocol {{ protocol_name }} : AnyObject {
+ {% for meth in methods.iter() -%}
+ {%- call swift::docstring(meth, 4) %}
+ func {{ meth.name()|fn_name }}({% call swift::arg_list_protocol(meth) %}) {% call swift::async(meth) -%}{% call swift::throws(meth) -%}
+ {%- match meth.return_type() -%}
+ {%- when Some with (return_type) %} -> {{ return_type|type_name -}}
+ {%- else -%}
+ {%- endmatch %}
+ {% endfor %}
+}
+
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift
index 44de9dd358..c262a7a216 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift
@@ -1,12 +1,14 @@
{%- let rec = ci|get_record_definition(name) %}
+{%- call swift::docstring(rec, 0) %}
public struct {{ type_name }} {
{%- for field in rec.fields() %}
- public var {{ field.name()|var_name }}: {{ field|type_name }}
+ {%- call swift::docstring(field, 4) %}
+ public {% if config.generate_immutable_records() %}let{% else %}var{% endif %} {{ field.name()|var_name }}: {{ field|type_name }}
{%- endfor %}
// Default memberwise initializers are never public by default, so we
// declare one manually.
- public init({% call swift::field_list_decl(rec) %}) {
+ public init({% call swift::field_list_decl(rec, false) %}) {
{%- for field in rec.fields() %}
self.{{ field.name()|var_name }} = {{ field.name()|var_name }}
{%- endfor %}
@@ -14,6 +16,7 @@ public struct {{ type_name }} {
}
{% if !contains_object_references %}
+{% if config.experimental_sendable_value_types() %}extension {{ type_name }}: Sendable {} {% endif %}
extension {{ type_name }}: Equatable, Hashable {
public static func ==(lhs: {{ type_name }}, rhs: {{ type_name }}) -> Bool {
{%- for field in rec.fields() %}
@@ -34,12 +37,16 @@ extension {{ type_name }}: Equatable, Hashable {
public struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ type_name }} {
- return try {{ type_name }}(
+ return {%- if rec.has_fields() %}
+ try {{ type_name }}(
{%- for field in rec.fields() %}
- {{ field.name()|arg_name }}: {{ field|read_fn }}(from: &buf)
- {%- if !loop.last %}, {% endif %}
+ {{ field.name()|arg_name }}: {{ field|read_fn }}(from: &buf)
+ {%- if !loop.last %}, {% endif %}
{%- endfor %}
)
+ {%- else %}
+ {{ type_name }}()
+ {%- endif %}
}
public static func write(_ value: {{ type_name }}, into buf: inout [UInt8]) {
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift
index 2f737b6635..a053334a30 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift
@@ -7,6 +7,10 @@ fileprivate extension RustBuffer {
self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data)
}
+ static func empty() -> RustBuffer {
+ RustBuffer(capacity: 0, len:0, data: nil)
+ }
+
static func from(_ ptr: UnsafeBufferPointer<UInt8>) -> RustBuffer {
try! rustCall { {{ ci.ffi_rustbuffer_from_bytes().name() }}(ForeignBytes(bufferPointer: ptr), $0) }
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift
index a2c6311931..ce946076f7 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift
@@ -1,48 +1 @@
-{%- if func.is_async() %}
-
-public func {{ func.name()|fn_name }}({%- call swift::arg_list_decl(func) -%}) async {% call swift::throws(func) %}{% match func.return_type() %}{% when Some with (return_type) %} -> {{ return_type|type_name }}{% when None %}{% endmatch %} {
- return {% call swift::try(func) %} await uniffiRustCallAsync(
- rustFutureFunc: {
- {{ func.ffi_func().name() }}(
- {%- for arg in func.arguments() %}
- {{ arg|lower_fn }}({{ arg.name()|var_name }}){% if !loop.last %},{% endif %}
- {%- endfor %}
- )
- },
- pollFunc: {{ func.ffi_rust_future_poll(ci) }},
- completeFunc: {{ func.ffi_rust_future_complete(ci) }},
- freeFunc: {{ func.ffi_rust_future_free(ci) }},
- {%- match func.return_type() %}
- {%- when Some(return_type) %}
- liftFunc: {{ return_type|lift_fn }},
- {%- when None %}
- liftFunc: { $0 },
- {%- endmatch %}
- {%- match func.throws_type() %}
- {%- when Some with (e) %}
- errorHandler: {{ e|ffi_converter_name }}.lift
- {%- else %}
- errorHandler: nil
- {% endmatch %}
- )
-}
-
-{% else %}
-
-{%- match func.return_type() -%}
-{%- when Some with (return_type) %}
-
-public func {{ func.name()|fn_name }}({%- call swift::arg_list_decl(func) -%}) {% call swift::throws(func) %} -> {{ return_type|type_name }} {
- return {% call swift::try(func) %} {{ return_type|lift_fn }}(
- {% call swift::to_ffi_call(func) %}
- )
-}
-
-{%- when None %}
-
-public func {{ func.name()|fn_name }}({% call swift::arg_list_decl(func) %}) {% call swift::throws(func) %} {
- {% call swift::to_ffi_call(func) %}
-}
-
-{% endmatch %}
-{%- endif %}
+{%- call swift::func_decl("public func", func, 0) %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Types.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Types.swift
index aba34f4b0b..5e26758f3c 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Types.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Types.swift
@@ -64,9 +64,6 @@
{%- when Type::CallbackInterface { name, module_path } %}
{%- include "CallbackInterfaceTemplate.swift" %}
-{%- when Type::ForeignExecutor %}
-{%- include "ForeignExecutorTemplate.swift" %}
-
{%- when Type::Custom { name, module_path, builtin } %}
{%- include "CustomType.swift" %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/macros.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/macros.swift
index 0a125e6f61..8692cd6ff0 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/macros.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/macros.swift
@@ -8,28 +8,97 @@
{%- call try(func) -%}
{%- match func.throws_type() -%}
{%- when Some with (e) -%}
- rustCallWithError({{ e|ffi_converter_name }}.lift) {
+ rustCallWithError({{ e|ffi_error_converter_name }}.lift) {
{%- else -%}
rustCall() {
{%- endmatch %}
- {{ func.ffi_func().name() }}({% call arg_list_lowered(func) -%} $0)
+ {{ func.ffi_func().name() }}(
+ {%- if func.takes_self() %}self.uniffiClonePointer(),{% endif %}
+ {%- call arg_list_lowered(func) -%} $0
+ )
}
{%- endmacro -%}
-{%- macro to_ffi_call_with_prefix(prefix, func) -%}
-{% call try(func) %}
- {%- match func.throws_type() %}
- {%- when Some with (e) %}
- rustCallWithError({{ e|ffi_converter_name }}.lift) {
+// eg, `public func foo_bar() { body }`
+{%- macro func_decl(func_decl, callable, indent) %}
+{%- call docstring(callable, indent) %}
+{{ func_decl }} {{ callable.name()|fn_name }}(
+ {%- call arg_list_decl(callable) -%})
+ {%- call async(callable) %}
+ {%- call throws(callable) %}
+ {%- match callable.return_type() %}
+ {%- when Some with (return_type) %} -> {{ return_type|type_name }}
+ {%- when None %}
+ {%- endmatch %} {
+ {%- call call_body(callable) %}
+}
+{%- endmacro %}
+
+// primary ctor - no name, no return-type.
+{%- macro ctor_decl(callable, indent) %}
+{%- call docstring(callable, indent) %}
+public convenience init(
+ {%- call arg_list_decl(callable) -%}) {%- call async(callable) %} {%- call throws(callable) %} {
+ {%- if callable.is_async() %}
+ let pointer =
+ {%- call call_async(callable) %}
+ {# The async mechanism returns an already constructed self.
+ We work around that by cloning the pointer from that object, then
+ assune the old object dies as there are no other references possible.
+ #}
+ .uniffiClonePointer()
{%- else %}
- rustCall() {
- {% endmatch %}
- {{ func.ffi_func().name() }}(
- {{- prefix }}, {% call arg_list_lowered(func) -%} $0
- )
+ let pointer =
+ {% call to_ffi_call(callable) %}
+ {%- endif %}
+ self.init(unsafeFromRawPointer: pointer)
}
{%- endmacro %}
+{%- macro call_body(callable) %}
+{%- if callable.is_async() %}
+ return {%- call call_async(callable) %}
+{%- else %}
+{%- match callable.return_type() -%}
+{%- when Some with (return_type) %}
+ return {% call try(callable) %} {{ return_type|lift_fn }}({% call to_ffi_call(callable) %})
+{%- when None %}
+{%- call to_ffi_call(callable) %}
+{%- endmatch %}
+{%- endif %}
+
+{%- endmacro %}
+
+{%- macro call_async(callable) %}
+ {% call try(callable) %} await uniffiRustCallAsync(
+ rustFutureFunc: {
+ {{ callable.ffi_func().name() }}(
+ {%- if callable.takes_self() %}
+ self.uniffiClonePointer(){% if !callable.arguments().is_empty() %},{% endif %}
+ {% endif %}
+ {%- for arg in callable.arguments() -%}
+ {{ arg|lower_fn }}({{ arg.name()|var_name }}){% if !loop.last %},{% endif %}
+ {%- endfor %}
+ )
+ },
+ pollFunc: {{ callable.ffi_rust_future_poll(ci) }},
+ completeFunc: {{ callable.ffi_rust_future_complete(ci) }},
+ freeFunc: {{ callable.ffi_rust_future_free(ci) }},
+ {%- match callable.return_type() %}
+ {%- when Some(return_type) %}
+ liftFunc: {{ return_type|lift_fn }},
+ {%- when None %}
+ liftFunc: { $0 },
+ {%- endmatch %}
+ {%- match callable.throws_type() %}
+ {%- when Some with (e) %}
+ errorHandler: {{ e|ffi_error_converter_name }}.lift
+ {%- else %}
+ errorHandler: nil
+ {% endmatch %}
+ )
+{%- endmacro %}
+
{%- macro arg_list_lowered(func) %}
{%- for arg in func.arguments() %}
{{ arg|lower_fn }}({{ arg.name()|var_name }}),
@@ -56,17 +125,30 @@
// Field lists as used in Swift declarations of Records and Enums.
// Note the var_name and type_name filters.
-#}
-{% macro field_list_decl(item) %}
+{% macro field_list_decl(item, has_nameless_fields) %}
{%- for field in item.fields() -%}
+ {%- call docstring(field, 8) %}
+ {%- if has_nameless_fields %}
+ {{- field|type_name -}}
+ {%- if !loop.last -%}, {%- endif -%}
+ {%- else -%}
{{ field.name()|var_name }}: {{ field|type_name -}}
{%- match field.default_value() %}
{%- when Some with(literal) %} = {{ literal|literal_swift(field) }}
{%- else %}
{%- endmatch -%}
{% if !loop.last %}, {% endif %}
+ {%- endif -%}
{%- endfor %}
{%- endmacro %}
+{% macro field_name(field, field_num) %}
+{%- if field.name().is_empty() -%}
+v{{- field_num -}}
+{%- else -%}
+{{ field.name()|var_name }}
+{%- endif -%}
+{%- endmacro %}
{% macro arg_list_protocol(func) %}
{%- for arg in func.arguments() -%}
@@ -75,15 +157,26 @@
{%- endfor %}
{%- endmacro %}
-
{%- macro async(func) %}
-{%- if func.is_async() %}async{% endif %}
+{%- if func.is_async() %}async {% endif %}
{%- endmacro -%}
{%- macro throws(func) %}
-{%- if func.throws() %}throws{% endif %}
+{%- if func.throws() %}throws {% endif %}
{%- endmacro -%}
{%- macro try(func) %}
{%- if func.throws() %}try {% else %}try! {% endif %}
{%- endmacro -%}
+
+{%- macro docstring_value(maybe_docstring, indent_spaces) %}
+{%- match maybe_docstring %}
+{%- when Some(docstring) %}
+{{ docstring|docstring(indent_spaces) }}
+{%- else %}
+{%- endmatch %}
+{%- endmacro %}
+
+{%- macro docstring(defn, indent_spaces) %}
+{%- call docstring_value(defn.docstring(), indent_spaces) %}
+{%- endmacro %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift
index c34d348efb..17fdde74e0 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift
@@ -1,5 +1,10 @@
// This file was autogenerated by some hot garbage in the `uniffi` crate.
// Trust me, you don't want to mess with it!
+
+// swiftlint:disable all
+
+{%- call swift::docstring_value(ci.namespace_docstring(), 0) %}
+
{%- import "macros.swift" as swift %}
import Foundation
{%- for imported_class in self.imports() %}
@@ -15,6 +20,7 @@ import {{ config.ffi_module_name() }}
{% include "RustBufferTemplate.swift" %}
{% include "Helpers.swift" %}
+{% include "HandleMap.swift" %}
// Public interface members begin here.
{{ type_helper_code }}
@@ -66,3 +72,5 @@ private func uniffiEnsureInitialized() {
fatalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
}
+
+// swiftlint:enable all
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/test.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/test.rs
index c3b2f15277..195a77696b 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/test.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/test.rs
@@ -2,10 +2,7 @@
License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use crate::{
- bindings::{RunScriptOptions, TargetLanguage},
- library_mode::generate_bindings,
-};
+use crate::{bindings::RunScriptOptions, library_mode::generate_bindings, BindingGeneratorDefault};
use anyhow::{bail, Context, Result};
use camino::{Utf8Path, Utf8PathBuf};
use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
@@ -15,6 +12,8 @@ use std::io::Write;
use std::process::{Command, Stdio};
use uniffi_testing::UniFFITestHelper;
+use crate::bindings::TargetLanguage;
+
/// Run Swift tests for a UniFFI test fixture
pub fn run_test(tmp_dir: &str, fixture_name: &str, script_file: &str) -> Result<()> {
run_script(
@@ -36,7 +35,7 @@ pub fn run_script(
args: Vec<String>,
options: &RunScriptOptions,
) -> Result<()> {
- let script_path = Utf8Path::new(".").join(script_file).canonicalize_utf8()?;
+ let script_path = Utf8Path::new(script_file).canonicalize_utf8()?;
let test_helper = UniFFITestHelper::new(crate_name)?;
let out_dir = test_helper.create_out_dir(tmp_dir, &script_path)?;
let cdylib_path = test_helper.copy_cdylib_to_out_dir(&out_dir)?;
@@ -126,8 +125,17 @@ struct GeneratedSources {
impl GeneratedSources {
fn new(crate_name: &str, cdylib_path: &Utf8Path, out_dir: &Utf8Path) -> Result<Self> {
- let sources =
- generate_bindings(cdylib_path, None, &[TargetLanguage::Swift], out_dir, false)?;
+ let sources = generate_bindings(
+ cdylib_path,
+ None,
+ &BindingGeneratorDefault {
+ target_languages: vec![TargetLanguage::Swift],
+ try_format_code: false,
+ },
+ None,
+ out_dir,
+ false,
+ )?;
let main_source = sources
.iter()
.find(|s| s.package.name == crate_name)
@@ -169,7 +177,7 @@ fn create_command(program: &str, options: &RunScriptOptions) -> Command {
if !options.show_compiler_messages {
// This prevents most compiler messages, but not remarks
command.arg("-suppress-warnings");
- // This gets the remarks. Note: swift will eventually get a `-supress-remarks` argument,
+ // This gets the remarks. Note: swift will eventually get a `-suppress-remarks` argument,
// maybe we can eventually move to that
command.stderr(Stdio::null());
}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs b/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs
index e3bca4f966..f176a7a684 100644
--- a/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs
+++ b/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs
@@ -33,9 +33,12 @@
//! # Ok::<(), anyhow::Error>(())
//! ```
+use std::iter;
+
+use heck::ToUpperCamelCase;
use uniffi_meta::Checksum;
-use super::ffi::{FfiArgument, FfiFunction, FfiType};
+use super::ffi::{FfiArgument, FfiCallbackFunction, FfiField, FfiFunction, FfiStruct, FfiType};
use super::object::Method;
use super::{AsType, Type, TypeIterator};
@@ -52,18 +55,11 @@ pub struct CallbackInterface {
// avoids a weird circular dependency in the calculation.
#[checksum_ignore]
pub(super) ffi_init_callback: FfiFunction,
+ #[checksum_ignore]
+ pub(super) docstring: Option<String>,
}
impl CallbackInterface {
- pub fn new(name: String, module_path: String) -> CallbackInterface {
- CallbackInterface {
- name,
- module_path,
- methods: Default::default(),
- ffi_init_callback: Default::default(),
- }
- }
-
pub fn name(&self) -> &str {
&self.name
}
@@ -77,18 +73,45 @@ impl CallbackInterface {
}
pub(super) fn derive_ffi_funcs(&mut self) {
- self.ffi_init_callback.name =
- uniffi_meta::init_callback_fn_symbol_name(&self.module_path, &self.name);
- self.ffi_init_callback.arguments = vec![FfiArgument {
- name: "callback_stub".to_string(),
- type_: FfiType::ForeignCallback,
- }];
- self.ffi_init_callback.return_type = None;
+ self.ffi_init_callback =
+ FfiFunction::callback_init(&self.module_path, &self.name, vtable_name(&self.name));
+ }
+
+ /// FfiCallbacks to define for our methods.
+ pub fn ffi_callbacks(&self) -> Vec<FfiCallbackFunction> {
+ ffi_callbacks(&self.name, &self.methods)
+ }
+
+ /// The VTable FFI type
+ pub fn vtable(&self) -> FfiType {
+ FfiType::Struct(vtable_name(&self.name))
+ }
+
+ /// the VTable struct to define.
+ pub fn vtable_definition(&self) -> FfiStruct {
+ vtable_struct(&self.name, &self.methods)
+ }
+
+ /// Vec of (ffi_callback, method) pairs
+ pub fn vtable_methods(&self) -> Vec<(FfiCallbackFunction, &Method)> {
+ self.methods
+ .iter()
+ .enumerate()
+ .map(|(i, method)| (method_ffi_callback(&self.name, method, i), method))
+ .collect()
}
pub fn iter_types(&self) -> TypeIterator<'_> {
Box::new(self.methods.iter().flat_map(Method::iter_types))
}
+
+ pub fn docstring(&self) -> Option<&str> {
+ self.docstring.as_deref()
+ }
+
+ pub fn has_async_method(&self) -> bool {
+ self.methods.iter().any(Method::is_async)
+ }
}
impl AsType for CallbackInterface {
@@ -100,6 +123,139 @@ impl AsType for CallbackInterface {
}
}
+impl TryFrom<uniffi_meta::CallbackInterfaceMetadata> for CallbackInterface {
+ type Error = anyhow::Error;
+
+ fn try_from(meta: uniffi_meta::CallbackInterfaceMetadata) -> anyhow::Result<Self> {
+ Ok(Self {
+ name: meta.name,
+ module_path: meta.module_path,
+ methods: Default::default(),
+ ffi_init_callback: Default::default(),
+ docstring: meta.docstring.clone(),
+ })
+ }
+}
+
+/// [FfiCallbackFunction] functions for the methods of a callback/trait interface
+pub fn ffi_callbacks(trait_name: &str, methods: &[Method]) -> Vec<FfiCallbackFunction> {
+ methods
+ .iter()
+ .enumerate()
+ .map(|(i, method)| method_ffi_callback(trait_name, method, i))
+ .collect()
+}
+
+pub fn method_ffi_callback(trait_name: &str, method: &Method, index: usize) -> FfiCallbackFunction {
+ if !method.is_async() {
+ FfiCallbackFunction {
+ name: method_ffi_callback_name(trait_name, index),
+ arguments: iter::once(FfiArgument::new("uniffi_handle", FfiType::UInt64))
+ .chain(method.arguments().into_iter().map(Into::into))
+ .chain(iter::once(match method.return_type() {
+ Some(t) => FfiArgument::new("uniffi_out_return", FfiType::from(t).reference()),
+ None => FfiArgument::new("uniffi_out_return", FfiType::VoidPointer),
+ }))
+ .collect(),
+ has_rust_call_status_arg: true,
+ return_type: None,
+ }
+ } else {
+ let completion_callback =
+ ffi_foreign_future_complete(method.return_type().map(FfiType::from));
+ FfiCallbackFunction {
+ name: method_ffi_callback_name(trait_name, index),
+ arguments: iter::once(FfiArgument::new("uniffi_handle", FfiType::UInt64))
+ .chain(method.arguments().into_iter().map(Into::into))
+ .chain([
+ FfiArgument::new(
+ "uniffi_future_callback",
+ FfiType::Callback(completion_callback.name),
+ ),
+ FfiArgument::new("uniffi_callback_data", FfiType::UInt64),
+ FfiArgument::new(
+ "uniffi_out_return",
+ FfiType::Struct("ForeignFuture".to_owned()).reference(),
+ ),
+ ])
+ .collect(),
+ has_rust_call_status_arg: false,
+ return_type: None,
+ }
+ }
+}
+
+/// Result struct to pass to the completion callback for async methods
+pub fn foreign_future_ffi_result_struct(return_ffi_type: Option<FfiType>) -> FfiStruct {
+ let return_type_name =
+ FfiType::return_type_name(return_ffi_type.as_ref()).to_upper_camel_case();
+ FfiStruct {
+ name: format!("ForeignFutureStruct{return_type_name}"),
+ fields: match return_ffi_type {
+ Some(return_ffi_type) => vec![
+ FfiField::new("return_value", return_ffi_type),
+ FfiField::new("call_status", FfiType::RustCallStatus),
+ ],
+ None => vec![
+ // In Rust, `return_value` is `()` -- a ZST.
+ // ZSTs are not valid in `C`, but they also take up 0 space.
+ // Skip the `return_value` field to make the layout correct.
+ FfiField::new("call_status", FfiType::RustCallStatus),
+ ],
+ },
+ }
+}
+
+/// Definition for callback functions to complete an async callback interface method
+pub fn ffi_foreign_future_complete(return_ffi_type: Option<FfiType>) -> FfiCallbackFunction {
+ let return_type_name =
+ FfiType::return_type_name(return_ffi_type.as_ref()).to_upper_camel_case();
+ FfiCallbackFunction {
+ name: format!("ForeignFutureComplete{return_type_name}"),
+ arguments: vec![
+ FfiArgument::new("callback_data", FfiType::UInt64),
+ FfiArgument::new(
+ "result",
+ FfiType::Struct(format!("ForeignFutureStruct{return_type_name}")),
+ ),
+ ],
+ return_type: None,
+ has_rust_call_status_arg: false,
+ }
+}
+
+/// [FfiStruct] for a callback/trait interface VTable
+///
+/// This struct has a FfiCallbackFunction field for each method, plus extra fields for special
+/// methods
+pub fn vtable_struct(trait_name: &str, methods: &[Method]) -> FfiStruct {
+ FfiStruct {
+ name: vtable_name(trait_name),
+ fields: methods
+ .iter()
+ .enumerate()
+ .map(|(i, method)| {
+ FfiField::new(
+ method.name(),
+ FfiType::Callback(format!("CallbackInterface{trait_name}Method{i}")),
+ )
+ })
+ .chain([FfiField::new(
+ "uniffi_free",
+ FfiType::Callback("CallbackInterfaceFree".to_owned()),
+ )])
+ .collect(),
+ }
+}
+
+pub fn method_ffi_callback_name(trait_name: &str, index: usize) -> String {
+ format!("CallbackInterface{trait_name}Method{index}")
+}
+
+pub fn vtable_name(trait_name: &str) -> String {
+ format!("VTableCallbackInterface{trait_name}")
+}
+
#[cfg(test)]
mod test {
use super::super::ComponentInterface;
@@ -146,4 +302,21 @@ mod test {
assert_eq!(callbacks_two.methods()[0].name(), "two");
assert_eq!(callbacks_two.methods()[1].name(), "too");
}
+
+ #[test]
+ fn test_docstring_callback_interface() {
+ const UDL: &str = r#"
+ namespace test{};
+ /// informative docstring
+ callback interface Testing { };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(
+ ci.get_callback_interface_definition("Testing")
+ .unwrap()
+ .docstring()
+ .unwrap(),
+ "informative docstring"
+ );
+ }
}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/enum_.rs b/third_party/rust/uniffi_bindgen/src/interface/enum_.rs
index 82baf1dd50..a666cc3605 100644
--- a/third_party/rust/uniffi_bindgen/src/interface/enum_.rs
+++ b/third_party/rust/uniffi_bindgen/src/interface/enum_.rs
@@ -94,7 +94,9 @@
//!
//! ```
//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
-//! # namespace example {};
+//! # namespace example {
+//! # [Throws=Example] void func();
+//! # };
//! # [Error]
//! # enum Example {
//! # "one",
@@ -130,7 +132,9 @@
//!
//! ```
//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
-//! # namespace example {};
+//! # namespace example {
+//! # [Throws=Example] void func();
+//! # };
//! # [Error]
//! # interface Example {
//! # one();
@@ -159,7 +163,7 @@ use anyhow::Result;
use uniffi_meta::Checksum;
use super::record::Field;
-use super::{AsType, Type, TypeIterator};
+use super::{AsType, Literal, Type, TypeIterator};
/// Represents an enum with named variants, each of which may have named
/// and typed fields.
@@ -170,6 +174,7 @@ use super::{AsType, Type, TypeIterator};
pub struct Enum {
pub(super) name: String,
pub(super) module_path: String,
+ pub(super) discr_type: Option<Type>,
pub(super) variants: Vec<Variant>,
// NOTE: `flat` is a misleading name and to make matters worse, has 2 different
// meanings depending on the context :(
@@ -189,6 +194,9 @@ pub struct Enum {
// * For an Enum not used as an error but which has no variants with data, `flat` will be
// false when generating the scaffolding but `true` when generating bindings.
pub(super) flat: bool,
+ pub(super) non_exhaustive: bool,
+ #[checksum_ignore]
+ pub(super) docstring: Option<String>,
}
impl Enum {
@@ -200,14 +208,61 @@ impl Enum {
&self.variants
}
+ // Get the literal value to use for the specified variant's discriminant.
+ // Follows Rust's rules when mixing specified and unspecified values; please
+ // file a bug if you find a case where it does not.
+ // However, it *does not* attempt to handle error cases - either cases where
+ // a discriminant is not unique, or where a discriminant would overflow the
+ // repr. The intention is that the Rust compiler itself will fail to build
+ // in those cases, so by the time this get's run we can be confident these
+ // error cases can't exist.
+ pub fn variant_discr(&self, variant_index: usize) -> Result<Literal> {
+ if variant_index >= self.variants.len() {
+ anyhow::bail!("Invalid variant index {variant_index}");
+ }
+ let mut next = 0;
+ let mut this;
+ let mut this_lit = Literal::new_uint(0);
+ for v in self.variants().iter().take(variant_index + 1) {
+ (this, this_lit) = match v.discr {
+ None => (
+ next,
+ if (next as i64) < 0 {
+ Literal::new_int(next as i64)
+ } else {
+ Literal::new_uint(next)
+ },
+ ),
+ Some(Literal::UInt(v, _, _)) => (v, Literal::new_uint(v)),
+ // in-practice, Literal::Int == a negative number.
+ Some(Literal::Int(v, _, _)) => (v as u64, Literal::new_int(v)),
+ _ => anyhow::bail!("Invalid literal type {v:?}"),
+ };
+ next = this.wrapping_add(1);
+ }
+ Ok(this_lit)
+ }
+
+ pub fn variant_discr_type(&self) -> &Option<Type> {
+ &self.discr_type
+ }
+
pub fn is_flat(&self) -> bool {
self.flat
}
+ pub fn is_non_exhaustive(&self) -> bool {
+ self.non_exhaustive
+ }
+
pub fn iter_types(&self) -> TypeIterator<'_> {
Box::new(self.variants.iter().flat_map(Variant::iter_types))
}
+ pub fn docstring(&self) -> Option<&str> {
+ self.docstring.as_deref()
+ }
+
// Sadly can't use TryFrom due to the 'is_flat' complication.
pub fn try_from_meta(meta: uniffi_meta::EnumMetadata, flat: bool) -> Result<Self> {
// This is messy - error enums are considered "flat" if the user
@@ -218,12 +273,15 @@ impl Enum {
Ok(Self {
name: meta.name,
module_path: meta.module_path,
+ discr_type: meta.discr_type,
variants: meta
.variants
.into_iter()
.map(TryInto::try_into)
.collect::<Result<_>>()?,
flat,
+ non_exhaustive: meta.non_exhaustive,
+ docstring: meta.docstring.clone(),
})
}
}
@@ -243,7 +301,10 @@ impl AsType for Enum {
#[derive(Debug, Clone, Default, PartialEq, Eq, Checksum)]
pub struct Variant {
pub(super) name: String,
+ pub(super) discr: Option<Literal>,
pub(super) fields: Vec<Field>,
+ #[checksum_ignore]
+ pub(super) docstring: Option<String>,
}
impl Variant {
@@ -259,6 +320,14 @@ impl Variant {
!self.fields.is_empty()
}
+ pub fn has_nameless_fields(&self) -> bool {
+ self.fields.iter().any(|f| f.name.is_empty())
+ }
+
+ pub fn docstring(&self) -> Option<&str> {
+ self.docstring.as_deref()
+ }
+
pub fn iter_types(&self) -> TypeIterator<'_> {
Box::new(self.fields.iter().flat_map(Field::iter_types))
}
@@ -270,11 +339,13 @@ impl TryFrom<uniffi_meta::VariantMetadata> for Variant {
fn try_from(meta: uniffi_meta::VariantMetadata) -> Result<Self> {
Ok(Self {
name: meta.name,
+ discr: meta.discr,
fields: meta
.fields
.into_iter()
.map(TryInto::try_into)
.collect::<Result<_>>()?,
+ docstring: meta.docstring.clone(),
})
}
}
@@ -447,7 +518,10 @@ mod test {
#[test]
fn test_variants() {
const UDL: &str = r#"
- namespace test{};
+ namespace test{
+ [Throws=Testing]
+ void func();
+ };
[Error]
enum Testing { "one", "two", "three" };
"#;
@@ -486,7 +560,10 @@ mod test {
#[test]
fn test_variant_data() {
const UDL: &str = r#"
- namespace test{};
+ namespace test{
+ [Throws=Testing]
+ void func();
+ };
[Error]
interface Testing {
@@ -564,4 +641,141 @@ mod test {
vec!["Normal", "Error"]
);
}
+
+ fn variant(val: Option<u64>) -> Variant {
+ Variant {
+ name: "v".to_string(),
+ discr: val.map(Literal::new_uint),
+ fields: vec![],
+ docstring: None,
+ }
+ }
+
+ fn check_discrs(e: &mut Enum, vs: Vec<Variant>) -> Vec<u64> {
+ e.variants = vs;
+ (0..e.variants.len())
+ .map(|i| e.variant_discr(i).unwrap())
+ .map(|l| match l {
+ Literal::UInt(v, _, _) => v,
+ _ => unreachable!(),
+ })
+ .collect()
+ }
+
+ #[test]
+ fn test_variant_values() {
+ let mut e = Enum {
+ module_path: "test".to_string(),
+ name: "test".to_string(),
+ discr_type: None,
+ variants: vec![],
+ flat: false,
+ non_exhaustive: false,
+ docstring: None,
+ };
+
+ assert!(e.variant_discr(0).is_err());
+
+ // single values
+ assert_eq!(check_discrs(&mut e, vec![variant(None)]), vec![0]);
+ assert_eq!(check_discrs(&mut e, vec![variant(Some(3))]), vec![3]);
+
+ // no values
+ assert_eq!(
+ check_discrs(&mut e, vec![variant(None), variant(None)]),
+ vec![0, 1]
+ );
+
+ // values
+ assert_eq!(
+ check_discrs(&mut e, vec![variant(Some(1)), variant(Some(3))]),
+ vec![1, 3]
+ );
+
+ // mixed values
+ assert_eq!(
+ check_discrs(&mut e, vec![variant(None), variant(Some(3)), variant(None)]),
+ vec![0, 3, 4]
+ );
+
+ assert_eq!(
+ check_discrs(
+ &mut e,
+ vec![variant(Some(4)), variant(None), variant(Some(1))]
+ ),
+ vec![4, 5, 1]
+ );
+ }
+
+ #[test]
+ fn test_docstring_enum() {
+ const UDL: &str = r#"
+ namespace test{};
+ /// informative docstring
+ enum Testing { "foo" };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(
+ ci.get_enum_definition("Testing")
+ .unwrap()
+ .docstring()
+ .unwrap(),
+ "informative docstring"
+ );
+ }
+
+ #[test]
+ fn test_docstring_enum_variant() {
+ const UDL: &str = r#"
+ namespace test{};
+ enum Testing {
+ /// informative docstring
+ "foo"
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(
+ ci.get_enum_definition("Testing").unwrap().variants()[0]
+ .docstring()
+ .unwrap(),
+ "informative docstring"
+ );
+ }
+
+ #[test]
+ fn test_docstring_associated_enum() {
+ const UDL: &str = r#"
+ namespace test{};
+ /// informative docstring
+ [Enum]
+ interface Testing { };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(
+ ci.get_enum_definition("Testing")
+ .unwrap()
+ .docstring()
+ .unwrap(),
+ "informative docstring"
+ );
+ }
+
+ #[test]
+ fn test_docstring_associated_enum_variant() {
+ const UDL: &str = r#"
+ namespace test{};
+ [Enum]
+ interface Testing {
+ /// informative docstring
+ testing();
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(
+ ci.get_enum_definition("Testing").unwrap().variants()[0]
+ .docstring()
+ .unwrap(),
+ "informative docstring"
+ );
+ }
}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/ffi.rs b/third_party/rust/uniffi_bindgen/src/interface/ffi.rs
index d18aaf8262..b27cb78477 100644
--- a/third_party/rust/uniffi_bindgen/src/interface/ffi.rs
+++ b/third_party/rust/uniffi_bindgen/src/interface/ffi.rs
@@ -47,20 +47,49 @@ pub enum FfiType {
/// A borrowed reference to some raw bytes owned by foreign language code.
/// The provider of this reference must keep it alive for the duration of the receiving call.
ForeignBytes,
- /// Pointer to a callback function that handles all callbacks on the foreign language side.
- ForeignCallback,
- /// Pointer-sized opaque handle that represents a foreign executor. Foreign bindings can
- /// either use an actual pointer or a usized integer.
- ForeignExecutorHandle,
- /// Pointer to the callback function that's invoked to schedule calls with a ForeignExecutor
- ForeignExecutorCallback,
- /// Pointer to a Rust future
- RustFutureHandle,
- /// Continuation function for a Rust future
- RustFutureContinuationCallback,
- RustFutureContinuationData,
- // TODO: you can imagine a richer structural typesystem here, e.g. `Ref<String>` or something.
- // We don't need that yet and it's possible we never will, so it isn't here for now.
+ /// Pointer to a callback function. The inner value which matches one of the callback
+ /// definitions in [crate::ComponentInterface::ffi_definitions].
+ Callback(String),
+ /// Pointer to a FFI struct (e.g. a VTable). The inner value matches one of the struct
+ /// definitions in [crate::ComponentInterface::ffi_definitions].
+ Struct(String),
+ /// Opaque 64-bit handle
+ ///
+ /// These are used to pass objects across the FFI.
+ Handle,
+ RustCallStatus,
+ /// Pointer to an FfiType.
+ Reference(Box<FfiType>),
+ /// Opaque pointer
+ VoidPointer,
+}
+
+impl FfiType {
+ pub fn reference(self) -> FfiType {
+ FfiType::Reference(Box::new(self))
+ }
+
+ /// Unique name for an FFI return type
+ pub fn return_type_name(return_type: Option<&FfiType>) -> String {
+ match return_type {
+ Some(t) => match t {
+ FfiType::UInt8 => "u8".to_owned(),
+ FfiType::Int8 => "i8".to_owned(),
+ FfiType::UInt16 => "u16".to_owned(),
+ FfiType::Int16 => "i16".to_owned(),
+ FfiType::UInt32 => "u32".to_owned(),
+ FfiType::Int32 => "i32".to_owned(),
+ FfiType::UInt64 => "u64".to_owned(),
+ FfiType::Int64 => "i64".to_owned(),
+ FfiType::Float32 => "f32".to_owned(),
+ FfiType::Float64 => "f64".to_owned(),
+ FfiType::RustArcPtr(_) => "pointer".to_owned(),
+ FfiType::RustBuffer(_) => "rust_buffer".to_owned(),
+ _ => unimplemented!("FFI return type: {t:?}"),
+ },
+ None => "void".to_owned(),
+ }
+ }
}
/// When passing data across the FFI, each `Type` value will be lowered into a corresponding
@@ -94,7 +123,6 @@ impl From<&Type> for FfiType {
Type::Object { name, .. } => FfiType::RustArcPtr(name.to_owned()),
// Callback interfaces are passed as opaque integer handles.
Type::CallbackInterface { .. } => FfiType::UInt64,
- Type::ForeignExecutor => FfiType::ForeignExecutorHandle,
// Other types are serialized into a bytebuffer and deserialized on the other side.
Type::Enum { .. }
| Type::Record { .. }
@@ -107,6 +135,11 @@ impl From<&Type> for FfiType {
name,
kind: ExternalKind::Interface,
..
+ }
+ | Type::External {
+ name,
+ kind: ExternalKind::Trait,
+ ..
} => FfiType::RustArcPtr(name.clone()),
Type::External {
name,
@@ -131,6 +164,24 @@ impl From<&&Type> for FfiType {
}
}
+/// An Ffi definition
+#[derive(Debug, Clone)]
+pub enum FfiDefinition {
+ Function(FfiFunction),
+ CallbackFunction(FfiCallbackFunction),
+ Struct(FfiStruct),
+}
+
+impl FfiDefinition {
+ pub fn name(&self) -> &str {
+ match self {
+ Self::Function(f) => f.name(),
+ Self::CallbackFunction(f) => f.name(),
+ Self::Struct(s) => s.name(),
+ }
+ }
+}
+
/// Represents an "extern C"-style function that will be part of the FFI.
///
/// These can't be declared explicitly in the UDL, but rather, are derived automatically
@@ -150,6 +201,19 @@ pub struct FfiFunction {
}
impl FfiFunction {
+ pub fn callback_init(module_path: &str, trait_name: &str, vtable_name: String) -> Self {
+ Self {
+ name: uniffi_meta::init_callback_vtable_fn_symbol_name(module_path, trait_name),
+ arguments: vec![FfiArgument {
+ name: "vtable".to_string(),
+ type_: FfiType::Struct(vtable_name).reference(),
+ }],
+ return_type: None,
+ has_rust_call_status_arg: false,
+ ..Self::default()
+ }
+ }
+
pub fn name(&self) -> &str {
&self.name
}
@@ -181,7 +245,7 @@ impl FfiFunction {
) {
self.arguments = args.into_iter().collect();
if self.is_async() {
- self.return_type = Some(FfiType::RustFutureHandle);
+ self.return_type = Some(FfiType::Handle);
self.has_rust_call_status_arg = false;
} else {
self.return_type = return_type;
@@ -212,14 +276,113 @@ pub struct FfiArgument {
}
impl FfiArgument {
+ pub fn new(name: impl Into<String>, type_: FfiType) -> Self {
+ Self {
+ name: name.into(),
+ type_,
+ }
+ }
+
pub fn name(&self) -> &str {
&self.name
}
+
pub fn type_(&self) -> FfiType {
self.type_.clone()
}
}
+/// Represents an "extern C"-style callback function
+///
+/// These are defined in the foreign code and passed to Rust as a function pointer.
+#[derive(Debug, Default, Clone)]
+pub struct FfiCallbackFunction {
+ // Name for this function type. This matches the value inside `FfiType::Callback`
+ pub(super) name: String,
+ pub(super) arguments: Vec<FfiArgument>,
+ pub(super) return_type: Option<FfiType>,
+ pub(super) has_rust_call_status_arg: bool,
+}
+
+impl FfiCallbackFunction {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn arguments(&self) -> Vec<&FfiArgument> {
+ self.arguments.iter().collect()
+ }
+
+ pub fn return_type(&self) -> Option<&FfiType> {
+ self.return_type.as_ref()
+ }
+
+ pub fn has_rust_call_status_arg(&self) -> bool {
+ self.has_rust_call_status_arg
+ }
+}
+
+/// Represents a repr(C) struct used in the FFI
+#[derive(Debug, Default, Clone)]
+pub struct FfiStruct {
+ pub(super) name: String,
+ pub(super) fields: Vec<FfiField>,
+}
+
+impl FfiStruct {
+ /// Get the name of this struct
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ /// Get the fields for this struct
+ pub fn fields(&self) -> &[FfiField] {
+ &self.fields
+ }
+}
+
+/// Represents a field of an [FfiStruct]
+#[derive(Debug, Clone)]
+pub struct FfiField {
+ pub(super) name: String,
+ pub(super) type_: FfiType,
+}
+
+impl FfiField {
+ pub fn new(name: impl Into<String>, type_: FfiType) -> Self {
+ Self {
+ name: name.into(),
+ type_,
+ }
+ }
+
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn type_(&self) -> FfiType {
+ self.type_.clone()
+ }
+}
+
+impl From<FfiFunction> for FfiDefinition {
+ fn from(value: FfiFunction) -> FfiDefinition {
+ FfiDefinition::Function(value)
+ }
+}
+
+impl From<FfiStruct> for FfiDefinition {
+ fn from(value: FfiStruct) -> FfiDefinition {
+ FfiDefinition::Struct(value)
+ }
+}
+
+impl From<FfiCallbackFunction> for FfiDefinition {
+ fn from(value: FfiCallbackFunction) -> FfiDefinition {
+ FfiDefinition::CallbackFunction(value)
+ }
+}
+
#[cfg(test)]
mod test {
// There's not really much to test here to be honest,
diff --git a/third_party/rust/uniffi_bindgen/src/interface/function.rs b/third_party/rust/uniffi_bindgen/src/interface/function.rs
index 2d18288c1c..8effc4c876 100644
--- a/third_party/rust/uniffi_bindgen/src/interface/function.rs
+++ b/third_party/rust/uniffi_bindgen/src/interface/function.rs
@@ -59,6 +59,8 @@ pub struct Function {
// avoids a weird circular dependency in the calculation.
#[checksum_ignore]
pub(super) ffi_func: FfiFunction,
+ #[checksum_ignore]
+ pub(super) docstring: Option<String>,
pub(super) throws: Option<Type>,
pub(super) checksum_fn_name: String,
// Force a checksum value, or we'll fallback to the trait.
@@ -128,6 +130,10 @@ impl Function {
.chain(self.return_type.iter().flat_map(Type::iter_types)),
)
}
+
+ pub fn docstring(&self) -> Option<&str> {
+ self.docstring.as_deref()
+ }
}
impl From<uniffi_meta::FnParamMetadata> for Argument {
@@ -163,6 +169,7 @@ impl From<uniffi_meta::FnMetadata> for Function {
arguments,
return_type,
ffi_func,
+ docstring: meta.docstring.clone(),
throws: meta.throws,
checksum_fn_name,
checksum: meta.checksum,
@@ -242,6 +249,9 @@ pub trait Callable {
fn return_type(&self) -> Option<Type>;
fn throws_type(&self) -> Option<Type>;
fn is_async(&self) -> bool;
+ fn takes_self(&self) -> bool {
+ false
+ }
fn result_type(&self) -> ResultType {
ResultType {
return_type: self.return_type(),
@@ -311,6 +321,10 @@ impl<T: Callable> Callable for &T {
fn is_async(&self) -> bool {
(*self).is_async()
}
+
+ fn takes_self(&self) -> bool {
+ (*self).takes_self()
+ }
}
#[cfg(test)]
@@ -364,4 +378,22 @@ mod test {
);
Ok(())
}
+
+ #[test]
+ fn test_docstring_function() {
+ const UDL: &str = r#"
+ namespace test {
+ /// informative docstring
+ void testing();
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(
+ ci.get_function_definition("testing")
+ .unwrap()
+ .docstring()
+ .unwrap(),
+ "informative docstring"
+ );
+ }
}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/mod.rs b/third_party/rust/uniffi_bindgen/src/interface/mod.rs
index 8e4df2149b..90a941637a 100644
--- a/third_party/rust/uniffi_bindgen/src/interface/mod.rs
+++ b/third_party/rust/uniffi_bindgen/src/interface/mod.rs
@@ -67,7 +67,9 @@ mod record;
pub use record::{Field, Record};
pub mod ffi;
-pub use ffi::{FfiArgument, FfiFunction, FfiType};
+pub use ffi::{
+ FfiArgument, FfiCallbackFunction, FfiDefinition, FfiField, FfiFunction, FfiStruct, FfiType,
+};
pub use uniffi_meta::Radix;
use uniffi_meta::{
ConstructorMetadata, LiteralMetadata, NamespaceMetadata, ObjectMetadata, TraitMethodMetadata,
@@ -139,6 +141,11 @@ impl ComponentInterface {
self.types.namespace
);
}
+
+ if group.namespace_docstring.is_some() {
+ self.types.namespace_docstring = group.namespace_docstring.clone();
+ }
+
// Unconditionally add the String type, which is used by the panic handling
self.types.add_known_type(&uniffi_meta::Type::String)?;
crate::macro_metadata::add_group_to_ci(self, group)?;
@@ -153,6 +160,10 @@ impl ComponentInterface {
&self.types.namespace.name
}
+ pub fn namespace_docstring(&self) -> Option<&str> {
+ self.types.namespace_docstring.as_deref()
+ }
+
pub fn uniffi_contract_version(&self) -> u32 {
// This is set by the scripts in the version-mismatch fixture
let force_version = std::env::var("UNIFFI_FORCE_CONTRACT_VERSION");
@@ -204,6 +215,29 @@ impl ComponentInterface {
self.objects.iter().find(|o| o.name == name)
}
+ fn callback_interface_callback_definitions(
+ &self,
+ ) -> impl IntoIterator<Item = FfiCallbackFunction> + '_ {
+ self.callback_interfaces
+ .iter()
+ .flat_map(|cbi| cbi.ffi_callbacks())
+ .chain(self.objects.iter().flat_map(|o| o.ffi_callbacks()))
+ }
+
+ /// Get the definitions for callback FFI functions
+ ///
+ /// These are defined by the foreign code and invoked by Rust.
+ fn callback_interface_vtable_definitions(&self) -> impl IntoIterator<Item = FfiStruct> + '_ {
+ self.callback_interface_definitions()
+ .iter()
+ .map(|cbi| cbi.vtable_definition())
+ .chain(
+ self.object_definitions()
+ .iter()
+ .flat_map(|o| o.vtable_definition()),
+ )
+ }
+
/// Get the definitions for every Callback Interface type in the interface.
pub fn callback_interface_definitions(&self) -> &[CallbackInterface] {
&self.callback_interfaces
@@ -215,6 +249,17 @@ impl ComponentInterface {
self.callback_interfaces.iter().find(|o| o.name == name)
}
+ /// Get the definitions for every Callback Interface type in the interface.
+ pub fn has_async_callback_interface_definition(&self) -> bool {
+ self.callback_interfaces
+ .iter()
+ .any(|cbi| cbi.has_async_method())
+ || self
+ .objects
+ .iter()
+ .any(|o| o.has_callback_interface() && o.has_async_method())
+ }
+
/// Get the definitions for every Method type in the interface.
pub fn iter_callables(&self) -> impl Iterator<Item = &dyn Callable> {
// Each of the `as &dyn Callable` casts is a trivial cast, but it seems like the clearest
@@ -241,13 +286,19 @@ impl ComponentInterface {
let fielded = !e.is_flat();
// For flat errors, we should only generate read() methods if we need them to support
// callback interface errors
- let used_in_callback_interface = self
+ let used_in_foreign_interface = self
.callback_interface_definitions()
.iter()
.flat_map(|cb| cb.methods())
+ .chain(
+ self.object_definitions()
+ .iter()
+ .filter(|o| o.has_callback_interface())
+ .flat_map(|o| o.methods()),
+ )
.any(|m| m.throws_type() == Some(&e.as_type()));
- self.is_name_used_as_error(&e.name) && (fielded || used_in_callback_interface)
+ self.is_name_used_as_error(&e.name) && (fielded || used_in_foreign_interface)
}
/// Get details about all `Type::External` types.
@@ -304,8 +355,17 @@ impl ComponentInterface {
/// This is important to know in language bindings that cannot integrate object types
/// tightly with the host GC, and hence need to perform manual destruction of objects.
pub fn item_contains_object_references(&self, item: &Type) -> bool {
- self.iter_types_in_item(item)
- .any(|t| matches!(t, Type::Object { .. }))
+ // this is surely broken for external records with object refs?
+ self.iter_types_in_item(item).any(|t| {
+ matches!(
+ t,
+ Type::Object { .. }
+ | Type::External {
+ kind: ExternalKind::Interface,
+ ..
+ }
+ )
+ })
}
/// Check whether the given item contains any (possibly nested) unsigned types
@@ -335,6 +395,13 @@ impl ComponentInterface {
.any(|t| matches!(t, Type::Map { .. }))
}
+ /// Check whether the interface contains any object types
+ pub fn contains_object_types(&self) -> bool {
+ self.types
+ .iter_known_types()
+ .any(|t| matches!(t, Type::Object { .. }))
+ }
+
// The namespace to use in crate-level FFI function definitions. Not used as the ffi
// namespace for types - each type has its own `module_path` which is used for them.
fn ffi_namespace(&self) -> &str {
@@ -364,7 +431,7 @@ impl ComponentInterface {
is_async: false,
arguments: vec![FfiArgument {
name: "size".to_string(),
- type_: FfiType::Int32,
+ type_: FfiType::UInt64,
}],
return_type: Some(FfiType::RustBuffer(None)),
has_rust_call_status_arg: true,
@@ -420,7 +487,7 @@ impl ComponentInterface {
},
FfiArgument {
name: "additional".to_string(),
- type_: FfiType::Int32,
+ type_: FfiType::UInt64,
},
],
return_type: Some(FfiType::RustBuffer(None)),
@@ -429,24 +496,6 @@ impl ComponentInterface {
}
}
- /// Builtin FFI function to set the Rust Future continuation callback
- pub fn ffi_rust_future_continuation_callback_set(&self) -> FfiFunction {
- FfiFunction {
- name: format!(
- "ffi_{}_rust_future_continuation_callback_set",
- self.ffi_namespace()
- ),
- arguments: vec![FfiArgument {
- name: "callback".to_owned(),
- type_: FfiType::RustFutureContinuationCallback,
- }],
- return_type: None,
- is_async: false,
- has_rust_call_status_arg: false,
- is_object_free_function: false,
- }
- }
-
/// Builtin FFI function to poll a Rust future.
pub fn ffi_rust_future_poll(&self, return_ffi_type: Option<FfiType>) -> FfiFunction {
FfiFunction {
@@ -455,12 +504,15 @@ impl ComponentInterface {
arguments: vec![
FfiArgument {
name: "handle".to_owned(),
- type_: FfiType::RustFutureHandle,
+ type_: FfiType::Handle,
+ },
+ FfiArgument {
+ name: "callback".to_owned(),
+ type_: FfiType::Callback("RustFutureContinuationCallback".to_owned()),
},
- // Data to pass to the continuation
FfiArgument {
- name: "uniffi_callback".to_owned(),
- type_: FfiType::RustFutureContinuationData,
+ name: "callback_data".to_owned(),
+ type_: FfiType::Handle,
},
],
return_type: None,
@@ -478,7 +530,7 @@ impl ComponentInterface {
is_async: false,
arguments: vec![FfiArgument {
name: "handle".to_owned(),
- type_: FfiType::RustFutureHandle,
+ type_: FfiType::Handle,
}],
return_type: return_ffi_type,
has_rust_call_status_arg: true,
@@ -493,7 +545,7 @@ impl ComponentInterface {
is_async: false,
arguments: vec![FfiArgument {
name: "handle".to_owned(),
- type_: FfiType::RustFutureHandle,
+ type_: FfiType::Handle,
}],
return_type: None,
has_rust_call_status_arg: false,
@@ -508,7 +560,7 @@ impl ComponentInterface {
is_async: false,
arguments: vec![FfiArgument {
name: "handle".to_owned(),
- type_: FfiType::RustFutureHandle,
+ type_: FfiType::Handle,
}],
return_type: None,
has_rust_call_status_arg: false,
@@ -518,29 +570,17 @@ impl ComponentInterface {
fn rust_future_ffi_fn_name(&self, base_name: &str, return_ffi_type: Option<FfiType>) -> String {
let namespace = self.ffi_namespace();
- match return_ffi_type {
- Some(t) => match t {
- FfiType::UInt8 => format!("ffi_{namespace}_{base_name}_u8"),
- FfiType::Int8 => format!("ffi_{namespace}_{base_name}_i8"),
- FfiType::UInt16 => format!("ffi_{namespace}_{base_name}_u16"),
- FfiType::Int16 => format!("ffi_{namespace}_{base_name}_i16"),
- FfiType::UInt32 => format!("ffi_{namespace}_{base_name}_u32"),
- FfiType::Int32 => format!("ffi_{namespace}_{base_name}_i32"),
- FfiType::UInt64 => format!("ffi_{namespace}_{base_name}_u64"),
- FfiType::Int64 => format!("ffi_{namespace}_{base_name}_i64"),
- FfiType::Float32 => format!("ffi_{namespace}_{base_name}_f32"),
- FfiType::Float64 => format!("ffi_{namespace}_{base_name}_f64"),
- FfiType::RustArcPtr(_) => format!("ffi_{namespace}_{base_name}_pointer"),
- FfiType::RustBuffer(_) => format!("ffi_{namespace}_{base_name}_rust_buffer"),
- _ => unimplemented!("FFI return type: {t:?}"),
- },
- None => format!("ffi_{namespace}_{base_name}_void"),
- }
+ let return_type_name = FfiType::return_type_name(return_ffi_type.as_ref());
+ format!("ffi_{namespace}_{base_name}_{return_type_name}")
}
/// Does this interface contain async functions?
pub fn has_async_fns(&self) -> bool {
self.iter_ffi_function_definitions().any(|f| f.is_async())
+ || self
+ .callback_interfaces
+ .iter()
+ .any(CallbackInterface::has_async_method)
}
/// Iterate over `T` parameters of the `FutureCallback<T>` callbacks in this interface
@@ -561,6 +601,73 @@ impl ComponentInterface {
unique_results.into_iter()
}
+ /// Iterate over all Ffi definitions
+ pub fn ffi_definitions(&self) -> impl Iterator<Item = FfiDefinition> + '_ {
+ // Note: for languages like Python it's important to keep things in dependency order.
+ // For example some FFI function definitions depend on FFI struct definitions, so the
+ // function definitions come last.
+ self.builtin_ffi_definitions()
+ .into_iter()
+ .chain(
+ self.callback_interface_callback_definitions()
+ .into_iter()
+ .map(Into::into),
+ )
+ .chain(
+ self.callback_interface_vtable_definitions()
+ .into_iter()
+ .map(Into::into),
+ )
+ .chain(self.iter_ffi_function_definitions().map(Into::into))
+ }
+
+ fn builtin_ffi_definitions(&self) -> impl IntoIterator<Item = FfiDefinition> + '_ {
+ [
+ FfiCallbackFunction {
+ name: "RustFutureContinuationCallback".to_owned(),
+ arguments: vec![
+ FfiArgument::new("data", FfiType::UInt64),
+ FfiArgument::new("poll_result", FfiType::Int8),
+ ],
+ return_type: None,
+ has_rust_call_status_arg: false,
+ }
+ .into(),
+ FfiCallbackFunction {
+ name: "ForeignFutureFree".to_owned(),
+ arguments: vec![FfiArgument::new("handle", FfiType::UInt64)],
+ return_type: None,
+ has_rust_call_status_arg: false,
+ }
+ .into(),
+ FfiCallbackFunction {
+ name: "CallbackInterfaceFree".to_owned(),
+ arguments: vec![FfiArgument::new("handle", FfiType::UInt64)],
+ return_type: None,
+ has_rust_call_status_arg: false,
+ }
+ .into(),
+ FfiStruct {
+ name: "ForeignFuture".to_owned(),
+ fields: vec![
+ FfiField::new("handle", FfiType::UInt64),
+ FfiField::new("free", FfiType::Callback("ForeignFutureFree".to_owned())),
+ ],
+ }
+ .into(),
+ ]
+ .into_iter()
+ .chain(
+ self.all_possible_return_ffi_types()
+ .flat_map(|return_type| {
+ [
+ callbacks::foreign_future_ffi_result_struct(return_type.clone()).into(),
+ callbacks::ffi_foreign_future_complete(return_type).into(),
+ ]
+ }),
+ )
+ }
+
/// List the definitions of all FFI functions in the interface.
///
/// The set of FFI functions is derived automatically from the set of higher-level types
@@ -569,9 +676,8 @@ impl ComponentInterface {
self.iter_user_ffi_function_definitions()
.cloned()
.chain(self.iter_rust_buffer_ffi_function_definitions())
- .chain(self.iter_futures_ffi_function_definitons())
+ .chain(self.iter_futures_ffi_function_definitions())
.chain(self.iter_checksum_ffi_functions())
- .chain(self.ffi_foreign_executor_callback_set())
.chain([self.ffi_uniffi_contract_version()])
}
@@ -618,9 +724,8 @@ impl ComponentInterface {
.into_iter()
}
- /// List all FFI functions definitions for async functionality.
- pub fn iter_futures_ffi_function_definitons(&self) -> impl Iterator<Item = FfiFunction> + '_ {
- let all_possible_return_ffi_types = [
+ fn all_possible_return_ffi_types(&self) -> impl Iterator<Item = Option<FfiType>> {
+ [
Some(FfiType::UInt8),
Some(FfiType::Int8),
Some(FfiType::UInt16),
@@ -631,46 +736,26 @@ impl ComponentInterface {
Some(FfiType::Int64),
Some(FfiType::Float32),
Some(FfiType::Float64),
- // RustBuffer and RustArcPtr have an inner field which doesn't affect the rust future
- // complete scaffolding function, so we just use a placeholder value here.
+ // RustBuffer and RustArcPtr have an inner field which we have to fill in with a
+ // placeholder value.
Some(FfiType::RustArcPtr("".to_owned())),
Some(FfiType::RustBuffer(None)),
None,
- ];
-
- iter::once(self.ffi_rust_future_continuation_callback_set()).chain(
- all_possible_return_ffi_types
- .into_iter()
- .flat_map(|return_type| {
- [
- self.ffi_rust_future_poll(return_type.clone()),
- self.ffi_rust_future_cancel(return_type.clone()),
- self.ffi_rust_future_free(return_type.clone()),
- self.ffi_rust_future_complete(return_type),
- ]
- }),
- )
+ ]
+ .into_iter()
}
- /// The ffi_foreign_executor_callback_set FFI function
- ///
- /// We only include this in the FFI if the `ForeignExecutor` type is actually used
- pub fn ffi_foreign_executor_callback_set(&self) -> Option<FfiFunction> {
- if self.types.contains(&Type::ForeignExecutor) {
- Some(FfiFunction {
- name: format!("ffi_{}_foreign_executor_callback_set", self.ffi_namespace()),
- arguments: vec![FfiArgument {
- name: "callback".into(),
- type_: FfiType::ForeignExecutorCallback,
- }],
- return_type: None,
- is_async: false,
- has_rust_call_status_arg: false,
- is_object_free_function: false,
+ /// List all FFI functions definitions for async functionality.
+ pub fn iter_futures_ffi_function_definitions(&self) -> impl Iterator<Item = FfiFunction> + '_ {
+ self.all_possible_return_ffi_types()
+ .flat_map(|return_type| {
+ [
+ self.ffi_rust_future_poll(return_type.clone()),
+ self.ffi_rust_future_cancel(return_type.clone()),
+ self.ffi_rust_future_free(return_type.clone()),
+ self.ffi_rust_future_complete(return_type),
+ ]
})
- } else {
- None
- }
}
/// List all API checksums to check
@@ -778,6 +863,8 @@ impl ComponentInterface {
bail!("Conflicting type definition for \"{}\"", defn.name());
}
self.types.add_known_types(defn.iter_types())?;
+ defn.throws_name()
+ .map(|n| self.errors.insert(n.to_string()));
self.functions.push(defn);
Ok(())
@@ -789,6 +876,8 @@ impl ComponentInterface {
let defn: Constructor = meta.into();
self.types.add_known_types(defn.iter_types())?;
+ defn.throws_name()
+ .map(|n| self.errors.insert(n.to_string()));
object.constructors.push(defn);
Ok(())
@@ -800,6 +889,9 @@ impl ComponentInterface {
.ok_or_else(|| anyhow!("add_method_meta: object {} not found", &method.object_name))?;
self.types.add_known_types(method.iter_types())?;
+ method
+ .throws_name()
+ .map(|n| self.errors.insert(n.to_string()));
method.object_impl = object.imp;
object.methods.push(method);
Ok(())
@@ -825,10 +917,6 @@ impl ComponentInterface {
Ok(())
}
- pub(super) fn note_name_used_as_error(&mut self, name: &str) {
- self.errors.insert(name.to_string());
- }
-
pub fn is_name_used_as_error(&self, name: &str) -> bool {
self.errors.contains(name)
}
@@ -856,6 +944,9 @@ impl ComponentInterface {
self.callback_interface_throws_types.insert(error.clone());
}
self.types.add_known_types(method.iter_types())?;
+ method
+ .throws_name()
+ .map(|n| self.errors.insert(n.to_string()));
cbi.methods.push(method);
} else {
self.add_method_meta(meta)?;
@@ -880,31 +971,6 @@ impl ComponentInterface {
bail!("Conflicting type definition for \"{}\"", f.name());
}
}
-
- for ty in self.iter_types() {
- match ty {
- Type::Object { name, .. } => {
- ensure!(
- self.objects.iter().any(|o| o.name == *name),
- "Object `{name}` has no definition"
- );
- }
- Type::Record { name, .. } => {
- ensure!(
- self.records.contains_key(name),
- "Record `{name}` has no definition",
- );
- }
- Type::Enum { name, .. } => {
- ensure!(
- self.enums.contains_key(name),
- "Enum `{name}` has no definition",
- );
- }
- _ => {}
- }
- }
-
Ok(())
}
@@ -1047,7 +1113,7 @@ fn throws_name(throws: &Option<Type>) -> Option<&str> {
// Type has no `name()` method, just `canonical_name()` which isn't what we want.
match throws {
None => None,
- Some(Type::Enum { name, .. }) => Some(name),
+ Some(Type::Enum { name, .. }) | Some(Type::Object { name, .. }) => Some(name),
_ => panic!("unknown throw type: {throws:?}"),
}
}
@@ -1089,35 +1155,50 @@ mod test {
let err = ComponentInterface::from_webidl(UDL2, "crate_name").unwrap_err();
assert_eq!(
err.to_string(),
- "Mismatching definition for enum `Testing`!\nexisting definition: Enum {
+ "Mismatching definition for enum `Testing`!
+existing definition: Enum {
name: \"Testing\",
module_path: \"crate_name\",
+ discr_type: None,
variants: [
Variant {
name: \"one\",
+ discr: None,
fields: [],
+ docstring: None,
},
Variant {
name: \"two\",
+ discr: None,
fields: [],
+ docstring: None,
},
],
flat: true,
+ non_exhaustive: false,
+ docstring: None,
},
new definition: Enum {
name: \"Testing\",
module_path: \"crate_name\",
+ discr_type: None,
variants: [
Variant {
name: \"three\",
+ discr: None,
fields: [],
+ docstring: None,
},
Variant {
name: \"four\",
+ discr: None,
fields: [],
+ docstring: None,
},
],
flat: true,
+ non_exhaustive: false,
+ docstring: None,
}",
);
@@ -1231,4 +1312,25 @@ new definition: Enum {
imp: ObjectImpl::Struct,
}));
}
+
+ #[test]
+ fn test_docstring_namespace() {
+ const UDL: &str = r#"
+ /// informative docstring
+ namespace test{};
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.namespace_docstring().unwrap(), "informative docstring");
+ }
+
+ #[test]
+ fn test_multiline_docstring() {
+ const UDL: &str = r#"
+ /// informative
+ /// docstring
+ namespace test{};
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.namespace_docstring().unwrap(), "informative\ndocstring");
+ }
}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/object.rs b/third_party/rust/uniffi_bindgen/src/interface/object.rs
index 942032b3c6..2b86e54a45 100644
--- a/third_party/rust/uniffi_bindgen/src/interface/object.rs
+++ b/third_party/rust/uniffi_bindgen/src/interface/object.rs
@@ -57,12 +57,11 @@
//! # Ok::<(), anyhow::Error>(())
//! ```
-use std::iter;
-
use anyhow::Result;
use uniffi_meta::Checksum;
-use super::ffi::{FfiArgument, FfiFunction, FfiType};
+use super::callbacks;
+use super::ffi::{FfiArgument, FfiCallbackFunction, FfiFunction, FfiStruct, FfiType};
use super::function::{Argument, Callable};
use super::{AsType, ObjectImpl, Type, TypeIterator};
@@ -92,14 +91,24 @@ pub struct Object {
// a regular method (albeit with a generated name)
// XXX - this should really be a HashSet, but not enough transient types support hash to make it worthwhile now.
pub(super) uniffi_traits: Vec<UniffiTrait>,
- // We don't include the FfiFunc in the hash calculation, because:
+ // We don't include the FfiFuncs in the hash calculation, because:
// - it is entirely determined by the other fields,
// so excluding it is safe.
// - its `name` property includes a checksum derived from the very
// hash value we're trying to calculate here, so excluding it
// avoids a weird circular dependency in the calculation.
+
+ // FFI function to clone a pointer for this object
+ #[checksum_ignore]
+ pub(super) ffi_func_clone: FfiFunction,
+ // FFI function to free a pointer for this object
#[checksum_ignore]
pub(super) ffi_func_free: FfiFunction,
+ // Ffi function to initialize the foreign callback for trait interfaces
+ #[checksum_ignore]
+ pub(super) ffi_init_callback: Option<FfiFunction>,
+ #[checksum_ignore]
+ pub(super) docstring: Option<String>,
}
impl Object {
@@ -118,6 +127,18 @@ impl Object {
&self.imp
}
+ pub fn is_trait_interface(&self) -> bool {
+ self.imp.is_trait_interface()
+ }
+
+ pub fn has_callback_interface(&self) -> bool {
+ self.imp.has_callback_interface()
+ }
+
+ pub fn has_async_method(&self) -> bool {
+ self.methods.iter().any(Method::is_async)
+ }
+
pub fn constructors(&self) -> Vec<&Constructor> {
self.constructors.iter().collect()
}
@@ -151,12 +172,28 @@ impl Object {
self.uniffi_traits.iter().collect()
}
+ pub fn ffi_object_clone(&self) -> &FfiFunction {
+ &self.ffi_func_clone
+ }
+
pub fn ffi_object_free(&self) -> &FfiFunction {
&self.ffi_func_free
}
+ pub fn ffi_init_callback(&self) -> &FfiFunction {
+ self.ffi_init_callback
+ .as_ref()
+ .unwrap_or_else(|| panic!("No ffi_init_callback set for {}", &self.name))
+ }
+
+ pub fn docstring(&self) -> Option<&str> {
+ self.docstring.as_deref()
+ }
+
pub fn iter_ffi_function_definitions(&self) -> impl Iterator<Item = &FfiFunction> {
- iter::once(&self.ffi_func_free)
+ [&self.ffi_func_clone, &self.ffi_func_free]
+ .into_iter()
+ .chain(&self.ffi_init_callback)
.chain(self.constructors.iter().map(|f| &f.ffi_func))
.chain(self.methods.iter().map(|f| &f.ffi_func))
.chain(
@@ -173,13 +210,26 @@ impl Object {
}
pub fn derive_ffi_funcs(&mut self) -> Result<()> {
+ assert!(!self.ffi_func_clone.name().is_empty());
assert!(!self.ffi_func_free.name().is_empty());
+ self.ffi_func_clone.arguments = vec![FfiArgument {
+ name: "ptr".to_string(),
+ type_: FfiType::RustArcPtr(self.name.to_string()),
+ }];
+ self.ffi_func_clone.return_type = Some(FfiType::RustArcPtr(self.name.to_string()));
self.ffi_func_free.arguments = vec![FfiArgument {
name: "ptr".to_string(),
type_: FfiType::RustArcPtr(self.name.to_string()),
}];
self.ffi_func_free.return_type = None;
self.ffi_func_free.is_object_free_function = true;
+ if self.has_callback_interface() {
+ self.ffi_init_callback = Some(FfiFunction::callback_init(
+ &self.module_path,
+ &self.name,
+ callbacks::vtable_name(&self.name),
+ ));
+ }
for cons in self.constructors.iter_mut() {
cons.derive_ffi_func();
@@ -194,6 +244,41 @@ impl Object {
Ok(())
}
+ /// For trait interfaces, FfiCallbacks to define for our methods, otherwise an empty vec.
+ pub fn ffi_callbacks(&self) -> Vec<FfiCallbackFunction> {
+ if self.is_trait_interface() {
+ callbacks::ffi_callbacks(&self.name, &self.methods)
+ } else {
+ vec![]
+ }
+ }
+
+ /// For trait interfaces, the VTable FFI type
+ pub fn vtable(&self) -> Option<FfiType> {
+ self.is_trait_interface()
+ .then(|| FfiType::Struct(callbacks::vtable_name(&self.name)))
+ }
+
+ /// For trait interfaces, the VTable struct to define. Otherwise None.
+ pub fn vtable_definition(&self) -> Option<FfiStruct> {
+ self.is_trait_interface()
+ .then(|| callbacks::vtable_struct(&self.name, &self.methods))
+ }
+
+ /// Vec of (ffi_callback_name, method) pairs
+ pub fn vtable_methods(&self) -> Vec<(FfiCallbackFunction, &Method)> {
+ self.methods
+ .iter()
+ .enumerate()
+ .map(|(i, method)| {
+ (
+ callbacks::method_ffi_callback(&self.name, method, i),
+ method,
+ )
+ })
+ .collect()
+ }
+
pub fn iter_types(&self) -> TypeIterator<'_> {
Box::new(
self.methods
@@ -218,6 +303,7 @@ impl AsType for Object {
impl From<uniffi_meta::ObjectMetadata> for Object {
fn from(meta: uniffi_meta::ObjectMetadata) -> Self {
+ let ffi_clone_name = meta.clone_ffi_symbol_name();
let ffi_free_name = meta.free_ffi_symbol_name();
Object {
module_path: meta.module_path,
@@ -226,10 +312,16 @@ impl From<uniffi_meta::ObjectMetadata> for Object {
constructors: Default::default(),
methods: Default::default(),
uniffi_traits: Default::default(),
+ ffi_func_clone: FfiFunction {
+ name: ffi_clone_name,
+ ..Default::default()
+ },
ffi_func_free: FfiFunction {
name: ffi_free_name,
..Default::default()
},
+ ffi_init_callback: None,
+ docstring: meta.docstring.clone(),
}
}
}
@@ -263,6 +355,7 @@ pub struct Constructor {
pub(super) name: String,
pub(super) object_name: String,
pub(super) object_module_path: String,
+ pub(super) is_async: bool,
pub(super) arguments: Vec<Argument>,
// We don't include the FFIFunc in the hash calculation, because:
// - it is entirely determined by the other fields,
@@ -272,6 +365,8 @@ pub struct Constructor {
// avoids a weird circular dependency in the calculation.
#[checksum_ignore]
pub(super) ffi_func: FfiFunction,
+ #[checksum_ignore]
+ pub(super) docstring: Option<String>,
pub(super) throws: Option<Type>,
pub(super) checksum_fn_name: String,
// Force a checksum value, or we'll fallback to the trait.
@@ -316,14 +411,20 @@ impl Constructor {
self.throws.as_ref()
}
+ pub fn docstring(&self) -> Option<&str> {
+ self.docstring.as_deref()
+ }
+
pub fn is_primary_constructor(&self) -> bool {
self.name == "new"
}
fn derive_ffi_func(&mut self) {
assert!(!self.ffi_func.name().is_empty());
- self.ffi_func.arguments = self.arguments.iter().map(Into::into).collect();
- self.ffi_func.return_type = Some(FfiType::RustArcPtr(self.object_name.clone()));
+ self.ffi_func.init(
+ Some(FfiType::RustArcPtr(self.object_name.clone())),
+ self.arguments.iter().map(Into::into),
+ );
}
pub fn iter_types(&self) -> TypeIterator<'_> {
@@ -339,14 +440,17 @@ impl From<uniffi_meta::ConstructorMetadata> for Constructor {
let ffi_func = FfiFunction {
name: ffi_name,
+ is_async: meta.is_async,
..FfiFunction::default()
};
Self {
name: meta.name,
object_name: meta.self_name,
+ is_async: meta.is_async,
object_module_path: meta.module_path,
arguments,
ffi_func,
+ docstring: meta.docstring.clone(),
throws: meta.throws.map(Into::into),
checksum_fn_name,
checksum: meta.checksum,
@@ -375,6 +479,8 @@ pub struct Method {
// avoids a weird circular dependency in the calculation.
#[checksum_ignore]
pub(super) ffi_func: FfiFunction,
+ #[checksum_ignore]
+ pub(super) docstring: Option<String>,
pub(super) throws: Option<Type>,
pub(super) takes_self_by_arc: bool,
pub(super) checksum_fn_name: String,
@@ -445,6 +551,10 @@ impl Method {
self.throws.as_ref()
}
+ pub fn docstring(&self) -> Option<&str> {
+ self.docstring.as_deref()
+ }
+
pub fn takes_self_by_arc(&self) -> bool {
self.takes_self_by_arc
}
@@ -466,6 +576,11 @@ impl Method {
.chain(self.return_type.iter().flat_map(Type::iter_types)),
)
}
+
+ /// For async callback interface methods, the FFI struct to pass to the completion function.
+ pub fn foreign_future_ffi_result_struct(&self) -> FfiStruct {
+ callbacks::foreign_future_ffi_result_struct(self.return_type.as_ref().map(FfiType::from))
+ }
}
impl From<uniffi_meta::MethodMetadata> for Method {
@@ -491,6 +606,7 @@ impl From<uniffi_meta::MethodMetadata> for Method {
arguments,
return_type,
ffi_func,
+ docstring: meta.docstring.clone(),
throws: meta.throws.map(Into::into),
takes_self_by_arc: meta.takes_self_by_arc,
checksum_fn_name,
@@ -503,19 +619,22 @@ impl From<uniffi_meta::TraitMethodMetadata> for Method {
fn from(meta: uniffi_meta::TraitMethodMetadata) -> Self {
let ffi_name = meta.ffi_symbol_name();
let checksum_fn_name = meta.checksum_symbol_name();
+ let is_async = meta.is_async;
let return_type = meta.return_type.map(Into::into);
let arguments = meta.inputs.into_iter().map(Into::into).collect();
let ffi_func = FfiFunction {
name: ffi_name,
+ is_async,
..FfiFunction::default()
};
Self {
name: meta.name,
object_name: meta.trait_name,
object_module_path: meta.module_path,
- is_async: false,
+ is_async,
arguments,
return_type,
+ docstring: meta.docstring.clone(),
throws: meta.throws.map(Into::into),
takes_self_by_arc: meta.takes_self_by_arc,
checksum_fn_name,
@@ -583,7 +702,7 @@ impl Callable for Constructor {
}
fn is_async(&self) -> bool {
- false
+ self.is_async
}
}
@@ -603,6 +722,10 @@ impl Callable for Method {
fn is_async(&self) -> bool {
self.is_async
}
+
+ fn takes_self(&self) -> bool {
+ true
+ }
}
#[cfg(test)]
@@ -770,4 +893,62 @@ mod test {
"Trait interfaces can not have constructors: \"new\""
);
}
+
+ #[test]
+ fn test_docstring_object() {
+ const UDL: &str = r#"
+ namespace test{};
+ /// informative docstring
+ interface Testing { };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(
+ ci.get_object_definition("Testing")
+ .unwrap()
+ .docstring()
+ .unwrap(),
+ "informative docstring"
+ );
+ }
+
+ #[test]
+ fn test_docstring_constructor() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface Testing {
+ /// informative docstring
+ constructor();
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(
+ ci.get_object_definition("Testing")
+ .unwrap()
+ .primary_constructor()
+ .unwrap()
+ .docstring()
+ .unwrap(),
+ "informative docstring"
+ );
+ }
+
+ #[test]
+ fn test_docstring_method() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface Testing {
+ /// informative docstring
+ void testing();
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(
+ ci.get_object_definition("Testing")
+ .unwrap()
+ .get_method("testing")
+ .docstring()
+ .unwrap(),
+ "informative docstring"
+ );
+ }
}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/record.rs b/third_party/rust/uniffi_bindgen/src/interface/record.rs
index 17d3774a49..e9a6004189 100644
--- a/third_party/rust/uniffi_bindgen/src/interface/record.rs
+++ b/third_party/rust/uniffi_bindgen/src/interface/record.rs
@@ -60,6 +60,8 @@ pub struct Record {
pub(super) name: String,
pub(super) module_path: String,
pub(super) fields: Vec<Field>,
+ #[checksum_ignore]
+ pub(super) docstring: Option<String>,
}
impl Record {
@@ -71,9 +73,17 @@ impl Record {
&self.fields
}
+ pub fn docstring(&self) -> Option<&str> {
+ self.docstring.as_deref()
+ }
+
pub fn iter_types(&self) -> TypeIterator<'_> {
Box::new(self.fields.iter().flat_map(Field::iter_types))
}
+
+ pub fn has_fields(&self) -> bool {
+ !self.fields.is_empty()
+ }
}
impl AsType for Record {
@@ -97,6 +107,7 @@ impl TryFrom<uniffi_meta::RecordMetadata> for Record {
.into_iter()
.map(TryInto::try_into)
.collect::<Result<_>>()?,
+ docstring: meta.docstring.clone(),
})
}
}
@@ -107,6 +118,8 @@ pub struct Field {
pub(super) name: String,
pub(super) type_: Type,
pub(super) default: Option<Literal>,
+ #[checksum_ignore]
+ pub(super) docstring: Option<String>,
}
impl Field {
@@ -118,6 +131,10 @@ impl Field {
self.default.as_ref()
}
+ pub fn docstring(&self) -> Option<&str> {
+ self.docstring.as_deref()
+ }
+
pub fn iter_types(&self) -> TypeIterator<'_> {
self.type_.iter_types()
}
@@ -140,6 +157,7 @@ impl TryFrom<uniffi_meta::FieldMetadata> for Field {
name,
type_,
default,
+ docstring: meta.docstring.clone(),
})
}
}
@@ -227,4 +245,39 @@ mod test {
.iter_types()
.any(|t| matches!(t, Type::Record { name, .. } if name == "Testing")));
}
+
+ #[test]
+ fn test_docstring_record() {
+ const UDL: &str = r#"
+ namespace test{};
+ /// informative docstring
+ dictionary Testing { };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(
+ ci.get_record_definition("Testing")
+ .unwrap()
+ .docstring()
+ .unwrap(),
+ "informative docstring"
+ );
+ }
+
+ #[test]
+ fn test_docstring_record_field() {
+ const UDL: &str = r#"
+ namespace test{};
+ dictionary Testing {
+ /// informative docstring
+ i32 testing;
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(
+ ci.get_record_definition("Testing").unwrap().fields()[0]
+ .docstring()
+ .unwrap(),
+ "informative docstring"
+ );
+ }
}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/universe.rs b/third_party/rust/uniffi_bindgen/src/interface/universe.rs
index e69d86e44f..70bc61f8a9 100644
--- a/third_party/rust/uniffi_bindgen/src/interface/universe.rs
+++ b/third_party/rust/uniffi_bindgen/src/interface/universe.rs
@@ -25,6 +25,7 @@ pub use uniffi_meta::{AsType, ExternalKind, NamespaceMetadata, ObjectImpl, Type,
pub(crate) struct TypeUniverse {
/// The unique prefixes that we'll use for namespacing when exposing this component's API.
pub namespace: NamespaceMetadata,
+ pub namespace_docstring: Option<String>,
// Named type definitions (including aliases).
type_definitions: HashMap<String, Type>,
@@ -83,9 +84,6 @@ impl TypeUniverse {
Type::Bytes => self.add_type_definition("bytes", type_)?,
Type::Timestamp => self.add_type_definition("timestamp", type_)?,
Type::Duration => self.add_type_definition("duration", type_)?,
- Type::ForeignExecutor => {
- self.add_type_definition("ForeignExecutor", type_)?;
- }
Type::Object { name, .. }
| Type::Record { name, .. }
| Type::Enum { name, .. }
@@ -118,6 +116,7 @@ impl TypeUniverse {
Ok(())
}
+ #[cfg(test)]
/// Check if a [Type] is present
pub fn contains(&self, type_: &Type) -> bool {
self.all_known_types.contains(type_)
diff --git a/third_party/rust/uniffi_bindgen/src/lib.rs b/third_party/rust/uniffi_bindgen/src/lib.rs
index 019b24022f..dfc90b32a6 100644
--- a/third_party/rust/uniffi_bindgen/src/lib.rs
+++ b/third_party/rust/uniffi_bindgen/src/lib.rs
@@ -58,9 +58,8 @@
//!
//! ### 3) Generate and include component scaffolding from the UDL file
//!
-//! First you will need to install `uniffi-bindgen` on your system using `cargo install uniffi_bindgen`.
-//! Then add to your crate `uniffi_build` under `[build-dependencies]`.
-//! Finally, add a `build.rs` script to your crate and have it call `uniffi_build::generate_scaffolding`
+//! Add to your crate `uniffi_build` under `[build-dependencies]`,
+//! then add a `build.rs` script to your crate and have it call `uniffi_build::generate_scaffolding`
//! to process your `.udl` file. This will generate some Rust code to be included in the top-level source
//! code of your crate. If your UDL file is named `example.udl`, then your build script would call:
//!
@@ -77,12 +76,13 @@
//!
//! ### 4) Generate foreign language bindings for the library
//!
-//! The `uniffi-bindgen` utility provides a command-line tool that can produce code to
+//! You will need ensure a local `uniffi-bindgen` - see <https://mozilla.github.io/uniffi-rs/tutorial/foreign_language_bindings.html>
+//! This utility provides a command-line tool that can produce code to
//! consume the Rust library in any of several supported languages.
//! It is done by calling (in kotlin for example):
//!
//! ```text
-//! uniffi-bindgen --language kotlin ./src/example.udl
+//! cargo run --bin -p uniffi-bindgen --language kotlin ./src/example.udl
//! ```
//!
//! This will produce a file `example.kt` in the same directory as the .udl file, containing kotlin bindings
@@ -160,15 +160,16 @@ pub trait BindingGenerator: Sized {
ci: &ComponentInterface,
config: &Self::Config,
out_dir: &Utf8Path,
+ try_format_code: bool,
) -> Result<()>;
/// Check if `library_path` used by library mode is valid for this generator
fn check_library_path(&self, library_path: &Utf8Path, cdylib_name: Option<&str>) -> Result<()>;
}
-struct BindingGeneratorDefault {
- target_languages: Vec<TargetLanguage>,
- try_format_code: bool,
+pub struct BindingGeneratorDefault {
+ pub target_languages: Vec<TargetLanguage>,
+ pub try_format_code: bool,
}
impl BindingGenerator for BindingGeneratorDefault {
@@ -179,6 +180,7 @@ impl BindingGenerator for BindingGeneratorDefault {
ci: &ComponentInterface,
config: &Self::Config,
out_dir: &Utf8Path,
+ _try_format_code: bool,
) -> Result<()> {
for &language in &self.target_languages {
bindings::write_bindings(
@@ -219,12 +221,13 @@ impl BindingGenerator for BindingGeneratorDefault {
/// - `library_file`: The path to a dynamic library to attempt to extract the definitions from and extend the component interface with. No extensions to component interface occur if it's [`None`]
/// - `crate_name`: Override the default crate name that is guessed from UDL file path.
pub fn generate_external_bindings<T: BindingGenerator>(
- binding_generator: T,
+ binding_generator: &T,
udl_file: impl AsRef<Utf8Path>,
config_file_override: Option<impl AsRef<Utf8Path>>,
out_dir_override: Option<impl AsRef<Utf8Path>>,
library_file: Option<impl AsRef<Utf8Path>>,
crate_name: Option<&str>,
+ try_format_code: bool,
) -> Result<()> {
let crate_name = crate_name
.map(|c| Ok(c.to_string()))
@@ -253,7 +256,7 @@ pub fn generate_external_bindings<T: BindingGenerator>(
udl_file.as_ref(),
out_dir_override.as_ref().map(|p| p.as_ref()),
)?;
- binding_generator.write_bindings(&component, &config, &out_dir)
+ binding_generator.write_bindings(&component, &config, &out_dir, try_format_code)
}
// Generate the infrastructural Rust code for implementing the UDL interface,
@@ -301,25 +304,23 @@ fn generate_component_scaffolding_inner(
// Generate the bindings in the target languages that call the scaffolding
// Rust code.
-pub fn generate_bindings(
+pub fn generate_bindings<T: BindingGenerator>(
udl_file: &Utf8Path,
config_file_override: Option<&Utf8Path>,
- target_languages: Vec<TargetLanguage>,
+ binding_generator: T,
out_dir_override: Option<&Utf8Path>,
library_file: Option<&Utf8Path>,
crate_name: Option<&str>,
try_format_code: bool,
) -> Result<()> {
generate_external_bindings(
- BindingGeneratorDefault {
- target_languages,
- try_format_code,
- },
+ &binding_generator,
udl_file,
config_file_override,
out_dir_override,
library_file,
crate_name,
+ try_format_code,
)
}
@@ -417,22 +418,53 @@ fn format_code_with_rustfmt(path: &Utf8Path) -> Result<()> {
Ok(())
}
+/// Load TOML from file if the file exists.
+fn load_toml_file(source: Option<&Utf8Path>) -> Result<Option<toml::value::Table>> {
+ if let Some(source) = source {
+ if source.exists() {
+ let contents =
+ fs::read_to_string(source).with_context(|| format!("read file: {:?}", source))?;
+ return Ok(Some(
+ toml::de::from_str(&contents)
+ .with_context(|| format!("parse toml: {:?}", source))?,
+ ));
+ }
+ }
+
+ Ok(None)
+}
+
+/// Load the default `uniffi.toml` config, merge TOML trees with `config_file_override` if specified.
fn load_initial_config<Config: DeserializeOwned>(
crate_root: &Utf8Path,
config_file_override: Option<&Utf8Path>,
) -> Result<Config> {
- let path = match config_file_override {
- Some(cfg) => Some(cfg.to_owned()),
- None => crate_root.join("uniffi.toml").canonicalize_utf8().ok(),
- };
- let toml_config = match path {
- Some(path) => {
- let contents = fs::read_to_string(path).context("Failed to read config file")?;
- toml::de::from_str(&contents)?
+ let mut config = load_toml_file(Some(crate_root.join("uniffi.toml").as_path()))
+ .context("default config")?
+ .unwrap_or(toml::value::Table::default());
+
+ let override_config = load_toml_file(config_file_override).context("override config")?;
+ if let Some(override_config) = override_config {
+ merge_toml(&mut config, override_config);
+ }
+
+ Ok(toml::Value::from(config).try_into()?)
+}
+
+fn merge_toml(a: &mut toml::value::Table, b: toml::value::Table) {
+ for (key, value) in b.into_iter() {
+ match a.get_mut(&key) {
+ Some(existing_value) => match (existing_value, value) {
+ (toml::Value::Table(ref mut t0), toml::Value::Table(t1)) => {
+ merge_toml(t0, t1);
+ }
+ (v, value) => *v = value,
+ },
+ None => {
+ a.insert(key, value);
+ }
}
- None => toml::Value::from(toml::value::Table::default()),
- };
- Ok(toml_config.try_into()?)
+ }
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
@@ -516,4 +548,56 @@ mod test {
let not_a_crate_root = &this_crate_root.join("src/templates");
assert!(guess_crate_root(&not_a_crate_root.join("src/example.udl")).is_err());
}
+
+ #[test]
+ fn test_merge_toml() {
+ let default = r#"
+ foo = "foo"
+ bar = "bar"
+
+ [table1]
+ foo = "foo"
+ bar = "bar"
+ "#;
+ let mut default = toml::de::from_str(default).unwrap();
+
+ let override_toml = r#"
+ # update key
+ bar = "BAR"
+ # insert new key
+ baz = "BAZ"
+
+ [table1]
+ # update key
+ bar = "BAR"
+ # insert new key
+ baz = "BAZ"
+
+ # new table
+ [table1.table2]
+ bar = "BAR"
+ baz = "BAZ"
+ "#;
+ let override_toml = toml::de::from_str(override_toml).unwrap();
+
+ let expected = r#"
+ foo = "foo"
+ bar = "BAR"
+ baz = "BAZ"
+
+ [table1]
+ foo = "foo"
+ bar = "BAR"
+ baz = "BAZ"
+
+ [table1.table2]
+ bar = "BAR"
+ baz = "BAZ"
+ "#;
+ let expected: toml::value::Table = toml::de::from_str(expected).unwrap();
+
+ merge_toml(&mut default, override_toml);
+
+ assert_eq!(&expected, &default);
+ }
}
diff --git a/third_party/rust/uniffi_bindgen/src/library_mode.rs b/third_party/rust/uniffi_bindgen/src/library_mode.rs
index f170ea5e91..c460c03d9f 100644
--- a/third_party/rust/uniffi_bindgen/src/library_mode.rs
+++ b/third_party/rust/uniffi_bindgen/src/library_mode.rs
@@ -16,8 +16,8 @@
/// - UniFFI can figure out the package/module names for each crate, eliminating the external
/// package maps.
use crate::{
- bindings::TargetLanguage, load_initial_config, macro_metadata, BindingGenerator,
- BindingGeneratorDefault, BindingsConfig, ComponentInterface, Result,
+ load_initial_config, macro_metadata, BindingGenerator, BindingsConfig, ComponentInterface,
+ Result,
};
use anyhow::{bail, Context};
use camino::Utf8Path;
@@ -33,21 +33,21 @@ use uniffi_meta::{
/// Generate foreign bindings
///
/// Returns the list of sources used to generate the bindings, in no particular order.
-pub fn generate_bindings(
+pub fn generate_bindings<T: BindingGenerator + ?Sized>(
library_path: &Utf8Path,
crate_name: Option<String>,
- target_languages: &[TargetLanguage],
+ binding_generator: &T,
+ config_file_override: Option<&Utf8Path>,
out_dir: &Utf8Path,
try_format_code: bool,
-) -> Result<Vec<Source<crate::Config>>> {
+) -> Result<Vec<Source<T::Config>>> {
generate_external_bindings(
- BindingGeneratorDefault {
- target_languages: target_languages.into(),
- try_format_code,
- },
+ binding_generator,
library_path,
- crate_name,
+ crate_name.clone(),
+ config_file_override,
out_dir,
+ try_format_code,
)
}
@@ -55,10 +55,12 @@ pub fn generate_bindings(
///
/// Returns the list of sources used to generate the bindings, in no particular order.
pub fn generate_external_bindings<T: BindingGenerator>(
- binding_generator: T,
+ binding_generator: &T,
library_path: &Utf8Path,
crate_name: Option<String>,
+ config_file_override: Option<&Utf8Path>,
out_dir: &Utf8Path,
+ try_format_code: bool,
) -> Result<Vec<Source<T::Config>>> {
let cargo_metadata = MetadataCommand::new()
.exec()
@@ -66,7 +68,12 @@ pub fn generate_external_bindings<T: BindingGenerator>(
let cdylib_name = calc_cdylib_name(library_path);
binding_generator.check_library_path(library_path, cdylib_name)?;
- let mut sources = find_sources(&cargo_metadata, library_path, cdylib_name)?;
+ let mut sources = find_sources(
+ &cargo_metadata,
+ library_path,
+ cdylib_name,
+ config_file_override,
+ )?;
for i in 0..sources.len() {
// Partition up the sources list because we're eventually going to call
// `update_from_dependency_configs()` which requires an exclusive reference to one source and
@@ -101,7 +108,7 @@ pub fn generate_external_bindings<T: BindingGenerator>(
}
for source in sources.iter() {
- binding_generator.write_bindings(&source.ci, &source.config, out_dir)?;
+ binding_generator.write_bindings(&source.ci, &source.config, out_dir, try_format_code)?;
}
Ok(sources)
@@ -118,10 +125,10 @@ pub struct Source<Config: BindingsConfig> {
// If `library_path` is a C dynamic library, return its name
pub fn calc_cdylib_name(library_path: &Utf8Path) -> Option<&str> {
- let cdylib_extentions = [".so", ".dll", ".dylib"];
+ let cdylib_extensions = [".so", ".dll", ".dylib"];
let filename = library_path.file_name()?;
let filename = filename.strip_prefix("lib").unwrap_or(filename);
- for ext in cdylib_extentions {
+ for ext in cdylib_extensions {
if let Some(f) = filename.strip_suffix(ext) {
return Some(f);
}
@@ -133,6 +140,7 @@ fn find_sources<Config: BindingsConfig>(
cargo_metadata: &cargo_metadata::Metadata,
library_path: &Utf8Path,
cdylib_name: Option<&str>,
+ config_file_override: Option<&Utf8Path>,
) -> Result<Vec<Source<Config>>> {
let items = macro_metadata::extract_from_library(library_path)?;
let mut metadata_groups = create_metadata_groups(&items);
@@ -178,7 +186,7 @@ fn find_sources<Config: BindingsConfig>(
ci.add_metadata(metadata)?;
};
ci.add_metadata(group)?;
- let mut config = load_initial_config::<Config>(crate_root, None)?;
+ let mut config = load_initial_config::<Config>(crate_root, config_file_override)?;
if let Some(cdylib_name) = cdylib_name {
config.update_from_cdylib_name(cdylib_name);
}
diff --git a/third_party/rust/uniffi_bindgen/src/macro_metadata/ci.rs b/third_party/rust/uniffi_bindgen/src/macro_metadata/ci.rs
index 7ce6c3a70b..69fad1980e 100644
--- a/third_party/rust/uniffi_bindgen/src/macro_metadata/ci.rs
+++ b/third_party/rust/uniffi_bindgen/src/macro_metadata/ci.rs
@@ -4,9 +4,7 @@
use crate::interface::{CallbackInterface, ComponentInterface, Enum, Record, Type};
use anyhow::{bail, Context};
-use uniffi_meta::{
- create_metadata_groups, group_metadata, EnumMetadata, ErrorMetadata, Metadata, MetadataGroup,
-};
+use uniffi_meta::{create_metadata_groups, group_metadata, EnumMetadata, Metadata, MetadataGroup};
/// Add Metadata items to the ComponentInterface
///
@@ -98,7 +96,9 @@ fn add_item_to_ci(iface: &mut ComponentInterface, item: Metadata) -> anyhow::Res
iface.add_record_definition(record)?;
}
Metadata::Enum(meta) => {
- let flat = meta.variants.iter().all(|v| v.fields.is_empty());
+ let flat = meta
+ .forced_flatness
+ .unwrap_or_else(|| meta.variants.iter().all(|v| v.fields.is_empty()));
add_enum_to_ci(iface, meta, flat)?;
}
Metadata::Object(meta) => {
@@ -117,22 +117,11 @@ fn add_item_to_ci(iface: &mut ComponentInterface, item: Metadata) -> anyhow::Res
module_path: meta.module_path.clone(),
name: meta.name.clone(),
})?;
- iface.add_callback_interface_definition(CallbackInterface::new(
- meta.name,
- meta.module_path,
- ));
+ iface.add_callback_interface_definition(CallbackInterface::try_from(meta)?);
}
Metadata::TraitMethod(meta) => {
iface.add_trait_method_meta(meta)?;
}
- Metadata::Error(meta) => {
- iface.note_name_used_as_error(meta.name());
- match meta {
- ErrorMetadata::Enum { enum_, is_flat } => {
- add_enum_to_ci(iface, enum_, is_flat)?;
- }
- };
- }
Metadata::CustomType(meta) => {
iface.types.add_known_type(&Type::Custom {
module_path: meta.module_path.clone(),
diff --git a/third_party/rust/uniffi_bindgen/src/macro_metadata/extract.rs b/third_party/rust/uniffi_bindgen/src/macro_metadata/extract.rs
index 25b5ef17ba..6d440919f1 100644
--- a/third_party/rust/uniffi_bindgen/src/macro_metadata/extract.rs
+++ b/third_party/rust/uniffi_bindgen/src/macro_metadata/extract.rs
@@ -30,7 +30,7 @@ fn extract_from_bytes(file_data: &[u8]) -> anyhow::Result<Vec<Metadata>> {
Object::PE(pe) => extract_from_pe(pe, file_data),
Object::Mach(mach) => extract_from_mach(mach, file_data),
Object::Archive(archive) => extract_from_archive(archive, file_data),
- Object::Unknown(_) => bail!("Unknown library format"),
+ _ => bail!("Unknown library format"),
}
}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/mod.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/mod.rs
index f3759cf6fa..7fd81831aa 100644
--- a/third_party/rust/uniffi_bindgen/src/scaffolding/mod.rs
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/mod.rs
@@ -45,7 +45,6 @@ mod filters {
format!("std::sync::Arc<{}>", imp.rust_name_for(name))
}
Type::CallbackInterface { name, .. } => format!("Box<dyn r#{name}>"),
- Type::ForeignExecutor => "::uniffi::ForeignExecutor".into(),
Type::Optional { inner_type } => {
format!("std::option::Option<{}>", type_rs(inner_type)?)
}
@@ -64,41 +63,12 @@ mod filters {
kind: ExternalKind::Interface,
..
} => format!("::std::sync::Arc<r#{name}>"),
- Type::External { name, .. } => format!("r#{name}"),
- })
- }
-
- // Map a type to Rust code that specifies the FfiConverter implementation.
- //
- // This outputs something like `<MyStruct as Lift<crate::UniFfiTag>>`
- pub fn ffi_trait(type_: &Type, trait_name: &str) -> Result<String, askama::Error> {
- Ok(match type_ {
Type::External {
name,
- kind: ExternalKind::Interface,
+ kind: ExternalKind::Trait,
..
- } => {
- format!("<::std::sync::Arc<r#{name}> as ::uniffi::{trait_name}<crate::UniFfiTag>>")
- }
- _ => format!(
- "<{} as ::uniffi::{trait_name}<crate::UniFfiTag>>",
- type_rs(type_)?
- ),
- })
- }
-
- pub fn return_type<T: Callable>(callable: &T) -> Result<String, askama::Error> {
- let return_type = match callable.return_type() {
- Some(t) => type_rs(&t)?,
- None => "()".to_string(),
- };
- match callable.throws_type() {
- Some(t) => type_rs(&t)?,
- None => "()".to_string(),
- };
- Ok(match callable.throws_type() {
- Some(e) => format!("::std::result::Result<{return_type}, {}>", type_rs(&e)?),
- None => return_type,
+ } => format!("::std::sync::Arc<dyn r#{name}>"),
+ Type::External { name, .. } => format!("r#{name}"),
})
}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/CallbackInterfaceTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/CallbackInterfaceTemplate.rs
index 64c69e4d8e..658f4c8de5 100644
--- a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/CallbackInterfaceTemplate.rs
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/CallbackInterfaceTemplate.rs
@@ -1,82 +1,17 @@
-{#
-// For each Callback Interface definition, we assume that there is a corresponding trait defined in Rust client code.
-// If the UDL callback interface and Rust trait's methods don't match, the Rust compiler will complain.
-// We generate:
-// * an init function to accept that `ForeignCallback` from the foreign language, and stores it.
-// * a holder for a `ForeignCallback`, of type `uniffi::ForeignCallbackInternals`.
-// * a proxy `struct` which implements the `trait` that the Callback Interface corresponds to. This
-// is the object that client code interacts with.
-// - for each method, arguments will be packed into a `RustBuffer` and sent over the `ForeignCallback` to be
-// unpacked and called. The return value is packed into another `RustBuffer` and sent back to Rust.
-// - a `Drop` `impl`, which tells the foreign language to forget about the real callback object.
-#}
-{% let trait_name = cbi.name() -%}
-{% let trait_impl = format!("UniFFICallbackHandler{}", trait_name) %}
-{% let foreign_callback_internals = format!("foreign_callback_{}_internals", trait_name)|upper -%}
-
-// Register a foreign callback for getting across the FFI.
-#[doc(hidden)]
-static {{ foreign_callback_internals }}: uniffi::ForeignCallbackInternals = uniffi::ForeignCallbackInternals::new();
-
-#[doc(hidden)]
-#[no_mangle]
-pub extern "C" fn {{ cbi.ffi_init_callback().name() }}(callback: uniffi::ForeignCallback, _: &mut uniffi::RustCallStatus) {
- {{ foreign_callback_internals }}.set_callback(callback);
- // The call status should be initialized to CALL_SUCCESS, so no need to modify it.
-}
-
-// Make an implementation which will shell out to the foreign language.
-#[doc(hidden)]
-#[derive(Debug)]
-struct {{ trait_impl }} {
- handle: u64
-}
-
-impl {{ trait_impl }} {
- fn new(handle: u64) -> Self {
- Self { handle }
- }
-}
-
-impl Drop for {{ trait_impl }} {
- fn drop(&mut self) {
- {{ foreign_callback_internals }}.invoke_callback::<(), crate::UniFfiTag>(
- self.handle, uniffi::IDX_CALLBACK_FREE, Default::default()
- )
- }
-}
-
-uniffi::deps::static_assertions::assert_impl_all!({{ trait_impl }}: Send);
-
-impl r#{{ trait_name }} for {{ trait_impl }} {
+#[::uniffi::export_for_udl(callback_interface)]
+pub trait r#{{ cbi.name() }} {
{%- for meth in cbi.methods() %}
-
- {#- Method declaration #}
- fn r#{{ meth.name() -}}
- ({% call rs::arg_list_decl_with_prefix("&self", meth) %})
- {%- match (meth.return_type(), meth.throws_type()) %}
- {%- when (Some(return_type), None) %} -> {{ return_type.borrow()|type_rs }}
- {%- when (Some(return_type), Some(err)) %} -> ::std::result::Result<{{ return_type.borrow()|type_rs }}, {{ err|type_rs }}>
- {%- when (None, Some(err)) %} -> ::std::result::Result<(), {{ err|type_rs }}>
- {% else -%}
- {%- endmatch -%} {
- {#- Method body #}
-
- {#- Packing args into a RustBuffer #}
- {% if meth.arguments().len() == 0 -%}
- let args_buf = Vec::new();
- {% else -%}
- let mut args_buf = Vec::new();
- {% endif -%}
+ fn r#{{ meth.name() }}(
+ {% if meth.takes_self_by_arc()%}self: Arc<Self>{% else %}&self{% endif %},
{%- for arg in meth.arguments() %}
- {{ arg.as_type().borrow()|ffi_trait("Lower") }}::write(r#{{ arg.name() }}, &mut args_buf);
- {%- endfor -%}
- let args_rbuf = uniffi::RustBuffer::from_vec(args_buf);
-
- {#- Calling into foreign code. #}
- {{ foreign_callback_internals }}.invoke_callback::<{{ meth|return_type }}, crate::UniFfiTag>(self.handle, {{ loop.index }}, args_rbuf)
- }
- {%- endfor %}
+ r#{{ arg.name() }}: {% if arg.by_ref() %}&{% endif %}{{ arg.as_type().borrow()|type_rs }},
+ {%- endfor %}
+ )
+ {%- match (meth.return_type(), meth.throws_type()) %}
+ {%- when (Some(return_type), None) %} -> {{ return_type|type_rs }};
+ {%- when (Some(return_type), Some(error_type)) %} -> ::std::result::Result::<{{ return_type|type_rs }}, {{ error_type|type_rs }}>;
+ {%- when (None, Some(error_type)) %} -> ::std::result::Result::<(), {{ error_type|type_rs }}>;
+ {%- when (None, None) %};
+ {%- endmatch %}
+ {% endfor %}
}
-
-::uniffi::scaffolding_ffi_converter_callback_interface!(r#{{ trait_name }}, {{ trait_impl }});
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/EnumTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/EnumTemplate.rs
index 6b9f96f224..f918ef2f3a 100644
--- a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/EnumTemplate.rs
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/EnumTemplate.rs
@@ -1,13 +1,12 @@
{#
-// For each enum declared in the UDL, we assume the caller has provided a corresponding
-// rust `enum`. We provide the traits for sending it across the FFI, which will fail to
-// compile if the provided struct has a different shape to the one declared in the UDL.
-//
-// We define a unit-struct to implement the trait to sidestep Rust's orphan rule (ADR-0006). It's
-// public so other crates can refer to it via an `[External='crate'] typedef`
+// Forward work to `uniffi_macros` This keeps macro-based and UDL-based generated code consistent.
#}
-#[::uniffi::derive_enum_for_udl]
+#[::uniffi::derive_enum_for_udl(
+ {%- if e.is_non_exhaustive() -%}
+ non_exhaustive,
+ {%- endif %}
+)]
enum r#{{ e.name() }} {
{%- for variant in e.variants() %}
r#{{ variant.name() }} {
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ErrorTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ErrorTemplate.rs
index 94538ecaa8..64f48e2334 100644
--- a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ErrorTemplate.rs
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ErrorTemplate.rs
@@ -1,10 +1,5 @@
{#
-// For each error declared in the UDL, we assume the caller has provided a corresponding
-// rust `enum`. We provide the traits for sending it across the FFI, which will fail to
-// compile if the provided struct has a different shape to the one declared in the UDL.
-//
-// We define a unit-struct to implement the trait to sidestep Rust's orphan rule (ADR-0006). It's
-// public so other crates can refer to it via an `[External='crate'] typedef`
+// Forward work to `uniffi_macros` This keeps macro-based and UDL-based generated code consistent.
#}
#[::uniffi::derive_error_for_udl(
@@ -14,6 +9,9 @@
with_try_read,
{%- endif %}
{%- endif %}
+ {%- if e.is_non_exhaustive() -%}
+ non_exhaustive,
+ {%- endif %}
)]
enum r#{{ e.name() }} {
{%- for variant in e.variants() %}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ExternalTypesTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ExternalTypesTemplate.rs
index ade1578897..d67e172cc2 100644
--- a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ExternalTypesTemplate.rs
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ExternalTypesTemplate.rs
@@ -10,6 +10,8 @@
::uniffi::ffi_converter_forward!(r#{{ name }}, ::{{ crate_name|crate_name_rs }}::UniFfiTag, crate::UniFfiTag);
{%- when ExternalKind::Interface %}
::uniffi::ffi_converter_arc_forward!(r#{{ name }}, ::{{ crate_name|crate_name_rs }}::UniFfiTag, crate::UniFfiTag);
+{%- when ExternalKind::Trait %}
+::uniffi::ffi_converter_arc_forward!(dyn r#{{ name }}, ::{{ crate_name|crate_name_rs }}::UniFfiTag, crate::UniFfiTag);
{%- endmatch %}
{% endif %}
{%- endfor %}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ObjectTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ObjectTemplate.rs
index e2445c670d..e752878af5 100644
--- a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ObjectTemplate.rs
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ObjectTemplate.rs
@@ -1,24 +1,15 @@
-// For each Object definition, we assume the caller has provided an appropriately-shaped `struct T`
-// with an `impl` for each method on the object. We create an `Arc<T>` for "safely" handing out
-// references to these structs to foreign language code, and we provide a `pub extern "C"` function
-// corresponding to each method.
-//
-// (Note that "safely" is in "scare quotes" - that's because we use functions on an `Arc` that
-// that are inherently unsafe, but the code we generate is safe in practice.)
-//
-// If the caller's implementation of the struct does not match with the methods or types specified
-// in the UDL, then the rust compiler will complain with a (hopefully at least somewhat helpful!)
-// error message when processing this generated code.
+{#
+// Forward work to `uniffi_macros` This keeps macro-based and UDL-based generated code consistent.
+#}
-{%- match obj.imp() -%}
-{%- when ObjectImpl::Trait %}
-#[::uniffi::export_for_udl]
+{%- if obj.is_trait_interface() %}
+#[::uniffi::export_for_udl{% if obj.has_callback_interface() %}(with_foreign){% endif %}]
pub trait r#{{ obj.name() }} {
{%- for meth in obj.methods() %}
- fn {{ meth.name() }}(
+ {% if meth.is_async() %}async {% endif %}fn r#{{ meth.name() }}(
{% if meth.takes_self_by_arc()%}self: Arc<Self>{% else %}&self{% endif %},
{%- for arg in meth.arguments() %}
- {{ arg.name() }}: {% if arg.by_ref() %}&{% endif %}{{ arg.as_type().borrow()|type_rs }},
+ r#{{ arg.name() }}: {% if arg.by_ref() %}&{% endif %}{{ arg.as_type().borrow()|type_rs }},
{%- endfor %}
)
{%- match (meth.return_type(), meth.throws_type()) %}
@@ -29,7 +20,7 @@ pub trait r#{{ obj.name() }} {
{%- endmatch %}
{% endfor %}
}
-{% when ObjectImpl::Struct %}
+{%- else %}
{%- for tm in obj.uniffi_traits() %}
{% match tm %}
{% when UniffiTrait::Debug { fmt }%}
@@ -46,9 +37,10 @@ pub trait r#{{ obj.name() }} {
struct {{ obj.rust_name() }} { }
{%- for cons in obj.constructors() %}
-#[::uniffi::export_for_udl(constructor)]
+#[::uniffi::export_for_udl]
impl {{ obj.rust_name() }} {
- pub fn r#{{ cons.name() }}(
+ #[uniffi::constructor]
+ pub {% if cons.is_async() %}async {% endif %}fn r#{{ cons.name() }}(
{%- for arg in cons.arguments() %}
r#{{ arg.name() }}: {% if arg.by_ref() %}&{% endif %}{{ arg.as_type().borrow()|type_rs }},
{%- endfor %}
@@ -68,7 +60,7 @@ impl {{ obj.rust_name() }} {
{%- for meth in obj.methods() %}
#[::uniffi::export_for_udl]
impl {{ obj.rust_name() }} {
- pub fn r#{{ meth.name() }}(
+ pub {% if meth.is_async() %}async {% endif %}fn r#{{ meth.name() }}(
{% if meth.takes_self_by_arc()%}self: Arc<Self>{% else %}&self{% endif %},
{%- for arg in meth.arguments() %}
r#{{ arg.name() }}: {% if arg.by_ref() %}&{% endif %}{{ arg.as_type().borrow()|type_rs }},
@@ -86,4 +78,4 @@ impl {{ obj.rust_name() }} {
}
{%- endfor %}
-{% endmatch %}
+{% endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/RecordTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/RecordTemplate.rs
index 85e131dd8c..a7affdf7b8 100644
--- a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/RecordTemplate.rs
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/RecordTemplate.rs
@@ -1,11 +1,5 @@
{#
-// For each record declared in the UDL, we assume the caller has provided a corresponding
-// rust `struct` with the declared fields. We provide the traits for sending it across the FFI.
-// If the caller's struct does not match the shape and types declared in the UDL then the rust
-// compiler will complain with a type error.
-//
-// We define a unit-struct to implement the trait to sidestep Rust's orphan rule (ADR-0006). It's
-// public so other crates can refer to it via an `[External='crate'] typedef`
+// Forward work to `uniffi_macros` This keeps macro-based and UDL-based generated code consistent.
#}
#[::uniffi::derive_record_for_udl]
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/TopLevelFunctionTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/TopLevelFunctionTemplate.rs
index eeee0f5ee2..27f3686b9f 100644
--- a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/TopLevelFunctionTemplate.rs
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/TopLevelFunctionTemplate.rs
@@ -1,5 +1,8 @@
+{#
+// Forward work to `uniffi_macros` This keeps macro-based and UDL-based generated code consistent.
+#}
#[::uniffi::export_for_udl]
-pub fn r#{{ func.name() }}(
+pub {% if func.is_async() %}async {% endif %}fn r#{{ func.name() }}(
{%- for arg in func.arguments() %}
r#{{ arg.name() }}: {% if arg.by_ref() %}&{% endif %}{{ arg.as_type().borrow()|type_rs }},
{%- endfor %}