commit ff43d7dbf1ad026defccc22a88ac80385bb78aaa
parent 3db397d760756fdb45e7a23dbe6b9efc5c843636
Author: Andy Leiserson <aleiserson@mozilla.com>
Date: Mon, 15 Dec 2025 21:47:13 +0000
Bug 1984493 - Vendor rust crates `block2`, `objc2`, and `objc2-encode` r=webgpu-reviewers,supply-chain-reviewers,teoxoy
Differential Revision: https://phabricator.services.mozilla.com/D275321
Diffstat:
149 files changed, 40132 insertions(+), 0 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -572,6 +572,15 @@ dependencies = [
]
[[package]]
+name = "block2"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5"
+dependencies = [
+ "objc2",
+]
+
+[[package]]
name = "bookmark_sync"
version = "0.1.0"
dependencies = [
@@ -5200,6 +5209,21 @@ dependencies = [
]
[[package]]
+name = "objc2"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05"
+dependencies = [
+ "objc2-encode",
+]
+
+[[package]]
+name = "objc2-encode"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
+
+[[package]]
name = "object"
version = "0.36.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -8094,11 +8118,13 @@ dependencies = [
"arrayvec",
"ash",
"bincode",
+ "block2",
"io-surface",
"log",
"metal",
"nsstring",
"objc",
+ "objc2",
"parking_lot",
"serde",
"static_prefs",
diff --git a/gfx/wgpu_bindings/Cargo.toml b/gfx/wgpu_bindings/Cargo.toml
@@ -62,6 +62,12 @@ windows = { version = "0.62", default-features = false, features = [
objc = "0.2"
metal = "0.32"
io-surface = "0.15"
+# TODO(bug 1984493): The `block2` crate will be used by wgpu in the near
+# future. The direct dependency on it can be removed when wgpu is updated to a
+# version that uses it. The rest of these will be used directly by
+# `wgpu_bindings` at that point.
+block2 = "0.6.2"
+objc2 = "0.6.3"
[dependencies]
bincode = "1"
diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml
@@ -1166,6 +1166,11 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy"
delta = "0.10.2 -> 0.10.3"
+[[audits.block2]]
+who = "Andy Leiserson <aleiserson@mozilla.com>"
+criteria = "safe-to-deploy"
+version = "0.6.2"
+
[[audits.build-parallel]]
who = "Jeff Muizelaar <jmuizelaar@mozilla.com>"
criteria = "safe-to-deploy"
@@ -4541,6 +4546,16 @@ criteria = "safe-to-deploy"
delta = "0.2.7 -> 0.2.7@git:4de89f5aa9851ceca4d40e7ac1e2759410c04324"
importable = false
+[[audits.objc2]]
+who = "Andy Leiserson <aleiserson@mozilla.com>"
+criteria = "safe-to-deploy"
+version = "0.6.3"
+
+[[audits.objc2-encode]]
+who = "Andy Leiserson <aleiserson@mozilla.com>"
+criteria = "safe-to-deploy"
+version = "4.1.0"
+
[[audits.object]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy"
diff --git a/third_party/rust/block2/.cargo-checksum.json b/third_party/rust/block2/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"CHANGELOG.md":"36c27561beba905bb52eb4c942884ba51cf120f437861f2928408191e20835c6","Cargo.lock":"993576b7d3ca78ec0a4d7acfb67c0dea2fee6c50166e11e7e52ef75fbb40474c","Cargo.toml":"e325c51f01145af3e11f2ee5ce7c47f10697d936c1d90b9ee81a0cf4f700f6d7","README.md":"99073a83cfe6ee090546e3063f41251f7309fe2a017c4add8494e4ad7990d058","src/abi.rs":"a953849cf8a30c0f94e76408bb83533074d5730134b82cb5f2e1953c5d3b4013","src/block.rs":"8c555d194c036c2c04e1df39b66b95437b14c3331fcdbad720934b4dbee5efdf","src/debug.rs":"e8c67ef2beaff33110e2d1fa0c4a3acd051780f8e985addfa734cdc93945d332","src/encoding.rs":"9f8116277ba304ee47089e311d176a9612d3ca56bd89e031d83346e873224054","src/ffi.rs":"6c1ff08f74f3219cf91d7c76d32aaa39834a3acfae54df6e544f23ec889b2259","src/global.rs":"3a17009b513ef9f18fd76b333e72f1ed082d63db1674bfffcf74bb5be40c4d57","src/lib.rs":"75e11b6c8d0d45df8ddbd44cbb65c96b41c57b9dd95bc9a089428b3d2eccb654","src/rc_block.rs":"fe821a8d5c05562a1a310283d432c8f28f0d13d18a75b3e5ca00b8941052c65e","src/stack.rs":"4ab335be7ce0aff1e36e00b69120d30721c58fc35d876712cdeaf895a86de72f","src/traits.rs":"7af1d39a56afd41cf6c49d99f895d2670c455d68135f69ad4b29cd578154dcdb","translation-config.toml":"15250688351cbf65e6a4544e1b2d85d3941d59b203fea54ec90e6e11545fe72b"},"package":"cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5"}
+\ No newline at end of file
diff --git a/third_party/rust/block2/CHANGELOG.md b/third_party/rust/block2/CHANGELOG.md
@@ -0,0 +1,320 @@
+# Changelog
+
+Notable changes to this crate will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
+
+## Unreleased - YYYY-MM-DD
+
+
+## [0.6.2] - 2025-10-04
+[0.6.2]: https://github.com/madsmtm/objc2/compare/block2-0.6.1...block2-0.6.2
+
+## Fixed
+* Fixed documentation on docs.rs.
+
+
+## [0.6.1] - 2025-04-19
+[0.6.1]: https://github.com/madsmtm/objc2/compare/block2-0.6.0...block2-0.6.1
+
+### Added
+* Added `DynBlock` alias to help facilitate upgrading to v0.7 when that's done.
+
+### Fixed
+* Slightly improved documentation around `ManualBlockEncoding`.
+
+
+## [0.6.0] - 2025-01-22
+[0.6.0]: https://github.com/madsmtm/objc2/compare/block2-0.5.1...block2-0.6.0
+
+### Added
+* Added `StackBlock::with_encoding` and `RcBlock::with_encoding` for creating
+ blocks with an encoding specified by `ManualBlockEncoding`.
+
+ This is useful for certain APIs that require blocks to have an encoding.
+* Added `RcBlock::as_ptr`.
+* Added `RcBlock::into_raw`.
+
+### Changed
+* **BREAKING**: Updated `objc2` dependency to `v0.6.0`.
+
+### Fixed
+* **BREAKING**: Converted function signatures into using `extern "C-unwind"`.
+ This allows unwinding through blocks.
+
+### Removed
+* **BREAKING**: Removed the deprecated `apple` Cargo feature flag.
+
+
+## [0.5.1] - 2024-05-21
+[0.5.1]: https://github.com/madsmtm/objc2/compare/block2-0.5.0...block2-0.5.1
+
+### Deprecated
+* Deprecated the `apple` Cargo feature flag, it is assumed by default on Apple
+ platforms.
+
+
+## [0.5.0] - 2024-04-17
+[0.5.0]: https://github.com/madsmtm/objc2/compare/block2-0.4.0...block2-0.5.0
+
+### Added
+* **BREAKING**: Added `Block::copy` to convert blocks to `RcBlock`. This
+ replaces `StackBlock::copy`, but since `StackBlock` implements `Deref`, this
+ will likely work as before.
+* Added `RcBlock::new(closure)` as a more efficient and flexible alternative
+ to `StackBlock::new(closure).copy()`.
+* Added `BlockFn` trait to describe valid `dyn Fn` types for blocks.
+
+### Changed
+* **BREAKING**: Changed how blocks specify their parameter and return types.
+ We now use `dyn Fn` so that it is more clear what the parameter and return
+ types are. This also allows us to support non-`'static` blocks.
+
+ ```rust
+ // Before
+ let block: &Block<(), ()>;
+ let block: &Block<(i32,), i32>;
+ let block: &Block<(i32, u32), (i32, u32)>;
+
+ // After
+ let block: &Block<dyn Fn()>;
+ let block: &Block<dyn Fn(i32) -> i32>;
+ let block: &Block<dyn Fn(i32, u32) -> (i32, u32)>;
+ // Now possible
+ let block: &Block<dyn Fn() + '_>; // Non-'static block
+ ```
+* **BREAKING**: Make `Block::call` safe, and instead move the upholding of the
+ safety invariant to the type itself.
+* **BREAKING**: Renamed `RcBlock::new(ptr)` to `RcBlock::from_raw(ptr)`.
+* **BREAKING**: Made `RcBlock` use the null-pointer optimization;
+ `RcBlock::from_raw` and `RcBlock::copy` now return an `Option`.
+* **BREAKING**: Only expose the actually public symbols `_Block_copy`,
+ `_Block_release`, `_Block_object_assign`, `_Block_object_dispose`,
+ `_NSConcreteGlobalBlock`, `_NSConcreteStackBlock` and `Class` in `ffi`
+ module.
+* **BREAKING**: Renamed `IntoConcreteBlock` to `IntoBlock`, moved
+ associated type `Output` to be a generic parameter, and added lifetime
+ parameter.`
+* No longer use the `block-sys` crate for linking to the blocks runtime.
+* Renamed `ConcreteBlock` to `StackBlock`, and added a lifetime parameter. The
+ old name is deprecated.
+* Added `Copy` implementation for `StackBlock`.
+
+### Removed
+* **BREAKING**: Removed `BlockArguments` in favour of `BlockFn`, which
+ describes both the parameter types, as well as the return type.
+
+### Fixed
+* **BREAKING**: `StackBlock::new` now requires the closure to be `Clone`. If
+ this is not desired, use `RcBlock::new` instead.
+* Relaxed the `F: Debug` bound on `StackBlock`'s `Debug` implementation.
+* **BREAKING**: Fixed `GlobalBlock` not having the correct variance. This may
+ break if you were using lifetimes in your parameters, as those are now a bit
+ too restrictive.
+
+
+## [0.4.0] - 2023-12-03
+[0.4.0]: https://github.com/madsmtm/objc2/compare/block2-0.3.0...block2-0.4.0
+
+### Changed
+* **BREAKING**: Updated `objc2` dependency to `v0.5.0`.
+
+
+## [0.3.0] - 2023-07-31
+[0.3.0]: https://github.com/madsmtm/objc2/compare/block2-0.2.0...block2-0.3.0
+
+### Fixed
+* Bumped version number to ensure that this crate can be compiled together
+ with code that depends on pre-releases of `0.2.0`.
+
+
+## [0.2.0] - 2023-06-20
+[0.2.0]: https://github.com/madsmtm/objc2/compare/block2-0.2.0-alpha.8...block2-0.2.0
+
+### Changed
+* **BREAKING**: Updated `objc2` dependency to `v0.4.0`.
+
+
+## [0.2.0-alpha.8] - 2023-02-07
+[0.2.0-alpha.8]: https://github.com/madsmtm/objc2/compare/block2-0.2.0-alpha.7...block2-0.2.0-alpha.8
+
+### Changed
+* **BREAKING**: Use traits from `objc2` `v0.3.0-beta.5` instead of
+ `objc2-encode`.
+* Updated `ffi` module to `block-sys v0.2.0`.
+
+
+## [0.2.0-alpha.7] - 2022-12-24
+[0.2.0-alpha.7]: https://github.com/madsmtm/objc2/compare/block2-0.2.0-alpha.6...block2-0.2.0-alpha.7
+
+### Changed
+* Improve efficiency when a block doesn't need to be destroyed.
+* **BREAKING**: Updated `objc2-encode` to `v2.0.0-pre.3`.
+* Updated `ffi` module to `block-sys v0.1.0-beta.2`.
+
+
+## [0.2.0-alpha.6] - 2022-08-28
+[0.2.0-alpha.6]: https://github.com/madsmtm/objc2/compare/block2-0.2.0-alpha.5...block2-0.2.0-alpha.6
+
+### Changed
+* **BREAKING**: Updated `objc2-encode` to `v2.0.0-pre.2`.
+* Updated `ffi` module to `block-sys v0.1.0-beta.1`.
+
+### Fixed
+* **BREAKING**: Cleaned up `BlockArguments` trait, it is now sealed and a
+ subtrait of `EncodeArguments`.
+* **BREAKING**: Cleaned up `IntoConcreteBlock` trait, it is now sealed and the
+ associated output type has been renamed to `Output`.
+
+
+## [0.2.0-alpha.5] - 2022-07-19
+[0.2.0-alpha.5]: https://github.com/madsmtm/objc2/compare/block2-0.2.0-alpha.4...block2-0.2.0-alpha.5
+
+### Added
+* Implemented `Debug` for `Block`, `ConcreteBlock`, `RcBlock` and
+ `GlobalBlock`.
+
+### Changed
+* **BREAKING**: Updated `objc2-encode` to `v2.0.0-pre.1`.
+* Updated `ffi` module to `block-sys v0.1.0-beta.0`.
+
+
+## [0.2.0-alpha.4] - 2022-06-13
+[0.2.0-alpha.4]: https://github.com/madsmtm/objc2/compare/block2-0.2.0-alpha.3...block2-0.2.0-alpha.4
+
+### Changed
+* **BREAKING**: Updated `objc2-encode` to `v2.0.0-pre.0`.
+* **BREAKING**: Updated `ffi` module to `block-sys v0.0.4`. This tweaks the
+ types of a lot of fields and parameters, and makes the apple runtime always
+ be the default.
+
+### Removed
+* **BREAKING**: Removed `DerefMut` implementation for `ConcreteBlock`.
+
+
+## [0.2.0-alpha.3] - 2022-01-03
+[0.2.0-alpha.3]: https://github.com/madsmtm/objc2/compare/block2-0.2.0-alpha.2...block2-0.2.0-alpha.3
+
+### Changed
+* Changed `global_block!` macro to take an optional semicolon at the end.
+* Improved documentation.
+* **BREAKING**: Updated `ffi` module to `block-sys v0.0.3`.
+
+
+## [0.2.0-alpha.2] - 2021-12-22
+[0.2.0-alpha.2]: https://github.com/madsmtm/objc2/compare/block2-0.2.0-alpha.1...block2-0.2.0-alpha.2
+
+### Added
+* `GlobalBlock` and corresponding `global_block!` macro, allowing statically
+ creating blocks that don't reference their environment.
+
+### Changed
+* **BREAKING**: Updated `ffi` module to `block-sys v0.0.2`. This means that
+ `Class` is now `!UnwindSafe`.
+
+
+## [0.2.0-alpha.1] - 2021-11-22
+[0.2.0-alpha.1]: https://github.com/madsmtm/objc2/compare/block2-0.2.0-alpha.0...block2-0.2.0-alpha.1
+
+### Added
+* Proper GNUStep support using `block-sys`. See that crate for usage.
+* Export `block-sys` as `ffi` module.
+
+### Removed
+* Dependency on `objc_test_utils`.
+
+### Fixed
+* `ConcreteBlock` no longer allocates block descriptors on the heap.
+* Better unwind safety in `ConcreteBlock::copy`.
+
+
+## [0.2.0-alpha.0] - 2021-10-28
+[0.2.0-alpha.0]: https://github.com/madsmtm/objc2/compare/block2-0.1.6...block2-0.2.0-alpha.0
+
+### Added
+* **BREAKING**: Blocks now require that parameter and return types implement
+ `objc2_encode::Encode`. This is a safety measure to prevent creating blocks
+ with invalid parameters.
+* Blocks now implements `objc2_encode::RefEncode` (and as such can be used in
+ Objective-C message sends).
+* Update to 2018 edition.
+
+### Changed
+* **BREAKING**: Forked the project, so it is now available under the name
+ `block2`.
+
+### Fixed
+* Soundness issues with using empty enums over FFI.
+
+
+## [0.1.6] (`block` crate) - 2016-05-08
+[0.1.6]: https://github.com/madsmtm/objc2/compare/block2-0.1.5...block2-0.1.6
+
+### Added
+* Support for linking to `libBlocksRuntime`.
+
+
+## [0.1.5] (`block` crate) - 2016-04-04
+[0.1.5]: https://github.com/madsmtm/objc2/compare/block2-0.1.4...block2-0.1.5
+
+### Changed
+* Minor code changes
+
+
+## [0.1.4] (`block` crate) - 2015-11-12
+[0.1.4]: https://github.com/madsmtm/objc2/compare/block2-0.1.3...block2-0.1.4
+
+### Removed
+* `libc` dependency.
+
+
+## [0.1.3] (`block` crate) - 2015-11-07
+[0.1.3]: https://github.com/madsmtm/objc2/compare/block2-0.1.2...block2-0.1.3
+
+### Changed
+* Updated `libc` dependency.
+
+
+## [0.1.2] (`block` crate) - 2015-10-10
+[0.1.2]: https://github.com/madsmtm/objc2/compare/block2-0.1.1...block2-0.1.2
+
+### Fixed
+* `improper_ctypes` warning.
+
+
+## [0.1.1] (`block` crate) - 2015-09-03
+[0.1.1]: https://github.com/madsmtm/objc2/compare/block2-0.1.0...block2-0.1.1
+
+### Fixed
+* Missing `Sized` bounds on traits.
+
+
+## [0.1.0] (`block` crate) - 2015-05-18
+[0.1.0]: https://github.com/madsmtm/objc2/compare/block2-0.0.2...block2-0.1.0
+
+### Added
+* `Clone` implementation for `RcBlock`.
+* Improved documentation.
+
+### Changed
+* **BREAKING**: Rename `IdBlock` to `RcBlock`.
+* **BREAKING**: Make `Block::call` take self immutably and make it `unsafe`.
+* **BREAKING**: Make `BlockArguments::call_block` `unsafe`.
+
+### Removed
+* **BREAKING**: `DerefMut` on `RcBlock`.
+* `objc` dependency.
+* `Foundation` dependency in tests.
+
+
+## [0.0.2] (`block` crate) - 2015-05-02
+[0.0.2]: https://github.com/madsmtm/objc2/compare/block2-0.0.1...block2-0.0.2
+
+### Changed
+* Use `objc_id`.
+
+
+## [0.0.1] (`block` crate) - 2015-04-17
+[0.0.1]: https://github.com/madsmtm/objc2/releases/tag/block2-0.0.1
+
+Initial version.
diff --git a/third_party/rust/block2/Cargo.lock b/third_party/rust/block2/Cargo.lock
@@ -0,0 +1,57 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "block2"
+version = "0.6.2"
+dependencies = [
+ "objc2",
+]
+
+[[package]]
+name = "cc"
+version = "1.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "objc2"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05"
+dependencies = [
+ "objc2-encode",
+ "objc2-exception-helper",
+ "objc2-proc-macros",
+]
+
+[[package]]
+name = "objc2-encode"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
+
+[[package]]
+name = "objc2-exception-helper"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "objc2-proc-macros"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c42034b8cee04579dfb1fc264762ac9cbc84b03fdd00850d2ea45b356f1297e"
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
diff --git a/third_party/rust/block2/Cargo.toml b/third_party/rust/block2/Cargo.toml
@@ -0,0 +1,115 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2021"
+rust-version = "1.71"
+name = "block2"
+version = "0.6.2"
+authors = ["Mads Marquart <mads@marquart.dk>"]
+build = false
+autolib = false
+autobins = false
+autoexamples = false
+autotests = false
+autobenches = false
+description = "Apple's C language extension of blocks"
+readme = "README.md"
+keywords = [
+ "objective-c",
+ "macos",
+ "ios",
+ "blocks",
+]
+categories = [
+ "api-bindings",
+ "development-tools::ffi",
+ "os::macos-apis",
+ "external-ffi-bindings",
+]
+license = "MIT"
+repository = "https://github.com/madsmtm/objc2"
+
+[package.metadata.docs.rs]
+default-target = "aarch64-apple-darwin"
+features = ["unstable-private"]
+rustc-args = [
+ "--cfg",
+ "docsrs",
+]
+targets = [
+ "aarch64-apple-darwin",
+ "x86_64-apple-darwin",
+ "aarch64-apple-ios",
+ "aarch64-apple-tvos",
+ "aarch64-apple-watchos",
+ "aarch64-apple-ios-macabi",
+ "aarch64-apple-visionos",
+ "x86_64-unknown-linux-gnu",
+ "i686-unknown-linux-gnu",
+]
+
+[package.metadata.release]
+shared-version = false
+tag-prefix = "block2"
+
+[features]
+alloc = []
+compiler-rt = ["objc2/unstable-compiler-rt"]
+default = ["std"]
+gnustep-1-7 = ["objc2/gnustep-1-7"]
+gnustep-1-8 = [
+ "gnustep-1-7",
+ "objc2/gnustep-1-8",
+]
+gnustep-1-9 = [
+ "gnustep-1-8",
+ "objc2/gnustep-1-9",
+]
+gnustep-2-0 = [
+ "gnustep-1-9",
+ "objc2/gnustep-2-0",
+]
+gnustep-2-1 = [
+ "gnustep-2-0",
+ "objc2/gnustep-2-1",
+]
+std = ["alloc"]
+unstable-coerce-pointee = []
+unstable-objfw = []
+unstable-private = []
+unstable-winobjc = ["gnustep-1-8"]
+
+[lib]
+name = "block2"
+path = "src/lib.rs"
+
+[dependencies.objc2]
+version = ">=0.6.2, <0.8.0"
+features = ["std"]
+default-features = false
+
+[dev-dependencies]
+
+[lints.clippy]
+ptr_as_ptr = "warn"
+redundant_feature_names = "allow"
+
+[lints.clippy.cargo]
+level = "warn"
+priority = -1
+
+[lints.rust]
+elided_lifetimes_in_paths = "warn"
+missing_copy_implementations = "warn"
+non_ascii_idents = "deny"
+unreachable_pub = "warn"
+unsafe_op_in_unsafe_fn = "deny"
diff --git a/third_party/rust/block2/README.md b/third_party/rust/block2/README.md
@@ -0,0 +1,19 @@
+# `block2`
+
+[](https://crates.io/crates/block2)
+[](../../LICENSE.md)
+[](https://docs.rs/block2/)
+[](https://github.com/madsmtm/objc2/actions/workflows/ci.yml)
+
+Apple's C language extension of blocks in Rust.
+
+This crate provides functionality for interacting with C blocks, which is the
+C-equivalent of Rust's closures.
+
+They are _technically_ not limited to only being used in Objective-C, though
+in practice it's likely the only place you'll ever encounter them.
+
+See [the docs](https://docs.rs/block2/) for a more thorough overview.
+
+This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2),
+see that for related crates.
diff --git a/third_party/rust/block2/src/abi.rs b/third_party/rust/block2/src/abi.rs
@@ -0,0 +1,375 @@
+//! The documentation for these bindings is a mix from GNUStep's and Apple's
+//! sources, but the [ABI specification][ABI] is really the place you should
+//! be looking!
+//!
+//! [ABI]: https://clang.llvm.org/docs/Block-ABI-Apple.html
+#![allow(unused)]
+
+use core::ffi::{c_char, c_int, c_ulong, c_void};
+use core::fmt;
+use core::mem::MaybeUninit;
+use core::ops::{BitAnd, BitOr};
+
+use alloc::format;
+
+use crate::ffi::Class;
+
+/// Block descriptor flags.
+#[repr(transparent)]
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub(crate) struct BlockFlags(pub(crate) c_int);
+
+impl BlockFlags {
+ pub(crate) const EMPTY: Self = Self(0);
+
+ /// Note: Not public ABI.
+ const BLOCK_DEALLOCATING: Self = Self(0x0001);
+
+ /// Note: Not public ABI.
+ const BLOCK_REFCOUNT_MASK: Self = Self(if cfg!(feature = "gnustep-1-7") {
+ // Mask for the reference count in byref structure's flags field. The low
+ // 3 bytes are reserved for the reference count, the top byte for the flags.
+ 0x00ffffff
+ } else if cfg!(any(feature = "compiler-rt", feature = "unstable-objfw")) {
+ 0xffff
+ } else if cfg!(target_vendor = "apple") {
+ 0xfffe // runtime
+ } else {
+ 0
+ });
+
+ /// Note: Not public ABI.
+ const BLOCK_INLINE_LAYOUT_STRING: Self = Self(1 << 21);
+
+ /// Note: Not public ABI.
+ const BLOCK_SMALL_DESCRIPTOR: Self = Self(1 << 22);
+
+ pub(crate) const BLOCK_IS_NOESCAPE: Self = Self(1 << 23);
+
+ /// Note: Not public ABI.
+ const BLOCK_NEEDS_FREE: Self = Self(1 << 24);
+
+ /// The block descriptor contains copy and dispose helpers.
+ pub(crate) const BLOCK_HAS_COPY_DISPOSE: Self = Self(1 << 25);
+
+ /// Helpers have C++ code.
+ #[doc(alias = "BLOCK_HAS_CXX_OBJ")]
+ pub(crate) const BLOCK_HAS_CTOR: Self = Self(1 << 26);
+
+ /// Note: Not public ABI.
+ const BLOCK_IS_GC: Self = Self(1 << 27);
+
+ /// Block is stored in global memory and does not need to be copied.
+ pub(crate) const BLOCK_IS_GLOBAL: Self = Self(1 << 28);
+
+ /// Block function uses a calling convention that returns a structure via a
+ /// pointer passed in by the caller.
+ ///
+ /// match (BLOCK_USE_STRET, BLOCK_HAS_SIGNATURE) {
+ /// (false, false) => 10.6.ABI, no signature field available
+ /// (true, false) => 10.6.ABI, no signature field available
+ /// (false, true) => ABI.2010.3.16, regular calling convention, presence of signature field
+ /// (true, true) => ABI.2010.3.16, stret calling convention, presence of signature field,
+ /// }
+ ///
+ /// See <https://clang.llvm.org/docs/Block-ABI-Apple.html#high-level>
+ #[doc(alias = "BLOCK_USE_SRET")]
+ #[doc(alias = "BLOCK_HAS_DESCRIPTOR")]
+ pub(crate) const BLOCK_USE_STRET: Self = Self(1 << 29);
+
+ /// Block has an Objective-C type encoding.
+ pub(crate) const BLOCK_HAS_SIGNATURE: Self = Self(1 << 30);
+
+ /// Note: Not public ABI.
+ const BLOCK_HAS_EXTENDED_LAYOUT: Self = Self(1 << 31);
+
+ /// `const` version of [`PartialEq`].
+ pub(crate) const fn equals(self, other: Self) -> bool {
+ self.0 == other.0
+ }
+
+ /// `const` version of [`BitOr`]: adds the flags together.
+ pub(crate) const fn union(self, other: Self) -> Self {
+ Self(self.0 | other.0)
+ }
+
+ /// `const` version of [`BitAnd`]: only keeps the common flags.
+ pub(crate) const fn intersect(self, other: Self) -> Self {
+ Self(self.0 & other.0)
+ }
+
+ /// Returns `true` if and only if all the flags from `other` are enabled,
+ /// i.e. `self & other == other`.
+ pub(crate) const fn has(self, other: Self) -> bool {
+ self.intersect(other).equals(other)
+ }
+}
+
+/// See [`BlockFlags::union`].
+impl BitOr for BlockFlags {
+ type Output = Self;
+
+ fn bitor(self, other: Self) -> Self {
+ self.union(other)
+ }
+}
+
+/// See [`BlockFlags::intersect`].
+impl BitAnd for BlockFlags {
+ type Output = Self;
+
+ fn bitand(self, other: Self) -> Self {
+ self.intersect(other)
+ }
+}
+
+impl fmt::Debug for BlockFlags {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut f = f.debug_struct("BlockFlags");
+ f.field("value", &format!("{:032b}", self.0));
+
+ macro_rules! test_flags {
+ {$(
+ $(#[$m:meta])?
+ $name:ident: $flag:ident
+ );* $(;)?} => ($(
+ $(#[$m])?
+ f.field(stringify!($name), &self.has(Self::$flag));
+ )*)
+ }
+ test_flags! {
+ #[cfg(target_vendor = "apple")]
+ deallocating: BLOCK_DEALLOCATING;
+ #[cfg(target_vendor = "apple")]
+ inline_layout_string: BLOCK_INLINE_LAYOUT_STRING;
+ #[cfg(target_vendor = "apple")]
+ small_descriptor: BLOCK_SMALL_DESCRIPTOR;
+ #[cfg(target_vendor = "apple")]
+ is_noescape: BLOCK_IS_NOESCAPE;
+ #[cfg(target_vendor = "apple")]
+ needs_free: BLOCK_NEEDS_FREE;
+ has_copy_dispose: BLOCK_HAS_COPY_DISPOSE;
+ has_ctor: BLOCK_HAS_CTOR;
+ #[cfg(target_vendor = "apple")]
+ is_gc: BLOCK_IS_GC;
+ is_global: BLOCK_IS_GLOBAL;
+ use_stret: BLOCK_USE_STRET;
+ has_signature: BLOCK_HAS_SIGNATURE;
+ #[cfg(target_vendor = "apple")]
+ has_extended_layout: BLOCK_HAS_EXTENDED_LAYOUT;
+ }
+
+ f.field("over_referenced", &self.has(Self::BLOCK_REFCOUNT_MASK));
+ f.field(
+ "reference_count",
+ &((*self & Self::BLOCK_REFCOUNT_MASK).0 >> 1),
+ );
+
+ f.finish_non_exhaustive()
+ }
+}
+
+/// The value is of some id-like type, and should be copied as an Objective-C
+/// object: i.e. by sending -retain or via the GC assign functions in GC mode
+/// (not yet supported).
+///
+/// id, NSObject, __attribute__((NSObject)), block, ...
+pub(crate) const BLOCK_FIELD_IS_OBJECT: c_int = 3;
+
+/// The field is a block. This must be copied by the block copy functions.
+///
+/// a block variable
+pub(crate) const BLOCK_FIELD_IS_BLOCK: c_int = 7;
+
+/// The field is an indirect reference to a variable declared with the __block
+/// storage qualifier.
+///
+/// the on stack structure holding the __block variable
+pub(crate) const BLOCK_FIELD_IS_BYREF: c_int = 8;
+
+/// The field is an indirect reference to a variable declared with the __block
+/// storage qualifier.
+///
+/// declared __weak, only used in byref copy helpers
+pub(crate) const BLOCK_FIELD_IS_WEAK: c_int = 16;
+
+/// The field is an indirect reference to a variable declared with the __block
+/// storage qualifier.
+///
+/// called from __block (byref) copy/dispose support routines.
+pub(crate) const BLOCK_BYREF_CALLER: c_int = 128;
+
+/// The expected header of every block.
+#[repr(C)]
+#[doc(alias = "__block_literal")]
+#[doc(alias = "Block_layout")]
+#[doc(alias = "Block_basic")]
+#[allow(missing_debug_implementations)]
+#[derive(Clone, Copy)]
+pub struct BlockHeader {
+ /// Class pointer.
+ ///
+ /// Always initialised to &_NSConcreteStackBlock for blocks that are
+ /// created on the stack or &_NSConcreteGlobalBlock for blocks that are
+ /// created in global storage.
+ pub isa: *const Class,
+ /// Flags.
+ ///
+ /// See the `BlockFlags` enumerated type for possible values.
+ ///
+ /// Contains reference count in Apple's and ObjFW's runtime.
+ #[doc(alias = "Block_flags")]
+ pub(crate) flags: BlockFlags,
+ /// Reserved.
+ ///
+ /// Initialized to 0 by the compiler, but is said to be uninitialized in
+ /// the specification.
+ ///
+ /// Used for the reference count in GNUStep's and WinObjC's runtime.
+ #[doc(alias = "Block_size")]
+ pub(crate) reserved: MaybeUninit<c_int>,
+ /// The function that implements the block.
+ ///
+ /// The first parameter is a pointer to this structure, the subsequent
+ /// parameters are the block's explicit parameters.
+ ///
+ /// If the BLOCK_USE_SRET & BLOCK_HAS_SIGNATURE flag is set, there is an
+ /// additional hidden parameter, which is a pointer to the space on the
+ /// stack allocated to hold the return value.
+ pub invoke: Option<unsafe extern "C-unwind" fn()>,
+ /// The block's descriptor.
+ pub(crate) descriptor: BlockDescriptorPtr,
+}
+
+/// The type of this is:
+/// ```pseudo-code
+/// match (BLOCK_HAS_COPY_DISPOSE, BLOCK_HAS_SIGNATURE) {
+/// (false, false) => BlockDescriptor,
+/// (true, false) => BlockDescriptorCopyDispose,
+/// (false, true) => BlockDescriptorSignature,
+/// (true, true) => BlockDescriptorCopyDisposeSignature,
+/// }
+/// ```
+///
+/// Since all of these start with `BlockDescriptor`, it is always safe to
+/// use the `basic` field.
+//
+// Note: We use an union on top of the pointer, since otherwise the descriptor
+// would be forced to have a greater size than is actually required.
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub(crate) union BlockDescriptorPtr {
+ pub(crate) basic: *const BlockDescriptor,
+ pub(crate) with_copy_dispose: *const BlockDescriptorCopyDispose,
+ pub(crate) with_signature: *const BlockDescriptorSignature,
+ pub(crate) with_copy_dispose_signature: *const BlockDescriptorCopyDisposeSignature,
+}
+
+/// Basic block descriptor.
+#[repr(C)]
+#[doc(alias = "__block_descriptor")]
+#[doc(alias = "Block_descriptor_1")]
+#[derive(Clone, Copy, Debug)]
+pub(crate) struct BlockDescriptor {
+ /// Reserved for future use. Currently always 0.
+ pub(crate) reserved: c_ulong,
+ /// Size of the block.
+ pub(crate) size: c_ulong,
+}
+
+/// Block descriptor that contains copy and dispose operations.
+///
+/// Requires BLOCK_HAS_COPY_DISPOSE.
+#[repr(C)]
+#[doc(alias = "__block_descriptor")]
+#[doc(alias = "Block_descriptor_2")]
+#[derive(Clone, Copy, Debug)]
+pub(crate) struct BlockDescriptorCopyDispose {
+ /// Reserved for future use. Currently always 0.
+ pub(crate) reserved: c_ulong,
+ /// Size of the block.
+ pub(crate) size: c_ulong,
+
+ /// Helper to copy the block if it contains nontrivial copy operations.
+ ///
+ /// This may be NULL since macOS 11.0.1 in Apple's runtime, but this
+ /// should not be relied on.
+ pub(crate) copy: Option<unsafe extern "C-unwind" fn(dst: *mut c_void, src: *const c_void)>,
+ /// Helper to destroy the block after being copied.
+ ///
+ /// This may be NULL since macOS 11.0.1 in Apple's runtime, but this
+ /// should not be relied on.
+ pub(crate) dispose: Option<unsafe extern "C-unwind" fn(src: *mut c_void)>,
+}
+
+/// Block descriptor that has an encoding / a signature.
+///
+/// Requires BLOCK_HAS_SIGNATURE.
+#[repr(C)]
+#[doc(alias = "__block_descriptor")]
+#[doc(alias = "Block_descriptor_3")]
+#[derive(Clone, Copy, Debug)]
+pub(crate) struct BlockDescriptorSignature {
+ /// Reserved for future use. Currently always 0.
+ pub(crate) reserved: c_ulong,
+ /// Size of the block.
+ pub(crate) size: c_ulong,
+
+ /// Objective-C type encoding of the block.
+ #[doc(alias = "signature")]
+ pub(crate) encoding: *const c_char,
+}
+
+/// Block descriptor that contains copy and dispose operations, and which
+/// has an encoding / a signature.
+///
+/// Requires BLOCK_HAS_COPY_DISPOSE and BLOCK_HAS_SIGNATURE.
+#[repr(C)]
+#[doc(alias = "__block_descriptor")]
+#[doc(alias = "Block_descriptor_2")]
+#[doc(alias = "Block_descriptor_3")]
+#[derive(Clone, Copy, Debug)]
+pub(crate) struct BlockDescriptorCopyDisposeSignature {
+ /// Reserved for future use. Currently always 0.
+ pub(crate) reserved: c_ulong,
+ /// Size of the block.
+ pub(crate) size: c_ulong,
+
+ /// Helper to copy the block if it contains nontrivial copy operations.
+ ///
+ /// This may be NULL since macOS 11.0.1 in Apple's runtime, but this
+ /// should not be relied on.
+ pub(crate) copy: Option<unsafe extern "C-unwind" fn(dst: *mut c_void, src: *const c_void)>,
+ /// Helper to destroy the block after being copied.
+ ///
+ /// This may be NULL since macOS 11.0.1 in Apple's runtime, but this
+ /// should not be relied on.
+ pub(crate) dispose: Option<unsafe extern "C-unwind" fn(src: *mut c_void)>,
+
+ /// Objective-C type encoding of the block.
+ #[doc(alias = "signature")]
+ pub(crate) encoding: *const c_char,
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ fn assert_no_trailing_padding<T>() {
+ struct AddU8<T> {
+ t: T,
+ extra: u8,
+ }
+ assert_ne!(core::mem::size_of::<T>(), core::mem::size_of::<AddU8<T>>());
+ }
+
+ #[test]
+ fn no_types_have_padding() {
+ assert_no_trailing_padding::<BlockHeader>();
+ assert_no_trailing_padding::<BlockDescriptorPtr>();
+ assert_no_trailing_padding::<BlockDescriptor>();
+ assert_no_trailing_padding::<BlockDescriptorCopyDispose>();
+ assert_no_trailing_padding::<BlockDescriptorSignature>();
+ assert_no_trailing_padding::<BlockDescriptorCopyDisposeSignature>();
+ }
+}
diff --git a/third_party/rust/block2/src/block.rs b/third_party/rust/block2/src/block.rs
@@ -0,0 +1,203 @@
+use core::fmt;
+use core::marker::PhantomData;
+use core::ptr::NonNull;
+
+use objc2::encode::{Encoding, RefEncode};
+
+use crate::abi::BlockHeader;
+use crate::debug::debug_block_header;
+use crate::rc_block::block_copy_fail;
+use crate::{BlockFn, RcBlock};
+
+/// An opaque type that holds an Objective-C block.
+///
+/// The generic type `F` must be a [`dyn`] [`Fn`] that implements
+/// the [`BlockFn`] trait (which means parameter and return types must be
+/// "encodable"), and describes the parameter and return types of the block.
+///
+/// For example, you may have the type `Block<dyn Fn(u8, u8) -> i32>`, and
+/// that would be a `'static` block that takes two `u8`s, and returns an
+/// `i32`.
+///
+/// If you want the block to carry a lifetime, use `Block<dyn Fn() + 'a>`,
+/// just like you'd usually do with `dyn Fn`.
+///
+/// [`dyn`]: https://doc.rust-lang.org/std/keyword.dyn.html
+///
+///
+/// # Memory layout
+///
+/// This is intended to be an `extern type`, and as such the memory layout of
+/// this type is _not_ guaranteed. That said, **pointers** to this type are
+/// always thin, and match that of Objective-C blocks. So the layout of e.g.
+/// `&Block<dyn Fn(...) -> ... + '_>` is defined, and guaranteed to be
+/// pointer-sized and ABI-compatible with a block pointer.
+///
+///
+/// # Safety invariant
+///
+/// Calling this potentially invokes foreign code, so you must verify, when
+/// creating a reference to this, or returning it from an external API, that
+/// it doesn't violate any of Rust's safety rules.
+///
+/// In particular, blocks are sharable with multiple references (see e.g.
+/// [`Block::copy`]), so the caller must ensure that calling it can never
+/// cause a data race. This usually means you'll have to use some form of
+/// interior mutability, if you need to mutate something from inside a block.
+//
+// TODO: Potentially restrict to `F: BlockFn`, for better error messages?
+#[repr(C)]
+pub struct Block<F: ?Sized> {
+ _inner: [u8; 0],
+ /// We store `BlockHeader` + the closure captures, but `Block` has to
+ /// remain an empty type because we don't know the size of the closure,
+ /// and otherwise the compiler would think we only have provenance over
+ /// `BlockHeader`.
+ ///
+ /// This is possible to improve once we have extern types.
+ _header: PhantomData<BlockHeader>,
+ _p: PhantomData<F>,
+}
+
+// SAFETY: Pointers to `Block` is an Objective-C block.
+// This is only valid when `F: BlockFn`, as that bounds the parameters and
+// return type to be encodable too.
+unsafe impl<F: ?Sized + BlockFn> RefEncode for Block<F> {
+ const ENCODING_REF: Encoding = Encoding::Block;
+}
+
+impl<F: ?Sized> Block<F> {
+ fn header(&self) -> &BlockHeader {
+ let ptr: NonNull<Self> = NonNull::from(self);
+ let ptr: NonNull<BlockHeader> = ptr.cast();
+ // SAFETY: `Block` is `BlockHeader` + closure
+ unsafe { ptr.as_ref() }
+ }
+
+ /// Copy the block onto the heap as an [`RcBlock`].
+ ///
+ /// The behaviour of this function depends on whether the block is from a
+ /// [`RcBlock`] or a [`StackBlock`]. In the former case, it will bump the
+ /// reference-count (just as-if you'd `Clone`'d the `RcBlock`), in the
+ /// latter case it will construct a new `RcBlock` from the `StackBlock`.
+ ///
+ /// This distinction should not matter, except for micro-optimizations.
+ ///
+ /// [`StackBlock`]: crate::StackBlock
+ #[doc(alias = "Block_copy")]
+ #[doc(alias = "_Block_copy")]
+ #[inline]
+ pub fn copy(&self) -> RcBlock<F> {
+ let ptr: *const Self = self;
+ let ptr: *mut Block<F> = ptr as *mut _;
+ // SAFETY: The lifetime of the block is extended from `&self` to that
+ // of the `RcBlock`, which is fine, because the lifetime of the
+ // contained closure `F` is still carried along to the `RcBlock`.
+ unsafe { RcBlock::copy(ptr) }.unwrap_or_else(|| block_copy_fail())
+ }
+
+ /// Call the block.
+ ///
+ /// The arguments must be passed as a tuple. The return is the output of
+ /// the block.
+ #[doc(alias = "invoke")]
+ pub fn call(&self, args: F::Args) -> F::Output
+ where
+ F: BlockFn,
+ {
+ // TODO: Is `invoke` actually ever null?
+ let invoke = self.header().invoke.unwrap_or_else(|| unreachable!());
+
+ let ptr: NonNull<Self> = NonNull::from(self);
+ let ptr: *mut Self = ptr.as_ptr();
+
+ // SAFETY: The closure is an `Fn`, and as such is safe to call from an
+ // immutable reference.
+ unsafe { F::__call_block(invoke, ptr, args) }
+ }
+}
+
+impl<F: ?Sized> fmt::Debug for Block<F> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut f = f.debug_struct("Block");
+ debug_block_header(self.header(), &mut f);
+ f.finish_non_exhaustive()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::cell::Cell;
+ use core::sync::atomic::{AtomicUsize, Ordering};
+
+ use super::*;
+
+ /// Test that the way you specify lifetimes are as documented in the
+ /// reference.
+ /// <https://doc.rust-lang.org/nightly/reference/lifetime-elision.html#default-trait-object-lifetimes>
+ #[test]
+ fn test_rust_dyn_lifetime_semantics() {
+ fn takes_static(block: &Block<dyn Fn() + 'static>) {
+ block.call(());
+ }
+
+ fn takes_elided(block: &Block<dyn Fn() + '_>) {
+ block.call(());
+ }
+
+ fn takes_unspecified(block: &Block<dyn Fn()>) {
+ block.call(());
+ }
+
+ // Static lifetime
+ static MY_STATIC: AtomicUsize = AtomicUsize::new(0);
+ MY_STATIC.store(0, Ordering::Relaxed);
+ let static_lifetime: RcBlock<dyn Fn() + 'static> = RcBlock::new(|| {
+ MY_STATIC.fetch_add(1, Ordering::Relaxed);
+ });
+ takes_static(&static_lifetime);
+ takes_elided(&static_lifetime);
+ takes_unspecified(&static_lifetime);
+ assert_eq!(MY_STATIC.load(Ordering::Relaxed), 3);
+
+ // Lifetime declared with `'_`
+ let captured = Cell::new(0);
+ let elided_lifetime: RcBlock<dyn Fn() + '_> = RcBlock::new(|| {
+ captured.set(captured.get() + 1);
+ });
+ // takes_static(&elided_lifetime); // Compile error
+ takes_elided(&elided_lifetime);
+ // takes_unspecified(&elided_lifetime); // Compile error
+ assert_eq!(captured.get(), 1);
+
+ // Lifetime kept unspecified
+ let captured = Cell::new(0);
+ let unspecified_lifetime: RcBlock<dyn Fn()> = RcBlock::new(|| {
+ captured.set(captured.get() + 1);
+ });
+ // takes_static(&unspecified_lifetime); // Compile error
+ takes_elided(&unspecified_lifetime);
+ // takes_unspecified(&unspecified_lifetime); // Compile error
+ assert_eq!(captured.get(), 1);
+ }
+
+ #[allow(dead_code)]
+ fn unspecified_in_fn_is_static(block: &Block<dyn Fn()>) -> &Block<dyn Fn() + 'static> {
+ block
+ }
+
+ #[allow(dead_code)]
+ fn lending_block<'b>(block: &Block<dyn Fn() -> &'b i32 + 'b>) {
+ let _ = *block.call(());
+ }
+
+ #[allow(dead_code)]
+ fn takes_lifetime(_: &Block<dyn Fn(&i32) -> &i32>) {
+ // Not actually callable yet
+ }
+
+ #[allow(dead_code)]
+ fn covariant<'b, 'f>(b: &'b Block<dyn Fn() + 'static>) -> &'b Block<dyn Fn() + 'f> {
+ b
+ }
+}
diff --git a/third_party/rust/block2/src/debug.rs b/third_party/rust/block2/src/debug.rs
@@ -0,0 +1,135 @@
+use core::ffi::CStr;
+use core::fmt::{Debug, DebugStruct, Error, Formatter};
+use core::ptr;
+
+use crate::abi::{BlockDescriptorPtr, BlockFlags, BlockHeader};
+use crate::ffi;
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+struct Isa(*const ffi::Class);
+
+impl Isa {
+ #[allow(unused_unsafe)]
+ fn is_global(self) -> bool {
+ ptr::eq(
+ unsafe { ptr::addr_of!(ffi::_NSConcreteGlobalBlock) },
+ self.0,
+ )
+ }
+
+ #[allow(unused_unsafe)]
+ fn is_stack(self) -> bool {
+ ptr::eq(unsafe { ptr::addr_of!(ffi::_NSConcreteStackBlock) }, self.0)
+ }
+}
+
+impl Debug for Isa {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
+ if self.is_global() {
+ f.write_str("_NSConcreteGlobalBlock")
+ } else if self.is_stack() {
+ f.write_str("_NSConcreteStackBlock")
+ } else {
+ write!(f, "{:?} (likely _NSConcreteMallocBlock)", self.0)
+ }
+ }
+}
+
+pub(crate) fn debug_block_header(header: &BlockHeader, f: &mut DebugStruct<'_, '_>) {
+ f.field("isa", &Isa(header.isa));
+ f.field("flags", &header.flags);
+ f.field("reserved", &header.reserved);
+ f.field("invoke", &header.invoke);
+ f.field(
+ "descriptor",
+ &BlockDescriptorHelper {
+ has_copy_dispose: header.flags.has(BlockFlags::BLOCK_HAS_COPY_DISPOSE),
+ has_signature: header.flags.has(BlockFlags::BLOCK_HAS_SIGNATURE),
+ descriptor: header.descriptor,
+ },
+ );
+}
+
+#[derive(Clone, Copy)]
+struct BlockDescriptorHelper {
+ has_copy_dispose: bool,
+ has_signature: bool,
+ descriptor: BlockDescriptorPtr,
+}
+
+impl Debug for BlockDescriptorHelper {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
+ if unsafe { self.descriptor.basic }.is_null() {
+ return f.write_str("(null)");
+ }
+
+ let mut f = f.debug_struct("BlockDescriptor");
+
+ let header = unsafe { self.descriptor.basic.as_ref().unwrap() };
+
+ f.field("reserved", &header.reserved);
+ f.field("size", &header.size);
+
+ match (self.has_copy_dispose, self.has_signature) {
+ (false, false) => {}
+ (true, false) => {
+ let descriptor = unsafe { self.descriptor.with_copy_dispose.as_ref().unwrap() };
+ f.field("copy", &descriptor.copy);
+ f.field("dispose", &descriptor.dispose);
+ }
+ (false, true) => {
+ let descriptor = unsafe { self.descriptor.with_signature.as_ref().unwrap() };
+ f.field(
+ "encoding",
+ &if descriptor.encoding.is_null() {
+ None
+ } else {
+ Some(unsafe { CStr::from_ptr(descriptor.encoding) })
+ },
+ );
+ }
+ (true, true) => {
+ let descriptor = unsafe {
+ self.descriptor
+ .with_copy_dispose_signature
+ .as_ref()
+ .unwrap()
+ };
+ f.field("copy", &descriptor.copy);
+ f.field("dispose", &descriptor.dispose);
+ f.field(
+ "encoding",
+ &if descriptor.encoding.is_null() {
+ None
+ } else {
+ Some(unsafe { CStr::from_ptr(descriptor.encoding) })
+ },
+ );
+ }
+ }
+
+ f.finish()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ #[allow(unused_unsafe)]
+ fn test_isa() {
+ let isa = Isa(unsafe { ptr::addr_of!(ffi::_NSConcreteGlobalBlock) });
+ assert!(isa.is_global());
+ assert!(!isa.is_stack());
+ let isa = Isa(unsafe { ptr::addr_of!(ffi::_NSConcreteStackBlock) });
+ assert!(!isa.is_global());
+ assert!(isa.is_stack());
+ let isa = Isa(unsafe { ptr::addr_of!(ffi::private::_NSConcreteMallocBlock) });
+ assert!(!isa.is_global());
+ assert!(!isa.is_stack());
+ let isa = Isa(ptr::null());
+ assert!(!isa.is_global());
+ assert!(!isa.is_stack());
+ }
+}
diff --git a/third_party/rust/block2/src/encoding.rs b/third_party/rust/block2/src/encoding.rs
@@ -0,0 +1,199 @@
+use alloc::ffi::CString;
+use alloc::string::ToString;
+use alloc::vec::Vec;
+use core::mem;
+
+use objc2::encode::{EncodeArguments, EncodeReturn, Encoding};
+
+/// Computes the raw signature string of the object corresponding to the block
+/// taking `A` as inputs and returning `R`.
+///
+/// Although this is currently implemented on a best-effort basis, this should
+/// still serve as a good way to obtain what to fill in the encoding string
+/// when implementing [`crate::ManualBlockEncoding`].
+///
+/// # Example
+///
+/// ```ignore
+/// assert_eq!(block_signature_string::<(i32, f32), u8>(), "C16@?0i8f12");
+/// ```
+#[allow(unused)]
+pub(crate) fn block_signature_string<A, R>() -> CString
+where
+ A: EncodeArguments,
+ R: EncodeReturn,
+{
+ block_signature_string_inner(A::ENCODINGS, &R::ENCODING_RETURN)
+}
+
+#[allow(unused)]
+fn block_signature_string_inner(args: &[Encoding], ret: &Encoding) -> CString {
+ // TODO: alignment?
+ let arg_sizes = args
+ .iter()
+ .map(Encoding::size)
+ .map(Option::unwrap_or_default)
+ .collect::<Vec<_>>();
+ let args_size = arg_sizes.iter().sum::<usize>();
+
+ // Take the hidden block parameter into account.
+ let mut off = mem::size_of::<*const ()>();
+ let mut res = ret.to_string();
+ res.push_str(&(off + args_size).to_string());
+ res.push_str("@?0");
+
+ for (arg_enc, arg_size) in args.iter().zip(arg_sizes) {
+ res.push_str(&arg_enc.to_string());
+ res.push_str(&off.to_string());
+ off += arg_size;
+ }
+
+ // UNWRAP: The above construction only uses controlled `ToString`
+ // implementations that do not include nul bytes.
+ CString::new(res).unwrap()
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use alloc::borrow::ToOwned;
+
+ #[test]
+ fn test_block_signature_string() {
+ for (args, ret, val) in [
+ (
+ &[][..],
+ &Encoding::Void,
+ #[cfg(target_pointer_width = "64")]
+ "v8@?0",
+ #[cfg(target_pointer_width = "32")]
+ "v4@?0",
+ ),
+ (
+ &[],
+ &Encoding::Int,
+ #[cfg(target_pointer_width = "64")]
+ "i8@?0",
+ #[cfg(target_pointer_width = "32")]
+ "i4@?0",
+ ),
+ (
+ &[],
+ &Encoding::Float,
+ #[cfg(target_pointer_width = "64")]
+ "f8@?0",
+ #[cfg(target_pointer_width = "32")]
+ "f4@?0",
+ ),
+ (
+ &[],
+ &Encoding::Bool,
+ #[cfg(target_pointer_width = "64")]
+ "B8@?0",
+ #[cfg(target_pointer_width = "32")]
+ "B4@?0",
+ ),
+ (
+ &[Encoding::Int],
+ &Encoding::Void,
+ #[cfg(target_pointer_width = "64")]
+ "v12@?0i8",
+ #[cfg(target_pointer_width = "32")]
+ "v8@?0i4",
+ ),
+ (
+ &[Encoding::Int],
+ &Encoding::Int,
+ #[cfg(target_pointer_width = "64")]
+ "i12@?0i8",
+ #[cfg(target_pointer_width = "32")]
+ "i8@?0i4",
+ ),
+ (
+ &[Encoding::Long, Encoding::Double, Encoding::FloatComplex],
+ &Encoding::Atomic(&Encoding::UChar),
+ #[cfg(target_pointer_width = "64")]
+ "AC32@?0l8d16jf24",
+ #[cfg(target_pointer_width = "32")]
+ "AC24@?0l4d8jf16",
+ ),
+ (
+ &[
+ Encoding::Union("ThisOrThat", &[Encoding::UShort, Encoding::Int]),
+ Encoding::Struct(
+ "ThisAndThat",
+ &[
+ Encoding::ULongLong,
+ Encoding::LongDoubleComplex,
+ Encoding::Atomic(&Encoding::Bool),
+ ],
+ ),
+ ],
+ &Encoding::String,
+ // Probably unaligned.
+ #[cfg(any(
+ target_arch = "x86_64",
+ all(target_arch = "aarch64", not(target_vendor = "apple"))
+ ))]
+ "*53@?0(ThisOrThat=Si)8{ThisAndThat=QjDAB}12",
+ #[cfg(all(target_arch = "aarch64", target_vendor = "apple"))]
+ "*37@?0(ThisOrThat=Si)8{ThisAndThat=QjDAB}12",
+ #[cfg(all(target_arch = "x86", target_vendor = "apple"))]
+ "*49@?0(ThisOrThat=Si)4{ThisAndThat=QjDAB}8",
+ #[cfg(all(target_arch = "x86", not(target_vendor = "apple")))]
+ "*41@?0(ThisOrThat=Si)4{ThisAndThat=QjDAB}8",
+ #[cfg(target_arch = "arm")]
+ "*37@?0(ThisOrThat=Si)4{ThisAndThat=QjDAB}8",
+ ),
+ (
+ &[
+ Encoding::Block,
+ Encoding::Class,
+ Encoding::Object,
+ Encoding::Pointer(&Encoding::Char),
+ Encoding::Sel,
+ Encoding::String,
+ Encoding::Unknown,
+ Encoding::Unknown,
+ Encoding::Unknown,
+ ],
+ &Encoding::Pointer(&Encoding::Atomic(&Encoding::UChar)),
+ #[cfg(target_pointer_width = "64")]
+ "^AC56@?0@?8#16@24^c32:40*48?56?56?56",
+ #[cfg(target_pointer_width = "32")]
+ "^AC28@?0@?4#8@12^c16:20*24?28?28?28",
+ ),
+ (
+ &[Encoding::Array(123, &Encoding::Object)],
+ &Encoding::Pointer(&Encoding::Class),
+ #[cfg(target_pointer_width = "64")]
+ "^#992@?0[123@]8",
+ #[cfg(target_pointer_width = "32")]
+ "^#496@?0[123@]4",
+ ),
+ // Bitfields can probably not be passed around through functions,
+ // so this may be a bit nonsensical, but let's test it anyway.
+ (
+ &[
+ Encoding::BitField(1, None),
+ Encoding::BitField(2, None),
+ Encoding::BitField(3, None),
+ Encoding::BitField(6, None),
+ Encoding::BitField(8, None),
+ Encoding::BitField(42, None),
+ Encoding::BitField(28, Some(&(2, Encoding::UInt))),
+ ],
+ &Encoding::Sel,
+ #[cfg(target_pointer_width = "64")]
+ ":25@?0b18b29b310b611b812b4213b2I2821",
+ #[cfg(target_pointer_width = "32")]
+ ":21@?0b14b25b36b67b88b429b2I2817",
+ ),
+ ] {
+ assert_eq!(
+ block_signature_string_inner(args, ret),
+ CString::new(val.to_owned().into_bytes()).unwrap()
+ );
+ }
+ }
+}
diff --git a/third_party/rust/block2/src/ffi.rs b/third_party/rust/block2/src/ffi.rs
@@ -0,0 +1,175 @@
+//! # Raw bindings to `Block.h`
+
+use core::cell::UnsafeCell;
+use core::ffi::c_int;
+use core::ffi::c_void;
+use core::marker::{PhantomData, PhantomPinned};
+
+/// Type for block class ISAs.
+///
+/// This will likely become an extern type in the future.
+#[repr(C)]
+#[allow(missing_debug_implementations)]
+pub struct Class {
+ /// The size probably doesn't really matter here, as we only ever use the
+ /// classes behind pointers, but let's import it with the correct size to
+ /// be sure.
+ ///
+ /// This applies with the compiler-rt runtime and with Apple's runtime.
+ #[cfg(not(any(feature = "gnustep-1-7", feature = "unstable-objfw")))]
+ _priv: [*mut c_void; 32],
+
+ /// The size of this is unknown, so let's use a ZST so the compiler
+ /// doesn't assume anything about the size.
+ #[cfg(any(feature = "gnustep-1-7", feature = "unstable-objfw"))]
+ _priv: [u8; 0],
+
+ /// Mark as `!Send + !Sync + !Unpin` and as mutable behind shared
+ /// references (`!Freeze`).
+ ///
+ /// Same as `objc2::ffi::OpaqueData`.
+ _opaque: UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>,
+}
+
+// Use `extern "C-unwind"`, runtime functions may call external routines.
+extern "C-unwind" {
+ /// Class ISA used for global blocks.
+ pub static _NSConcreteGlobalBlock: Class;
+
+ /// Class ISA used for stack blocks.
+ pub static _NSConcreteStackBlock: Class;
+
+ /// Copy/retain a block.
+ ///
+ /// When called on a:
+ /// - Global block: Does nothing.
+ /// - Stack block: `memmove`s the block to a new heap allocation, calls
+ /// the copy helper, and returns the new malloc block.
+ /// - Malloc block: Increments the retain count.
+ ///
+ /// Returns `NULL` on allocation failure.
+ #[doc(alias = "Block_copy")]
+ pub fn _Block_copy(block: *const c_void) -> *mut c_void;
+
+ /// Release a block.
+ ///
+ /// When called on a:
+ /// - Global block: Does nothing.
+ /// - Stack block: Does nothing.
+ /// - Malloc block: Decrements the retain count, and if it reaches zero,
+ /// calls the dispose helper and frees the underlying storage.
+ #[doc(alias = "Block_release")]
+ pub fn _Block_release(block: *const c_void);
+
+ /// Copy a block field or `__block` variable from one location to another.
+ ///
+ /// Called by C compilers to clone fields inside copy helper routines, and
+ /// to handle memory management of `__block` marked variables.
+ pub fn _Block_object_assign(dest_addr: *mut c_void, object: *const c_void, flags: c_int);
+
+ /// Dispose an object previously copied using `_Block_object_assign`.
+ ///
+ /// Called by C compilers to drop fields inside dispose helper routines,
+ /// and handle memory management of `__block` marked variables.
+ pub fn _Block_object_dispose(object: *const c_void, flags: c_int);
+}
+
+/// `Block_private.h`
+#[allow(missing_docs)]
+#[cfg(any(test, feature = "unstable-private"))]
+pub mod private {
+ use super::*;
+ #[cfg(any(doc, target_vendor = "apple", feature = "gnustep-1-7"))]
+ use core::ffi::c_char;
+ #[cfg(any(doc, target_vendor = "apple", feature = "compiler-rt"))]
+ use core::ffi::c_ulong;
+
+ extern "C-unwind" {
+ pub static _NSConcreteMallocBlock: Class;
+ #[cfg(any(doc, target_vendor = "apple", feature = "compiler-rt"))]
+ pub static _NSConcreteAutoBlock: Class;
+ #[cfg(any(doc, target_vendor = "apple", feature = "compiler-rt"))]
+ pub static _NSConcreteFinalizingBlock: Class;
+ #[cfg(any(doc, target_vendor = "apple", feature = "compiler-rt"))]
+ pub static _NSConcreteWeakBlockVariable: Class;
+
+ #[cfg(any(doc, target_vendor = "apple", feature = "compiler-rt"))]
+ pub fn Block_size(block: *mut c_void) -> c_ulong; // usize
+
+ // Whether the return value of the block is on the stack.
+ // macOS 10.7
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn _Block_use_stret(block: *mut c_void) -> bool;
+
+ // Returns a string describing the block's GC layout.
+ // This uses the GC skip/scan encoding.
+ // May return NULL.
+ // macOS 10.7
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn _Block_layout(block: *mut c_void) -> *const c_char;
+
+ // Returns a string describing the block's layout.
+ // This uses the "extended layout" form described above.
+ // May return NULL.
+ // macOS 10.8
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn _Block_extended_layout(block: *mut c_void) -> *const c_char;
+
+ // Callable only from the ARR weak subsystem while in exclusion zone
+ // macOS 10.7
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn _Block_tryRetain(block: *const c_void) -> bool;
+
+ // Callable only from the ARR weak subsystem while in exclusion zone
+ // macOS 10.7
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn _Block_isDeallocating(block: *const c_void) -> bool;
+
+ // indicates whether block was compiled with compiler that sets the ABI
+ // related metadata bits
+ // macOS 10.7
+ #[cfg(any(doc, target_vendor = "apple", feature = "gnustep-1-7"))]
+ pub fn _Block_has_signature(block: *mut c_void) -> bool;
+
+ // Returns a string describing the block's parameter and return types.
+ // The encoding scheme is the same as Objective-C @encode.
+ // Returns NULL for blocks compiled with some compilers.
+ // macOS 10.7
+ #[cfg(any(doc, target_vendor = "apple", feature = "gnustep-1-7"))]
+ pub fn _Block_signature(block: *mut c_void) -> *const c_char;
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use core::ptr;
+
+ #[test]
+ fn smoke() {
+ assert_eq!(unsafe { _Block_copy(ptr::null_mut()) }, ptr::null_mut());
+ unsafe { _Block_release(ptr::null_mut()) };
+ }
+
+ #[test]
+ #[allow(unused_unsafe)]
+ #[cfg(feature = "std")]
+ fn test_linkable() {
+ use std::println;
+ println!("{:?}", unsafe { ptr::addr_of!(_NSConcreteGlobalBlock) });
+ println!("{:?}", unsafe { ptr::addr_of!(_NSConcreteStackBlock) });
+ println!("{:?}", unsafe {
+ ptr::addr_of!(private::_NSConcreteMallocBlock)
+ });
+ println!("{:p}", _Block_copy as unsafe extern "C-unwind" fn(_) -> _);
+ println!(
+ "{:p}",
+ _Block_object_assign as unsafe extern "C-unwind" fn(_, _, _)
+ );
+ println!(
+ "{:p}",
+ _Block_object_dispose as unsafe extern "C-unwind" fn(_, _)
+ );
+ println!("{:p}", _Block_release as unsafe extern "C-unwind" fn(_));
+ }
+}
diff --git a/third_party/rust/block2/src/global.rs b/third_party/rust/block2/src/global.rs
@@ -0,0 +1,302 @@
+use core::ffi::c_ulong;
+use core::fmt;
+use core::marker::PhantomData;
+use core::mem;
+use core::mem::MaybeUninit;
+use core::ops::Deref;
+use core::ptr::{self, NonNull};
+
+use crate::abi::{BlockDescriptor, BlockDescriptorPtr, BlockFlags, BlockHeader};
+use crate::debug::debug_block_header;
+use crate::{Block, BlockFn};
+
+// TODO: Should this be a static to help the compiler deduplicating them?
+const GLOBAL_DESCRIPTOR: BlockDescriptor = BlockDescriptor {
+ reserved: 0,
+ size: mem::size_of::<BlockHeader>() as c_ulong,
+};
+
+/// A global Objective-C block that does not capture an environment.
+///
+/// This can be used as an optimization of [`RcBlock`] if your closure doesn't
+/// capture any variables.
+///
+/// This is a smart pointer that [`Deref`]s to [`Block`].
+///
+/// It can created and stored in static memory using the [`global_block!`]
+/// macro.
+///
+/// [`RcBlock`]: crate::RcBlock
+/// [`global_block!`]: crate::global_block
+#[repr(C)]
+pub struct GlobalBlock<F: ?Sized> {
+ header: BlockHeader,
+ // We don't store a function pointer, instead it is placed inside the
+ // invoke function.
+ f: PhantomData<F>,
+}
+
+// TODO: Add `Send + Sync` bounds once the block itself supports that.
+unsafe impl<F: ?Sized + BlockFn> Sync for GlobalBlock<F> {}
+unsafe impl<F: ?Sized + BlockFn> Send for GlobalBlock<F> {}
+
+// Note: We can't put correct bounds on A and R because we have a const fn,
+// and that's not allowed yet in our MSRV.
+//
+// Fortunately, we don't need them, since they're present on `Sync`, so
+// constructing the static in `global_block!` with an invalid `GlobalBlock`
+// triggers an error.
+impl<F: ?Sized> GlobalBlock<F> {
+ // TODO: Use new ABI with BLOCK_HAS_SIGNATURE
+ const FLAGS: BlockFlags = BlockFlags::BLOCK_IS_GLOBAL.union(BlockFlags::BLOCK_USE_STRET);
+
+ #[doc(hidden)]
+ #[allow(clippy::declare_interior_mutable_const)]
+ pub const __DEFAULT_HEADER: BlockHeader = BlockHeader {
+ // Populated in `global_block!`
+ isa: ptr::null_mut(),
+ flags: Self::FLAGS,
+ reserved: MaybeUninit::new(0),
+ // Populated in `global_block!`
+ invoke: None,
+ descriptor: BlockDescriptorPtr {
+ basic: &GLOBAL_DESCRIPTOR,
+ },
+ };
+
+ /// Use the [`global_block`] macro instead.
+ #[doc(hidden)]
+ #[inline]
+ pub const unsafe fn from_header(header: BlockHeader) -> Self {
+ Self {
+ header,
+ f: PhantomData,
+ }
+ }
+
+ // TODO: Add some constructor for when `F: Copy`.
+}
+
+impl<F: ?Sized + BlockFn> Deref for GlobalBlock<F> {
+ type Target = Block<F>;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ let ptr: NonNull<Self> = NonNull::from(self);
+ let ptr: NonNull<Block<F>> = ptr.cast();
+ // SAFETY: This has the same layout as `Block`
+ //
+ // A global block does not hold any data, so it is safe to call
+ // immutably.
+ unsafe { ptr.as_ref() }
+ }
+}
+
+impl<F: ?Sized> fmt::Debug for GlobalBlock<F> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut f = f.debug_struct("GlobalBlock");
+ debug_block_header(&self.header, &mut f);
+ f.finish_non_exhaustive()
+ }
+}
+
+/// Construct a static [`GlobalBlock`].
+///
+/// The syntax is similar to a static closure (except that all types have to
+/// be specified). Note that the block cannot capture its environment, its
+/// parameter types must be [`EncodeArgument`] and the return type must be
+/// [`EncodeReturn`].
+///
+/// [`EncodeArgument`]: objc2::encode::EncodeArgument
+/// [`EncodeReturn`]: objc2::encode::EncodeReturn
+///
+/// # Examples
+///
+/// ```
+/// use block2::global_block;
+/// global_block! {
+/// static MY_BLOCK = || -> i32 {
+/// 42
+/// };
+/// }
+/// assert_eq!(MY_BLOCK.call(()), 42);
+/// ```
+///
+/// ```
+/// use block2::global_block;
+/// global_block! {
+/// static ADDER_BLOCK = |x: i32, y: i32| -> i32 {
+/// x + y
+/// };
+/// }
+/// assert_eq!(ADDER_BLOCK.call((5, 7)), 12);
+/// ```
+///
+/// The following does not compile because [`Box`] is not [`EncodeReturn`]:
+///
+/// ```compile_fail,E0277
+/// use block2::global_block;
+/// global_block! {
+/// pub static BLOCK = |b: Box<i32>| {};
+/// }
+/// ```
+///
+/// This also doesn't work (yet), as blocks are overly restrictive about the
+/// lifetimes involved.
+///
+/// ```compile_fail
+/// use block2::global_block;
+/// global_block! {
+/// pub static BLOCK_WITH_LIFETIME = |x: &i32| -> i32 {
+/// *x + 42
+/// };
+/// }
+/// let x = 5;
+/// let res = BLOCK_WITH_LIFETIME.call((&x,));
+/// assert_eq!(res, 47);
+/// ```
+///
+/// There is also no way to get a block function that's generic over its
+/// parameters. One could imagine the following syntax would work, but it
+/// can't due to implementation limitations:
+///
+/// ```compile_fail
+/// use block2::global_block;
+/// global_block! {
+/// pub static BLOCK<T: Encode> = |b: T| {};
+/// }
+/// ```
+///
+/// [`Box`]: alloc::boxed::Box
+#[macro_export]
+macro_rules! global_block {
+ // `||` is parsed as one token
+ (
+ $(#[$m:meta])*
+ $vis:vis static $name:ident = || $(-> $r:ty)? $body:block $(;)?
+ ) => {
+ $crate::global_block!(
+ $(#[$m])*
+ $vis static $name = |,| $(-> $r)? $body
+ );
+ };
+ (
+ $(#[$m:meta])*
+ $vis:vis static $name:ident = |$($a:ident: $t:ty),* $(,)?| $(-> $r:ty)? $body:block $(;)?
+ ) => {
+ $(#[$m])*
+ #[allow(unused_unsafe)]
+ $vis static $name: $crate::GlobalBlock<dyn Fn($($t),*) $(-> $r)? + 'static> = unsafe {
+ let mut header = $crate::GlobalBlock::<dyn Fn($($t),*) $(-> $r)? + 'static>::__DEFAULT_HEADER;
+ header.isa = ::core::ptr::addr_of!($crate::ffi::_NSConcreteGlobalBlock);
+ header.invoke = ::core::option::Option::Some({
+ unsafe extern "C-unwind" fn inner(
+ _: *mut $crate::GlobalBlock<dyn Fn($($t),*) $(-> $r)? + 'static>,
+ $($a: $t),*
+ ) $(-> $r)? {
+ $body
+ }
+
+ // TODO: SAFETY
+ ::core::mem::transmute::<
+ unsafe extern "C-unwind" fn(*mut $crate::GlobalBlock<dyn Fn($($t),*) $(-> $r)? + 'static>, $($a: $t),*) $(-> $r)?,
+ unsafe extern "C-unwind" fn(),
+ >(inner)
+ });
+ $crate::GlobalBlock::from_header(header)
+ };
+ };
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use alloc::format;
+
+ global_block! {
+ /// Test comments and visibility
+ pub(super) static NOOP_BLOCK = || {};
+ }
+
+ global_block! {
+ /// Multiple parameters + trailing comma
+ #[allow(unused)]
+ static BLOCK = |x: i32, y: i32, z: i32, w: i32,| -> i32 {
+ x + y + z + w
+ };
+ }
+
+ #[test]
+ fn test_noop_block() {
+ NOOP_BLOCK.call(());
+ }
+
+ #[test]
+ fn test_defined_in_function() {
+ global_block!(static MY_BLOCK = || -> i32 {
+ 42
+ });
+ assert_eq!(MY_BLOCK.call(()), 42);
+ }
+
+ #[cfg(target_vendor = "apple")]
+ const DEBUG_BLOCKFLAGS: &str = r#"BlockFlags {
+ value: "00110000000000000000000000000000",
+ deallocating: false,
+ inline_layout_string: false,
+ small_descriptor: false,
+ is_noescape: false,
+ needs_free: false,
+ has_copy_dispose: false,
+ has_ctor: false,
+ is_gc: false,
+ is_global: true,
+ use_stret: true,
+ has_signature: false,
+ has_extended_layout: false,
+ over_referenced: false,
+ reference_count: 0,
+ ..
+ }"#;
+
+ #[cfg(not(target_vendor = "apple"))]
+ const DEBUG_BLOCKFLAGS: &str = r#"BlockFlags {
+ value: "00110000000000000000000000000000",
+ has_copy_dispose: false,
+ has_ctor: false,
+ is_global: true,
+ use_stret: true,
+ has_signature: false,
+ over_referenced: false,
+ reference_count: 0,
+ ..
+ }"#;
+
+ #[test]
+ fn test_debug() {
+ let invoke = NOOP_BLOCK.header.invoke.unwrap();
+ let size = mem::size_of::<BlockHeader>();
+ let maybeuninit = <MaybeUninit<i32>>::uninit();
+ let expected = format!(
+ "GlobalBlock {{
+ isa: _NSConcreteGlobalBlock,
+ flags: {DEBUG_BLOCKFLAGS},
+ reserved: {maybeuninit:?},
+ invoke: Some(
+ {invoke:#?},
+ ),
+ descriptor: BlockDescriptor {{
+ reserved: 0,
+ size: {size},
+ }},
+ ..
+}}"
+ );
+ assert_eq!(format!("{NOOP_BLOCK:#?}"), expected);
+ }
+
+ #[allow(dead_code)]
+ fn covariant<'f>(b: GlobalBlock<dyn Fn() + 'static>) -> GlobalBlock<dyn Fn() + 'f> {
+ b
+ }
+}
diff --git a/third_party/rust/block2/src/lib.rs b/third_party/rust/block2/src/lib.rs
@@ -0,0 +1,456 @@
+//! # Apple's C language extension of blocks
+//!
+//! C Blocks are functions which capture their environments, i.e. the
+//! C-equivalent of Rust's [`Fn`] closures. As they were originally developed
+//! by Apple, they're often used in Objective-C code. This crate provides
+//! capabilities to create, manage and invoke these blocks, in an ergonomic,
+//! "Rust-centric" fashion.
+//!
+//! At a high level, this crate contains four types, each representing
+//! different kinds of blocks, and different kinds of ownership.
+//!
+//! | `block2` type | Equivalent Rust type |
+//! | ---------------------------------------- | --------------------- |
+//! | `&Block<dyn Fn() + 'a>` | `&dyn Fn() + 'a` |
+//! | `RcBlock<dyn Fn() + 'a>` | `Arc<dyn Fn() + 'a>` |
+//! | `StackBlock<'a, (), (), impl Fn() + 'a>` | `impl Fn() + 'a` |
+//! | `GlobalBlock<dyn Fn()>` | [`fn` item] |
+//!
+//! For more information on the specifics of the block implementation, see the
+//! [C language specification][lang] and the [ABI specification][ABI].
+//!
+//! [lang]: https://clang.llvm.org/docs/BlockLanguageSpec.html
+//! [ABI]: http://clang.llvm.org/docs/Block-ABI-Apple.html
+//! [`fn` item]: https://doc.rust-lang.org/reference/types/function-item.html
+//!
+//!
+//! ## Using blocks
+//!
+//! You can create a new block from a closure using [`RcBlock::new`]. This can
+//! then be used to call functions or Objective-C methods that takes a block:
+//!
+//! ```
+//! use block2::RcBlock;
+//! #
+//! # struct ExampleObject;
+//! #
+//! # impl ExampleObject {
+//! # fn someMethod(&self, block: &block2::Block<dyn Fn(i32, i32) -> i32>) {
+//! # assert_eq!(block.call((5, 8)), 18);
+//! # }
+//! # }
+//! #
+//! # let obj = ExampleObject;
+//!
+//! let val = 5;
+//! let block = RcBlock::new(move |a, b| a + b + val);
+//! obj.someMethod(&block);
+//! ```
+//!
+//!
+//! ## My block isn't being run?
+//!
+//! Most of the time, blocks are used to do asynchronous work; but just like
+//! futures in Rust don't do anything unless polled, a lot of Apple APIs won't
+//! call your block unless a [run loop][run_loop] is active, see that link for
+//! more information on how to do so.
+//!
+//! [run_loop]: objc2::topics::run_loop
+//!
+//!
+//! ## Lifetimes
+//!
+//! When dealing with blocks, there can be quite a few lifetimes to keep in
+//! mind.
+//!
+//! The most important one is the lifetime of the block's data, i.e. the
+//! lifetime of the data in the closure contained in the block. This lifetime
+//! can be specified as `'f` in `&Block<dyn Fn() + 'f>`.
+//!
+//! Note that `&Block<dyn Fn()>`, without any lifetime specifier, can be a bit
+//! confusing, as the default depends on where it is typed. In function/method
+//! signatures, it defaults to `'static`, but as the type of e.g. a `let`
+//! binding, the lifetime may be inferred to be something smaller, see [the
+//! reference][ref-dyn-lifetime] for details. If in doubt, either add a
+//! `+ 'static` or `+ '_` to force an escaping or non-escaping block.
+//!
+//! Another lifetime is the lifetime of the currently held pointer, i.e. `'b`
+//! in `&'b Block<dyn Fn()>`. This lifetime can be safely extended using
+//! [`Block::copy`], so should prove to be little trouble (of course the
+//! lifetime still can't be extended past the lifetime of the captured data
+//! above).
+//!
+//! Finally, the block's parameter and return types can also contain
+//! lifetimes, as `'a` and `'r` in `&Block<dyn Fn(&'a i32) -> &'r u32>`.
+//! Unfortunately, these lifetimes are quite problematic and unsupported at
+//! the moment, due to Rust trait limitations regarding higher-ranked trait
+//! bounds. If you run into problems with this in a block that takes or
+//! returns a reference, consider using the ABI-compatible `NonNull<T>`, or
+//! transmute to a `'static` lifetime.
+//!
+//! [ref-dyn-lifetime]: https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes
+//!
+//!
+//! ## Thread safety
+//!
+//! Thread-safe blocks are not yet representable in `block2`, and as such any
+//! function that requires a thread-safe block must be marked `unsafe`.
+//!
+//!
+//! ## Mutability
+//!
+//! Blocks are generally assumed to be shareable, and as such can only very
+//! rarely be made mutable.
+//!
+//! You will likely have to use interior mutability helpers like [`RefCell`]
+//! or [`Cell`] instead, see below.
+//!
+//! [`RefCell`]: core::cell::RefCell
+//! [`Cell`]: core::cell::Cell
+//!
+//!
+//! ### Transforming [`FnMut`] to a block
+//!
+//! Mutable closures differs from immutable ones in part in that they need to
+//! avoid re-entrancy.
+//!
+//! The below example transforms [`FnMut`] to [`Fn`] using a [`RefCell`]. We
+//! do not include this function as part of the public API of `block2`, as the
+//! specifics are very dependent on your use-case, and can be optimized with
+//! e.g. a [`Cell`] if your closure is [`Copy`] or if you do not care about
+//! unwind safety, or with [`UnsafeCell`] if you are able to unsafely
+//! guarantee the absence of re-entrancy.
+//!
+//! [`UnsafeCell`]: core::cell::UnsafeCell
+//!
+//! ```
+//! use std::cell::RefCell;
+//! use block2::RcBlock;
+//!
+//! fn fnmut_to_fn(closure: impl FnMut()) -> impl Fn() {
+//! let cell = RefCell::new(closure);
+//!
+//! move || {
+//! let mut closure = cell.try_borrow_mut().expect("re-entrant call");
+//! (closure)()
+//! }
+//! }
+//!
+//! let mut x = 0;
+//! let b = RcBlock::new(fnmut_to_fn(|| {
+//! x += 1;
+//! }));
+//! b.call(());
+//! b.call(());
+//! drop(b);
+//! assert_eq!(x, 2);
+//! ```
+//!
+//!
+//! ### Transforming [`FnOnce`] to a block
+//!
+//! [`FnOnce`] is similar to [`FnMut`] in that we must protect against
+//! re-entrancy, with the addition that it can also only be called once.
+//!
+//! Ensuring that it can be called once can be done by taking the closure
+//! out of an [`Option`] as shown in the example below. We can use [`Cell`]
+//! instead of [`RefCell`] here, since we never need to put the closure "back"
+//! for later use (like we need to do with `FnMut` above).
+//!
+//! In certain cases you may be able to do micro-optimizations, namely to use
+//! a [`ManuallyDrop`], if you wanted to optimize with the assumption that the
+//! block is always called, or [`unwrap_unchecked`] if you wanted to optimize
+//! with the assumption that it is only called once.
+//!
+//! [`Cell`]: core::cell::Cell
+//! [`ManuallyDrop`]: core::mem::ManuallyDrop
+//! [`unwrap_unchecked`]: core::option::Option::unwrap_unchecked
+//!
+//! ```
+//! use std::cell::Cell;
+//! use block2::RcBlock;
+//!
+//! fn fnonce_to_fn(closure: impl FnOnce()) -> impl Fn() {
+//! let cell = Cell::new(Some(closure));
+//! move || {
+//! let closure = cell.take().expect("called twice");
+//! closure()
+//! }
+//! }
+//!
+//! let v = vec![1, 2, 3];
+//! let b = RcBlock::new(fnonce_to_fn(move || {
+//! drop(v);
+//! }));
+//! b.call(());
+//! ```
+//!
+//!
+//! ## External functions using blocks
+//!
+//! To declare external functions or methods that takes blocks, use
+//! `&Block<dyn Fn(Params) -> R>` or `Option<&Block<dyn Fn(Args) -> R>>`,
+//! where `Params` is the parameter types, and `R` is the return type.
+//!
+//! For this example, consider the function `check_addition` which takes a
+//! single parameter, namely a block that adds two integers, and then checks
+//! that the addition is correct.
+//!
+//! Such a function could be written in C like in the following.
+//!
+//! ```objc
+//! #include <cassert>
+//! #include <stdint.h>
+//! #include <Block.h>
+//!
+//! void check_addition(int32_t (^block)(int32_t, int32_t)) {
+//! assert(block(5, 8) == 13);
+//! }
+//! ```
+//!
+//! An `extern "C" { ... }` declaration for that function would then be:
+//!
+//! ```
+//! use block2::Block;
+//!
+//! extern "C" {
+//! fn check_addition(block: &Block<dyn Fn(i32, i32) -> i32>);
+//! }
+//! ```
+//!
+//! This can similarly be done for Objcective-C methods declared with
+//! [`objc2::extern_methods!`] (though most of the time, the [framework
+//! crates][framework-crates] will take care of that for you).
+//!
+//! ```
+//! use block2::Block;
+//! use objc2::extern_methods;
+//! #
+//! # use objc2::ClassType;
+//! # objc2::extern_class!(
+//! # #[unsafe(super(objc2::runtime::NSObject))]
+//! # #[name = "NSObject"]
+//! # struct MyClass;
+//! # );
+//!
+//! impl MyClass {
+//! extern_methods!(
+//! #[unsafe(method(checkAddition:))]
+//! pub fn checkAddition(&self, block: &Block<dyn Fn(i32, i32) -> i32>);
+//! );
+//! }
+//! ```
+//!
+//! If the function/method allows passing `NULL` blocks, the type should be
+//! `Option<&Block<dyn Fn(i32, i32) -> i32>>` instead.
+//!
+//! [framework-crates]: objc2::topics::about_generated
+//!
+//!
+//! ## Invoking blocks
+//!
+//! We can also define the external function in Rust, and expose it to
+//! Objective-C. To do this, we can use [`Block::call`] to invoke the block
+//! inside the function.
+//!
+//! ```
+//! use block2::Block;
+//!
+//! #[no_mangle]
+//! extern "C" fn check_addition(block: &Block<dyn Fn(i32, i32) -> i32>) {
+//! assert_eq!(block.call((5, 8)), 13);
+//! }
+//! ```
+//!
+//! Note the extra parentheses in the `call` method, since the arguments must
+//! be passed as a tuple.
+//!
+//!
+//! ## Specifying a runtime
+//!
+//! Different runtime implementations exist and act in slightly different ways
+//! (and have several different helper functions), the most important aspect
+//! being that the libraries are named differently, so we must take that into
+//! account when linking.
+//!
+//! You can choose the desired runtime by using the relevant cargo feature
+//! flags, see the following sections.
+//!
+//!
+//! ### Apple's [`libclosure`](https://github.com/apple-oss-distributions/libclosure)
+//!
+//! This is the most common and most sophisticated runtime, and it has quite a
+//! lot more features than the specification mandates.
+//!
+//! The minimum required operating system versions are as follows (though in
+//! practice Rust itself requires higher versions than this):
+//!
+//! - macOS: `10.6`
+//! - iOS/iPadOS: `3.2`
+//! - tvOS: `1.0`
+//! - watchOS: `1.0`
+//!
+//! **This is used by default**, so you do not need to specify a runtime if
+//! you're using this crate on of these platforms.
+//!
+//!
+//! ### LLVM `compiler-rt`'s [`libBlocksRuntime`](https://github.com/llvm/llvm-project/tree/release/13.x/compiler-rt/lib/BlocksRuntime)
+//!
+//! - Feature flag: `compiler-rt`.
+//!
+//! This is a copy of Apple's older (around macOS 10.6) runtime, and is now
+//! used in [Swift's `libdispatch`] and [Swift's Foundation] as well.
+//!
+//! The runtime and associated headers can be installed on many Linux systems
+//! with the `libblocksruntime-dev` package.
+//!
+//! Using this runtime probably won't work together with `objc2` crate.
+//!
+//! [Swift's `libdispatch`]: https://github.com/apple/swift-corelibs-libdispatch/tree/swift-5.5.1-RELEASE/src/BlocksRuntime
+//! [Swift's Foundation]: https://github.com/apple/swift-corelibs-foundation/tree/swift-5.5.1-RELEASE/Sources/BlocksRuntime
+//!
+//!
+//! ### GNUStep's [`libobjc2`](https://github.com/gnustep/libobjc2)
+//!
+//! - Feature flag: `gnustep-1-7`, `gnustep-1-8`, `gnustep-1-9`, `gnustep-2-0`
+//! and `gnustep-2-1` depending on the version you're using.
+//!
+//! GNUStep is a bit odd, because it bundles blocks support into its
+//! Objective-C runtime. This means we have to link to `libobjc`, and this is
+//! done by depending on the `objc2` crate. A bit unorthodox, yes, but it
+//! works.
+//!
+//! Sources:
+//!
+//! - [`Block.h`](https://github.com/gnustep/libobjc2/blob/v2.1/objc/blocks_runtime.h)
+//! - [`Block_private.h`](https://github.com/gnustep/libobjc2/blob/v2.1/objc/blocks_private.h)
+//!
+//!
+//! ### Microsoft's [`WinObjC`](https://github.com/microsoft/WinObjC)
+//!
+//! - Feature flag: `unstable-winobjc`.
+//!
+//! **Unstable: Hasn't been tested on Windows yet!**
+//!
+//! [A fork](https://github.com/microsoft/libobjc2) based on GNUStep's `libobjc2`
+//! version 1.8.
+//!
+//!
+//! ### [`ObjFW`](https://github.com/ObjFW/ObjFW)
+//!
+//! - Feature flag: `unstable-objfw`.
+//!
+//! **Unstable: Doesn't work yet!**
+//!
+//!
+//! ## C Compiler configuration
+//!
+//! To our knowledge, only Clang supports blocks. To compile C or Objective-C
+//! sources using these features, you should set [the `-fblocks` flag][flag].
+//!
+//! [flag]: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fblocks
+
+#![no_std]
+#![allow(rustdoc::broken_intra_doc_links)] // FIXME link to objc2::topics
+#![warn(missing_docs)]
+#![warn(missing_debug_implementations)]
+#![warn(clippy::missing_errors_doc)]
+#![warn(clippy::missing_panics_doc)]
+// Update in Cargo.toml as well.
+#![doc(html_root_url = "https://docs.rs/block2/0.6.2")]
+#![cfg_attr(docsrs, feature(doc_cfg))]
+#![cfg_attr(feature = "unstable-coerce-pointee", feature(derive_coerce_pointee))]
+
+#[cfg(not(feature = "alloc"))]
+compile_error!("The `alloc` feature currently must be enabled.");
+
+extern crate alloc;
+#[cfg(feature = "std")]
+extern crate std;
+
+#[cfg(all(
+ not(docsrs),
+ not(any(
+ target_vendor = "apple",
+ feature = "compiler-rt",
+ feature = "gnustep-1-7",
+ feature = "unstable-objfw",
+ ))
+))]
+compile_error!("`block2` only works on Apple platforms. Pass `--target aarch64-apple-darwin` or similar to compile for macOS.\n(If you're absolutely certain that you want to use `block2` on Linux/Windows, you can specify that with the `gnustep-x-y`/`compiler-rt` Cargo features instead).");
+
+#[cfg(any(
+ all(feature = "compiler-rt", feature = "gnustep-1-7"),
+ all(feature = "gnustep-1-7", feature = "unstable-objfw"),
+ all(feature = "compiler-rt", feature = "unstable-objfw"),
+))]
+compile_error!("Only one runtime may be selected");
+
+#[cfg(feature = "unstable-objfw")]
+compile_error!("ObjFW is not yet supported");
+
+// Link to `libclosure` (internally called `libsystem_blocks.dylib`), which is
+// exported by `libSystem.dylib`.
+//
+// Note: Don't get confused by the presence of `System.framework`, it is a
+// deprecated wrapper over the dynamic library, so we'd rather use the latter.
+//
+// Alternative: Only link to `libsystem_blocks.dylib`:
+// println!("cargo:rustc-link-search=native=/usr/lib/system");
+// println!("cargo:rustc-link-lib=dylib=system_blocks");
+#[cfg_attr(
+ all(
+ target_vendor = "apple",
+ not(feature = "compiler-rt"),
+ not(feature = "gnustep-1-7"),
+ not(feature = "unstable-objfw"),
+ not(feature = "std"), // `libSystem` is already linked by `libstd`.
+ ),
+ link(name = "System", kind = "dylib")
+)]
+// Link to `libBlocksRuntime`.
+#[cfg_attr(feature = "compiler-rt", link(name = "BlocksRuntime", kind = "dylib"))]
+// Link to `libobjfw`, which provides the blocks implementation.
+#[cfg_attr(feature = "unstable-objfw", link(name = "objfw", kind = "dylib"))]
+extern "C" {}
+
+// Don't link to anything on GNUStep; objc2 already does that for us!
+//
+// We do want to ensure linkage actually happens, though.
+#[cfg(feature = "gnustep-1-7")]
+extern crate objc2 as _;
+
+mod abi;
+mod block;
+mod debug;
+mod encoding;
+pub mod ffi;
+mod global;
+mod rc_block;
+mod stack;
+mod traits;
+
+pub use self::block::Block;
+pub use self::global::GlobalBlock;
+pub use self::rc_block::RcBlock;
+pub use self::stack::StackBlock;
+pub use self::traits::{BlockFn, IntoBlock, ManualBlockEncoding};
+
+/// Deprecated alias for a `'static` `StackBlock`.
+#[deprecated = "renamed to `StackBlock`"]
+pub type ConcreteBlock<A, R, Closure> = StackBlock<'static, A, R, Closure>;
+
+// Note: We could use `_Block_object_assign` and `_Block_object_dispose` to
+// implement a `ByRef<T>` wrapper, which would behave like `__block` marked
+// variables and have roughly the same memory management scheme as blocks.
+//
+// But I've yet to see the value in such a wrapper in Rust code compared to
+// just using `Box`, `Rc` or `Arc`, and since `__block` variables are
+// basically never exposed as part of a (public) function's API, we won't
+// implement such a thing yet.
+
+/// Helper type to allow changing [`Block`] in the future without affecting
+/// framework crates.
+///
+/// Tracked in [#572](https://github.com/madsmtm/objc2/issues/572).
+pub type DynBlock<F> = crate::Block<F>;
diff --git a/third_party/rust/block2/src/rc_block.rs b/third_party/rust/block2/src/rc_block.rs
@@ -0,0 +1,365 @@
+use core::fmt;
+use core::mem::ManuallyDrop;
+use core::ops::Deref;
+use core::ptr::NonNull;
+
+use objc2::encode::{EncodeArguments, EncodeReturn};
+
+use crate::abi::BlockHeader;
+use crate::debug::debug_block_header;
+use crate::traits::{ManualBlockEncoding, ManualBlockEncodingExt, NoBlockEncoding, UserSpecified};
+use crate::{ffi, Block, IntoBlock, StackBlock};
+
+/// A reference-counted Objective-C block that is stored on the heap.
+///
+/// This is a smart pointer that [`Deref`]s to [`Block`].
+///
+/// The generic type `F` must be a [`dyn`] [`Fn`] that implements the
+/// [`BlockFn`] trait, just like described in [`Block`]'s documentation.
+///
+/// [`dyn`]: https://doc.rust-lang.org/std/keyword.dyn.html
+/// [`BlockFn`]: crate::BlockFn
+///
+///
+/// # Memory-layout
+///
+/// This is guaranteed to have the same size and alignment as a pointer to a
+/// block (i.e. same size as `*const Block<A, R>`).
+///
+/// Additionally, it participates in the null-pointer optimization, that is,
+/// `Option<RcBlock<A, R>>` is guaranteed to have the same size as
+/// `RcBlock<A, R>`.
+#[repr(transparent)]
+#[doc(alias = "MallocBlock")]
+#[cfg_attr(
+ feature = "unstable-coerce-pointee",
+ derive(std::marker::CoercePointee)
+)]
+pub struct RcBlock<F: ?Sized> {
+ // Covariant
+ ptr: NonNull<Block<F>>,
+}
+
+impl<F: ?Sized> RcBlock<F> {
+ /// A raw pointer to the underlying block.
+ ///
+ /// The pointer is valid for at least as long as the `RcBlock` is alive.
+ ///
+ /// This is an associated method, and must be called as
+ /// `RcBlock::as_ptr(&block)`.
+ #[inline]
+ pub fn as_ptr(this: &Self) -> *mut Block<F> {
+ this.ptr.as_ptr()
+ }
+
+ /// Consumes the `RcBlock`, passing ownership of the retain count to the
+ /// caller.
+ ///
+ /// After calling this function, the caller is responsible for releasing
+ /// the memory with [`ffi::_Block_release`] or similar.
+ ///
+ /// This is an associated method, and must be called as
+ /// `RcBlock::into_raw(block)`.
+ ///
+ ///
+ /// # Examples
+ ///
+ /// Converting a `RcBlock` to a pointer and back.
+ ///
+ /// ```
+ /// use block2::RcBlock;
+ ///
+ /// let add2 = RcBlock::new(|x: i32| -> i32 {
+ /// x + 2
+ /// });
+ /// let ptr = RcBlock::into_raw(add2);
+ /// // SAFETY: The pointer is valid, and ownership from above.
+ /// let add2 = unsafe { RcBlock::from_raw(ptr) }.unwrap();
+ /// ```
+ pub fn into_raw(this: Self) -> *mut Block<F> {
+ let this = ManuallyDrop::new(this);
+ this.ptr.as_ptr()
+ }
+
+ /// Construct an `RcBlock` from the given block pointer by taking
+ /// ownership.
+ ///
+ /// This will return `None` if the pointer is NULL.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The given pointer must point to a valid block, the parameter and
+ /// return types must be correct, and the block must have a +1 reference /
+ /// retain count from somewhere else.
+ ///
+ /// Additionally, the block must be safe to call (or, if it is not, then
+ /// you must treat every call to the block as `unsafe`).
+ #[inline]
+ pub unsafe fn from_raw(ptr: *mut Block<F>) -> Option<Self> {
+ NonNull::new(ptr).map(|ptr| Self { ptr })
+ }
+
+ /// Construct an `RcBlock` from the given block pointer.
+ ///
+ /// The block will be copied, and have its reference-count increased by
+ /// one.
+ ///
+ /// This will return `None` if the pointer is NULL, or if an allocation
+ /// failure occurred.
+ ///
+ /// See [`Block::copy`] for a safe alternative when you already know the
+ /// block pointer is valid.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The given pointer must point to a valid block, and the parameter and
+ /// return types must be correct.
+ ///
+ /// Additionally, the block must be safe to call (or, if it is not, then
+ /// you must treat every call to the block as `unsafe`).
+ #[doc(alias = "Block_copy")]
+ #[doc(alias = "_Block_copy")]
+ #[inline]
+ pub unsafe fn copy(ptr: *mut Block<F>) -> Option<Self> {
+ let ptr: *mut Block<F> = unsafe { ffi::_Block_copy(ptr.cast()) }.cast();
+ // SAFETY: We just copied the block, so the reference count is +1
+ unsafe { Self::from_raw(ptr) }
+ }
+}
+
+// TODO: Move so this appears first in the docs.
+impl<F: ?Sized> RcBlock<F> {
+ /// Construct a `RcBlock` with the given closure.
+ ///
+ /// The closure will be coped to the heap on construction.
+ ///
+ /// When the block is called, it will return the value that results from
+ /// calling the closure.
+ // Note: Unsure if this should be #[inline], but I think it may be able to
+ // benefit from not being completely so.
+ #[inline]
+ pub fn new<'f, A, R, Closure>(closure: Closure) -> Self
+ where
+ A: EncodeArguments,
+ R: EncodeReturn,
+ Closure: IntoBlock<'f, A, R, Dyn = F>,
+ {
+ Self::maybe_encoded::<_, _, _, NoBlockEncoding<A, R>>(closure)
+ }
+
+ /// Constructs a new [`RcBlock`] with the given function and encoding
+ /// information.
+ ///
+ /// See [`StackBlock::with_encoding`] as to why and how this could be
+ /// useful. The same requirements as [`Self::new`] apply here as well.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # use core::ffi::CStr;
+ /// # use block2::{Block, ManualBlockEncoding, RcBlock};
+ /// # use objc2_foundation::NSError;
+ /// #
+ /// struct MyBlockEncoding;
+ /// // SAFETY: The encoding is correct.
+ /// unsafe impl ManualBlockEncoding for MyBlockEncoding {
+ /// type Arguments = (*mut NSError,);
+ /// type Return = i32;
+ /// const ENCODING_CSTR: &'static CStr = if cfg!(target_pointer_width = "64") {
+ /// cr#"i16@?0@"NSError"8"#
+ /// } else {
+ /// cr#"i8@?0@"NSError"4"#
+ /// };
+ /// }
+ ///
+ /// let my_block = RcBlock::with_encoding::<_, _, _, MyBlockEncoding>(|_err: *mut NSError| {
+ /// 42i32
+ /// });
+ /// assert_eq!(my_block.call((core::ptr::null_mut(),)), 42);
+ /// ```
+ #[inline]
+ pub fn with_encoding<'f, A, R, Closure, E>(closure: Closure) -> Self
+ where
+ A: EncodeArguments,
+ R: EncodeReturn,
+ Closure: IntoBlock<'f, A, R, Dyn = F>,
+ E: ManualBlockEncoding<Arguments = A, Return = R>,
+ {
+ Self::maybe_encoded::<_, _, _, UserSpecified<E>>(closure)
+ }
+
+ fn maybe_encoded<'f, A, R, Closure, E>(closure: Closure) -> Self
+ where
+ A: EncodeArguments,
+ R: EncodeReturn,
+ Closure: IntoBlock<'f, A, R, Dyn = F>,
+ E: ManualBlockEncodingExt<Arguments = A, Return = R>,
+ {
+ // SAFETY: The stack block is copied once below.
+ //
+ // Note: We could theoretically use `_NSConcreteMallocBlock`, and use
+ // `malloc` ourselves to put the block on the heap, but that symbol is
+ // not part of the public ABI, and may break in the future.
+ //
+ // Clang doesn't do this optimization either.
+ // <https://github.com/llvm/llvm-project/blob/llvmorg-17.0.6/clang/lib/CodeGen/CGBlocks.cpp#L281-L284>
+ let block = unsafe { StackBlock::new_no_clone::<E>(closure) };
+
+ // Transfer ownership from the stack to the heap.
+ let mut block = ManuallyDrop::new(block);
+ let ptr: *mut StackBlock<'f, A, R, Closure> = &mut *block;
+ let ptr: *mut Block<F> = ptr.cast();
+ // SAFETY: The block will be moved to the heap, and we forget the
+ // original block because the heap block will drop in our dispose
+ // helper.
+ unsafe { Self::copy(ptr) }.unwrap_or_else(|| rc_new_fail())
+ }
+}
+
+impl<F: ?Sized> Clone for RcBlock<F> {
+ /// Increase the reference-count of the block.
+ #[doc(alias = "Block_copy")]
+ #[doc(alias = "_Block_copy")]
+ #[inline]
+ fn clone(&self) -> Self {
+ // SAFETY: The block pointer is valid, and its safety invariant is
+ // upheld, since the only way to get an `RcBlock` in the first place
+ // is through unsafe functions that requires these preconditions to be
+ // upheld.
+ unsafe { Self::copy(self.ptr.as_ptr()) }.unwrap_or_else(|| rc_clone_fail())
+ }
+}
+
+// Intentionally not `#[track_caller]`, to keep the code-size smaller (as this
+// error is very unlikely).
+fn rc_new_fail() -> ! {
+ // This likely means the system is out of memory.
+ panic!("failed creating RcBlock")
+}
+
+// Intentionally not `#[track_caller]`, see above.
+pub(crate) fn block_copy_fail() -> ! {
+ // This likely means the system is out of memory.
+ panic!("failed copying Block")
+}
+
+// Intentionally not `#[track_caller]`, see above.
+fn rc_clone_fail() -> ! {
+ unreachable!("cloning a RcBlock bumps the reference count, which should be infallible")
+}
+
+impl<F: ?Sized> Deref for RcBlock<F> {
+ type Target = Block<F>;
+
+ #[inline]
+ fn deref(&self) -> &Block<F> {
+ // SAFETY: The pointer is valid, as ensured by creation methods, and
+ // will be so for as long as the `RcBlock` is, since that holds +1
+ // reference count.
+ unsafe { self.ptr.as_ref() }
+ }
+}
+
+impl<F: ?Sized> Drop for RcBlock<F> {
+ /// Release the block, decreasing the reference-count by 1.
+ ///
+ /// The `Drop` method of the underlying closure will be called once the
+ /// reference-count reaches zero.
+ #[doc(alias = "Block_release")]
+ #[doc(alias = "_Block_release")]
+ #[inline]
+ fn drop(&mut self) {
+ // SAFETY: The pointer has +1 reference count, as ensured by creation
+ // methods.
+ unsafe { ffi::_Block_release(self.ptr.as_ptr().cast()) };
+ }
+}
+
+impl<F: ?Sized> fmt::Debug for RcBlock<F> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut f = f.debug_struct("RcBlock");
+ let header = unsafe { self.ptr.cast::<BlockHeader>().as_ref() };
+ debug_block_header(header, &mut f);
+ f.finish_non_exhaustive()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use alloc::rc::Rc;
+ use core::cell::OnceCell;
+
+ use super::*;
+
+ #[test]
+ fn return_rc_block() {
+ fn get_adder(x: i32) -> RcBlock<dyn Fn(i32) -> i32> {
+ RcBlock::new(move |y| y + x)
+ }
+
+ let add2 = get_adder(2);
+ assert_eq!(add2.call((5,)), 7);
+ assert_eq!(add2.call((-1,)), 1);
+ }
+
+ #[test]
+ fn rc_block_with_precisely_described_lifetimes() {
+ fn args<'a, 'b>(
+ f: impl Fn(&'a i32, &'b i32) + 'static,
+ ) -> RcBlock<dyn Fn(&'a i32, &'b i32) + 'static> {
+ RcBlock::new(f)
+ }
+
+ fn args_return<'a, 'b>(
+ f: impl Fn(&'a i32) -> &'b i32 + 'static,
+ ) -> RcBlock<dyn Fn(&'a i32) -> &'b i32 + 'static> {
+ RcBlock::new(f)
+ }
+
+ fn args_entire<'a, 'b>(f: impl Fn(&'a i32) + 'b) -> RcBlock<dyn Fn(&'a i32) + 'b> {
+ RcBlock::new(f)
+ }
+
+ fn return_entire<'a, 'b>(
+ f: impl Fn() -> &'a i32 + 'b,
+ ) -> RcBlock<dyn Fn() -> &'a i32 + 'b> {
+ RcBlock::new(f)
+ }
+
+ let _ = args(|_, _| {});
+ let _ = args_return(|x| x);
+ let _ = args_entire(|_| {});
+ let _ = return_entire(|| &5);
+ }
+
+ #[allow(dead_code)]
+ fn covariant<'f>(b: RcBlock<dyn Fn() + 'static>) -> RcBlock<dyn Fn() + 'f> {
+ b
+ }
+
+ #[test]
+ fn allow_re_entrancy() {
+ #[allow(clippy::type_complexity)]
+ let block: Rc<OnceCell<RcBlock<dyn Fn(u32) -> u32>>> = Rc::new(OnceCell::new());
+
+ let captured_block = block.clone();
+ let fibonacci = move |n| {
+ let captured_fibonacci = captured_block.get().unwrap();
+ match n {
+ 0 => 0,
+ 1 => 1,
+ n => captured_fibonacci.call((n - 1,)) + captured_fibonacci.call((n - 2,)),
+ }
+ };
+
+ let block = block.get_or_init(|| RcBlock::new(fibonacci));
+
+ assert_eq!(block.call((0,)), 0);
+ assert_eq!(block.call((1,)), 1);
+ assert_eq!(block.call((6,)), 8);
+ assert_eq!(block.call((10,)), 55);
+ assert_eq!(block.call((19,)), 4181);
+ }
+}
diff --git a/third_party/rust/block2/src/stack.rs b/third_party/rust/block2/src/stack.rs
@@ -0,0 +1,536 @@
+use core::ffi::c_ulong;
+use core::ffi::c_void;
+use core::fmt;
+use core::marker::PhantomData;
+use core::mem::{self, MaybeUninit};
+use core::ops::Deref;
+use core::panic::{RefUnwindSafe, UnwindSafe};
+use core::ptr::{self, NonNull};
+
+use objc2::encode::{EncodeArguments, EncodeReturn, Encoding, RefEncode};
+
+use crate::abi::{
+ BlockDescriptor, BlockDescriptorCopyDispose, BlockDescriptorCopyDisposeSignature,
+ BlockDescriptorPtr, BlockDescriptorSignature, BlockFlags, BlockHeader,
+};
+use crate::debug::debug_block_header;
+use crate::traits::{ManualBlockEncoding, ManualBlockEncodingExt, NoBlockEncoding, UserSpecified};
+use crate::{ffi, Block, IntoBlock};
+
+/// An Objective-C block constructed on the stack.
+///
+/// This can be a micro-optimization if you know that the function you're
+/// passing the block to won't [copy] the block at all (e.g. if it guarantees
+/// that it'll run it synchronously). That's very rare though, most of the
+/// time you'll want to use [`RcBlock`].
+///
+/// This is a smart pointer that [`Deref`]s to [`Block`].
+///
+/// [copy]: Block::copy
+/// [`RcBlock`]: crate::RcBlock
+///
+///
+/// # Type parameters
+///
+/// The type parameters for this is a bit complex due to trait system
+/// limitations. Usually, you will not need to specify them, as the compiler
+/// should be able to infer them.
+///
+/// - The lifetime `'f`, which is the lifetime of the block.
+/// - The parameter `A`, which is a tuple representing the parameters to the
+/// block.
+/// - The parameter `R`, which is the return type of the block.
+/// - The parameter `Closure`, which is the contained closure type. This is
+/// usually not nameable, and you will have to use `_`, `impl Fn()` or
+/// similar.
+///
+///
+/// # Memory layout
+///
+/// The memory layout of this type is _not_ guaranteed.
+///
+/// That said, it will always be safe to reinterpret pointers to this as a
+/// pointer to a [`Block`] with the corresponding `dyn Fn` type.
+#[repr(C)]
+pub struct StackBlock<'f, A, R, Closure> {
+ /// For correct variance of the other type parameters.
+ ///
+ /// We add extra auto traits such that they depend on the closure instead.
+ p: PhantomData<dyn Fn(A) -> R + Send + Sync + RefUnwindSafe + UnwindSafe + Unpin + 'f>,
+ header: BlockHeader,
+ /// The block's closure.
+ ///
+ /// The ABI requires this field to come after the header.
+ ///
+ /// Note that this is not wrapped in a `ManuallyDrop`; once the
+ /// `StackBlock` is dropped, the closure will also be dropped.
+ pub(crate) closure: Closure,
+}
+
+// SAFETY: Pointers to the stack block is always safe to reinterpret as an
+// ordinary block pointer.
+unsafe impl<'f, A, R, Closure> RefEncode for StackBlock<'f, A, R, Closure>
+where
+ A: EncodeArguments,
+ R: EncodeReturn,
+ Closure: IntoBlock<'f, A, R>,
+{
+ const ENCODING_REF: Encoding = Encoding::Block;
+}
+
+// Basic constants and helpers.
+impl<A, R, Closure> StackBlock<'_, A, R, Closure> {
+ /// The size of the block header and the trailing closure.
+ ///
+ /// This ensures that the closure that the block contains is also moved to
+ /// the heap in `_Block_copy` operations.
+ const SIZE: c_ulong = mem::size_of::<Self>() as _;
+
+ // Drop the closure that this block contains.
+ unsafe extern "C-unwind" fn drop_closure(block: *mut c_void) {
+ let block: *mut Self = block.cast();
+ // When this function is called, the block no longer lives on the
+ // stack, it has been moved to the heap as part of some `_Block_copy`
+ // operation, including ownership over the block.
+ //
+ // It is our task to ensure that the closure's data is properly
+ // disposed, which we do by calling `Drop`.
+
+ // We use `addr_of_mut` here to not assume anything about the block's
+ // contents. This is probably overly cautious, `BlockHeader` already
+ // makes very few assumptions about the validity of the data it
+ // contains.
+ //
+ // SAFETY: The block pointer is valid, and contains the closure.
+ let closure = unsafe { ptr::addr_of_mut!((*block).closure) };
+ // SAFETY: The ownership of the closure was moved into the block as
+ // part of some `_Block_copy` operation, and as such it is valid to
+ // drop here.
+ unsafe { ptr::drop_in_place(closure) };
+ }
+
+ const DESCRIPTOR_BASIC: BlockDescriptor = BlockDescriptor {
+ reserved: 0,
+ size: Self::SIZE,
+ };
+}
+
+// `StackBlock::new`
+impl<A, R, Closure: Clone> StackBlock<'_, A, R, Closure> {
+ // Clone the closure from one block to another.
+ unsafe extern "C-unwind" fn clone_closure(dst: *mut c_void, src: *const c_void) {
+ let dst: *mut Self = dst.cast();
+ let src: *const Self = src.cast();
+ // When this function is called as part of some `_Block_copy`
+ // operation, the `dst` block has been constructed on the heap, and
+ // the `src` block has been `memmove`d to that.
+ //
+ // It is our task to ensure that the rest of the closure's data is
+ // properly copied, which we do by calling `Clone`. This newly cloned
+ // closure will be dropped in `drop_closure`.
+
+ // We use `addr_of[_mut]` to not assume anything about the block's
+ // contents, see `drop_closure` for details.
+ //
+ // SAFETY: The block pointers are valid, and each contain the closure.
+ let dst_closure = unsafe { ptr::addr_of_mut!((*dst).closure) };
+ let src_closure = unsafe { &*ptr::addr_of!((*src).closure) };
+ // SAFETY: `dst` is valid for writes.
+ // TODO: How do we ensure proper alignment?
+ //
+ // Note: This is slightly inefficient, as we're overwriting the
+ // already `memmove`d data once more, which is unnecessary for closure
+ // captures that implement `Copy`.
+ unsafe { ptr::write(dst_closure, src_closure.clone()) };
+ }
+
+ const DESCRIPTOR_WITH_CLONE: BlockDescriptorCopyDispose = BlockDescriptorCopyDispose {
+ reserved: 0,
+ size: Self::SIZE,
+ copy: Some(Self::clone_closure),
+ dispose: Some(Self::drop_closure),
+ };
+}
+
+impl<'f, A, R, Closure> StackBlock<'f, A, R, Closure>
+where
+ A: EncodeArguments,
+ R: EncodeReturn,
+ Closure: IntoBlock<'f, A, R> + Clone,
+{
+ /// Construct a `StackBlock` with the given closure.
+ ///
+ /// Note that this requires [`Clone`], as a C block is generally assumed
+ /// to be copy-able. If you want to avoid that, put the block directly on
+ /// the heap using [`RcBlock::new`].
+ ///
+ /// When the block is called, it will return the value that results from
+ /// calling the closure.
+ ///
+ /// [`RcBlock::new`]: crate::RcBlock::new
+ ///
+ ///
+ /// ## Example
+ ///
+ /// ```
+ /// use block2::StackBlock;
+ /// #
+ /// # extern "C" fn check_addition(block: &block2::Block<dyn Fn(i32, i32) -> i32>) {
+ /// # assert_eq!(block.call((5, 8)), 13);
+ /// # }
+ ///
+ /// let block = StackBlock::new(|a, b| a + b);
+ /// check_addition(&block);
+ /// ```
+ #[inline]
+ pub fn new(closure: Closure) -> Self {
+ Self::maybe_encoded::<NoBlockEncoding<A, R>>(closure)
+ }
+
+ /// Constructs a new [`StackBlock`] with the given function and encoding
+ /// information.
+ ///
+ /// Some particular newer-ish Apple Objective-C APIs expect the block they
+ /// are given to be created with encoding information set in the block
+ /// object itself and crash the calling process if they fail to find it,
+ /// which renders them pretty much unusable with only [`Self::new`] that
+ /// currently does not set such encoding information. This is for example
+ /// the case in [`FileProvider`] for [`NSFileProviderManager`]'s
+ /// [`reimportItemsBelowItemWithIdentifier:completionHandler:`] and
+ /// [`waitForStabilizationWithCompletionHandler:`], but also in
+ /// [`NetworkExtension`] for [`NEFilterDataProvider`]'s
+ /// [`applySettings:completionHandler`]. A complete list of such APIs may
+ /// not be easily obtained, though.
+ ///
+ /// This encoding information string could technically be generated at
+ /// compile time using the generic parameters already available to
+ /// [`Self::new`]. However, doing so would require some constant evaluation
+ /// features that are yet to be implemented and stabilized in the Rust
+ /// compiler. This function is therefore exposed in the meantime so users
+ /// may still be able to call the concerned APIs by providing the raw
+ /// encoding information string themselves, thus obtaining a block
+ /// containing it and working with these APIs.
+ ///
+ /// You provide the encoding through the `E` type parameter, which should
+ /// implement [`ManualBlockEncoding`].
+ ///
+ /// The same requirements as [`Self::new`] apply here as well.
+ ///
+ /// [`FileProvider`]: https://developer.apple.com/documentation/fileprovider?language=objc
+ /// [`NSFileProviderManager`]: https://developer.apple.com/documentation/fileprovider/nsfileprovidermanager?language=objc
+ /// [`reimportItemsBelowItemWithIdentifier:completionHandler:`]: https://developer.apple.com/documentation/fileprovider/nsfileprovidermanager/reimportitems(below:completionhandler:)?language=objc
+ /// [`waitForStabilizationWithCompletionHandler:`]: https://developer.apple.com/documentation/fileprovider/nsfileprovidermanager/waitforstabilization(completionhandler:)?language=objc
+ /// [`NetworkExtension`]: https://developer.apple.com/documentation/networkextension?language=objc
+ /// [`NEFilterDataProvider`]: https://developer.apple.com/documentation/networkextension/nefilterdataprovider?language=objc
+ /// [`applySettings:completionHandler`]: https://developer.apple.com/documentation/networkextension/nefilterdataprovider/3181998-applysettings?language=objc
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # use core::ffi::CStr;
+ /// # use block2::{Block, ManualBlockEncoding, StackBlock};
+ /// # use objc2_foundation::NSError;
+ /// #
+ /// struct MyBlockEncoding;
+ /// // SAFETY: The encoding is correct.
+ /// unsafe impl ManualBlockEncoding for MyBlockEncoding {
+ /// type Arguments = (*mut NSError,);
+ /// type Return = i32;
+ /// const ENCODING_CSTR: &'static CStr = if cfg!(target_pointer_width = "64") {
+ /// cr#"i16@?0@"NSError"8"#
+ /// } else {
+ /// cr#"i8@?0@"NSError"4"#
+ /// };
+ /// }
+ ///
+ /// let my_block = StackBlock::with_encoding::<MyBlockEncoding>(|_err: *mut NSError| {
+ /// 42i32
+ /// });
+ /// assert_eq!(my_block.call((core::ptr::null_mut(),)), 42);
+ /// ```
+ #[inline]
+ pub fn with_encoding<E>(closure: Closure) -> Self
+ where
+ E: ManualBlockEncoding<Arguments = A, Return = R>,
+ {
+ Self::maybe_encoded::<UserSpecified<E>>(closure)
+ }
+
+ #[inline]
+ fn maybe_encoded<E>(closure: Closure) -> Self
+ where
+ E: ManualBlockEncodingExt<Arguments = A, Return = R>,
+ {
+ // TODO: Re-consider calling `crate::traits::debug_assert_block_encoding`.
+ let header = BlockHeader {
+ #[allow(unused_unsafe)]
+ isa: unsafe { ptr::addr_of!(ffi::_NSConcreteStackBlock) },
+ flags: BlockFlags::BLOCK_HAS_COPY_DISPOSE
+ | if !E::IS_NONE {
+ BlockFlags::BLOCK_HAS_SIGNATURE
+ } else {
+ BlockFlags::EMPTY
+ },
+ reserved: MaybeUninit::new(0),
+ invoke: Some(Closure::__get_invoke_stack_block()),
+ // TODO: Use `Self::DESCRIPTOR_BASIC` when `F: Copy`
+ // (probably only possible with specialization).
+ descriptor: if E::IS_NONE {
+ // SAFETY: The descriptor must (probably) point to `static`
+ // memory, as Objective-C code may assume the block's
+ // descriptor to be alive past the lifetime of the block
+ // itself.
+ //
+ // Ideally, we'd have declared the descriptor as a `static`
+ // but since Rust doesn't have generic statics, we have to
+ // rely on [promotion] here to convert the constant into a
+ // static.
+ //
+ // For this to work, it requires that the descriptor type does
+ // not implement `Drop` (it does not), and that the descriptor
+ // does not contain `UnsafeCell` (it does not).
+ //
+ // [promotion]: https://doc.rust-lang.org/reference/destructors.html#constant-promotion
+ BlockDescriptorPtr {
+ with_copy_dispose: &Self::DESCRIPTOR_WITH_CLONE,
+ }
+ } else {
+ // SAFETY: see above; the value is already a similar constant,
+ // so promotion can be guaranteed as well here.
+ BlockDescriptorPtr {
+ // TODO: move to a `const fn` defined next to the partially-
+ // copied constant and called here in an inline `const` when
+ // the MSRV is at least 1.79.
+ with_copy_dispose_signature:
+ &<Self as EncodedCloneDescriptors<E>>::DESCRIPTOR_WITH_CLONE_AND_ENCODING,
+ }
+ },
+ };
+ Self {
+ p: PhantomData,
+ header,
+ closure,
+ }
+ }
+}
+
+// `RcBlock::with_encoding`
+impl<'f, A, R, Closure> StackBlock<'f, A, R, Closure> {
+ unsafe extern "C-unwind" fn empty_clone_closure(_dst: *mut c_void, _src: *const c_void) {
+ // We do nothing, the closure has been `memmove`'d already, and
+ // ownership will be passed in `RcBlock::with_encoding`.
+ }
+
+ const DESCRIPTOR_WITH_DROP: BlockDescriptorCopyDispose = BlockDescriptorCopyDispose {
+ reserved: 0,
+ size: Self::SIZE,
+ copy: Some(Self::empty_clone_closure),
+ dispose: Some(Self::drop_closure),
+ };
+
+ /// # Safety
+ ///
+ /// `_Block_copy` must be called on the resulting stack block only once.
+ #[inline]
+ pub(crate) unsafe fn new_no_clone<E>(closure: Closure) -> Self
+ where
+ A: EncodeArguments,
+ R: EncodeReturn,
+ Closure: IntoBlock<'f, A, R>,
+ E: ManualBlockEncodingExt<Arguments = A, Return = R>,
+ {
+ // TODO: Re-consider calling `crate::traits::debug_assert_block_encoding`.
+ // Don't need to emit copy and dispose helpers if the closure
+ // doesn't need it.
+ let flags = if mem::needs_drop::<Self>() {
+ BlockFlags::BLOCK_HAS_COPY_DISPOSE
+ } else {
+ BlockFlags::EMPTY
+ } | if !E::IS_NONE {
+ BlockFlags::BLOCK_HAS_SIGNATURE
+ } else {
+ BlockFlags::EMPTY
+ };
+ // See discussion in `new` above with regards to the safety of the
+ // pointer to the descriptor.
+ let descriptor = match (mem::needs_drop::<Self>(), E::IS_NONE) {
+ (true, true) => {
+ // SAFETY: see above.
+ BlockDescriptorPtr {
+ with_copy_dispose: &Self::DESCRIPTOR_WITH_DROP,
+ }
+ }
+ (false, true) => {
+ // SAFETY: see above.
+ BlockDescriptorPtr {
+ basic: &Self::DESCRIPTOR_BASIC,
+ }
+ }
+ (true, false) => {
+ // SAFETY: see above; the value is already a similar constant,
+ // so promotion can be guaranteed as well here.
+ BlockDescriptorPtr {
+ // TODO: move to a `const fn` defined next to the partially-
+ // copied constant and called here in an inline `const` when
+ // the MSRV is at least 1.79.
+ with_copy_dispose_signature:
+ &<Self as EncodedDescriptors<E>>::DESCRIPTOR_WITH_DROP_AND_ENCODING,
+ }
+ }
+ (false, false) => {
+ // SAFETY: see above; the value is already a similar constant,
+ // so promotion can be guaranteed as well here.
+ BlockDescriptorPtr {
+ // TODO: move to a `const fn` defined next to the partially-
+ // copied constant and called here in an inline `const` when
+ // the MSRV is at least 1.79.
+ with_signature:
+ &<Self as EncodedDescriptors<E>>::DESCRIPTOR_BASIC_WITH_ENCODING,
+ }
+ }
+ };
+
+ let header = BlockHeader {
+ #[allow(unused_unsafe)]
+ isa: unsafe { ptr::addr_of!(ffi::_NSConcreteStackBlock) },
+ flags,
+ reserved: MaybeUninit::new(0),
+ invoke: Some(Closure::__get_invoke_stack_block()),
+ descriptor,
+ };
+ Self {
+ p: PhantomData,
+ header,
+ closure,
+ }
+ }
+}
+
+/// Dummy trait used in order to link [`StackBlock`]'s descriptor constants and
+/// a [`ManualBlockEncoding`] into new derived constants at compile time.
+///
+/// This is definitely a hack that should be replaced with `const fn`s defined
+/// next to [`StackBlock`]'s descriptor constants and used in the below
+/// constants' stead with inline `const`s to guarantee proper promotion.
+///
+/// See also the below [`EncodedCloneDescriptors`].
+trait EncodedDescriptors<E: ManualBlockEncoding> {
+ const DESCRIPTOR_BASIC_WITH_ENCODING: BlockDescriptorSignature;
+ const DESCRIPTOR_WITH_DROP_AND_ENCODING: BlockDescriptorCopyDisposeSignature;
+}
+
+impl<'f, A, R, Closure, E> EncodedDescriptors<E> for StackBlock<'f, A, R, Closure>
+where
+ A: EncodeArguments,
+ R: EncodeReturn,
+ Closure: IntoBlock<'f, A, R>,
+ E: ManualBlockEncoding<Arguments = A, Return = R>,
+{
+ /// [`Self::DESCRIPTOR_BASIC`] with the signature added from `E`.
+ const DESCRIPTOR_BASIC_WITH_ENCODING: BlockDescriptorSignature = BlockDescriptorSignature {
+ reserved: Self::DESCRIPTOR_BASIC.reserved,
+ size: Self::DESCRIPTOR_BASIC.size,
+ encoding: E::ENCODING_CSTR.as_ptr(),
+ };
+ /// [`Self::DESCRIPTOR_WITH_DROP`] with the signature added from `E`.
+ const DESCRIPTOR_WITH_DROP_AND_ENCODING: BlockDescriptorCopyDisposeSignature =
+ BlockDescriptorCopyDisposeSignature {
+ reserved: Self::DESCRIPTOR_WITH_DROP.reserved,
+ size: Self::DESCRIPTOR_WITH_DROP.size,
+ copy: Self::DESCRIPTOR_WITH_DROP.copy,
+ dispose: Self::DESCRIPTOR_WITH_DROP.dispose,
+ encoding: E::ENCODING_CSTR.as_ptr(),
+ };
+}
+
+/// Identical role as [`EncodedDescriptors`], with the additional requirement
+/// that the block closure be [`Clone`] when implemented on [`StackBlock`]
+/// since [`StackBlock::DESCRIPTOR_WITH_CLONE`] is defined in such a context.
+trait EncodedCloneDescriptors<E: ManualBlockEncoding> {
+ const DESCRIPTOR_WITH_CLONE_AND_ENCODING: BlockDescriptorCopyDisposeSignature;
+}
+
+impl<'f, A, R, Closure, E> EncodedCloneDescriptors<E> for StackBlock<'f, A, R, Closure>
+where
+ A: EncodeArguments,
+ R: EncodeReturn,
+ Closure: IntoBlock<'f, A, R> + Clone,
+ E: ManualBlockEncoding<Arguments = A, Return = R>,
+{
+ /// [`Self::DESCRIPTOR_WITH_CLONE`] with the signature added from `E`.
+ const DESCRIPTOR_WITH_CLONE_AND_ENCODING: BlockDescriptorCopyDisposeSignature =
+ BlockDescriptorCopyDisposeSignature {
+ reserved: Self::DESCRIPTOR_WITH_CLONE.reserved,
+ size: Self::DESCRIPTOR_WITH_CLONE.size,
+ copy: Self::DESCRIPTOR_WITH_CLONE.copy,
+ dispose: Self::DESCRIPTOR_WITH_CLONE.dispose,
+ encoding: E::ENCODING_CSTR.as_ptr(),
+ };
+}
+
+impl<A, R, Closure: Clone> Clone for StackBlock<'_, A, R, Closure> {
+ #[inline]
+ fn clone(&self) -> Self {
+ Self {
+ p: PhantomData,
+ header: self.header,
+ closure: self.closure.clone(),
+ }
+ }
+}
+
+impl<A, R, Closure: Copy> Copy for StackBlock<'_, A, R, Closure> {}
+
+impl<'f, A, R, Closure> Deref for StackBlock<'f, A, R, Closure>
+where
+ A: EncodeArguments,
+ R: EncodeReturn,
+ Closure: IntoBlock<'f, A, R>,
+{
+ type Target = Block<Closure::Dyn>;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ let ptr: NonNull<Self> = NonNull::from(self);
+ let ptr: NonNull<Block<Closure::Dyn>> = ptr.cast();
+ // SAFETY: A pointer to `StackBlock` is always safe to convert to a
+ // pointer to `Block`, and the reference will be valid as long the
+ // stack block exists.
+ //
+ // Finally, the stack block is implemented correctly, such that it is
+ // safe to call `_Block_copy` on the returned value.
+ unsafe { ptr.as_ref() }
+ }
+}
+
+impl<A, R, Closure> fmt::Debug for StackBlock<'_, A, R, Closure> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut f = f.debug_struct("StackBlock");
+ debug_block_header(&self.header, &mut f);
+ f.finish_non_exhaustive()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_size() {
+ assert_eq!(
+ mem::size_of::<BlockHeader>(),
+ <StackBlock<'_, (), (), ()>>::SIZE as _,
+ );
+ assert_eq!(
+ mem::size_of::<BlockHeader>() + mem::size_of::<fn()>(),
+ <StackBlock<'_, (), (), fn()>>::SIZE as _,
+ );
+ }
+
+ #[allow(dead_code)]
+ fn covariant<'b, 'f>(
+ b: StackBlock<'static, (), (), impl Fn() + 'static>,
+ ) -> StackBlock<'b, (), (), impl Fn() + 'f> {
+ b
+ }
+}
diff --git a/third_party/rust/block2/src/traits.rs b/third_party/rust/block2/src/traits.rs
@@ -0,0 +1,395 @@
+use core::ffi::CStr;
+use core::marker::PhantomData;
+use core::mem;
+use core::ptr;
+
+use objc2::encode::EncodeArguments;
+use objc2::encode::{EncodeArgument, EncodeReturn};
+
+use crate::{Block, StackBlock};
+
+mod private {
+ pub trait Sealed<A, R> {}
+}
+
+/// Types that represent closure parameters/arguments and return types in a
+/// block.
+///
+/// This is implemented for [`dyn`] [`Fn`] closures with up to 12 parameters,
+/// where each parameter implements [`EncodeArgument`] and the return type
+/// implements [`EncodeReturn`].
+///
+/// [`dyn`]: https://doc.rust-lang.org/std/keyword.dyn.html
+///
+///
+/// # Safety
+///
+/// This is a sealed trait, and should not need to be implemented. Open an
+/// issue if you know a use-case where this restrition should be lifted!
+pub unsafe trait BlockFn: private::Sealed<Self::Args, Self::Output> {
+ /// The parameters/arguments to the block.
+ type Args: EncodeArguments;
+
+ /// The return type of the block.
+ type Output: EncodeReturn;
+
+ /// Calls the given invoke function with the block and arguments.
+ #[doc(hidden)]
+ unsafe fn __call_block(
+ invoke: unsafe extern "C-unwind" fn(),
+ block: *mut Block<Self>,
+ args: Self::Args,
+ ) -> Self::Output;
+}
+
+/// Types that may be converted into a block.
+///
+/// This is implemented for [`Fn`] closures of up to 12 parameters, where each
+/// parameter implements [`EncodeArgument`] and the return type implements
+/// [`EncodeReturn`].
+///
+///
+/// # Safety
+///
+/// This is a sealed trait, and should not need to be implemented. Open an
+/// issue if you know a use-case where this restrition should be lifted!
+pub unsafe trait IntoBlock<'f, A, R>: private::Sealed<A, R>
+where
+ A: EncodeArguments,
+ R: EncodeReturn,
+{
+ /// The type-erased `dyn Fn(...Args) -> R + 'f`.
+ type Dyn: ?Sized + BlockFn<Args = A, Output = R>;
+
+ #[doc(hidden)]
+ fn __get_invoke_stack_block() -> unsafe extern "C-unwind" fn();
+}
+
+macro_rules! impl_traits {
+ ($($a:ident: $t:ident),*) => (
+ impl<$($t: EncodeArgument,)* R: EncodeReturn, Closure> private::Sealed<($($t,)*), R> for Closure
+ where
+ Closure: ?Sized + Fn($($t),*) -> R,
+ {}
+
+ // TODO: Add `+ Send`, `+ Sync` and `+ Send + Sync` versions.
+ unsafe impl<$($t: EncodeArgument,)* R: EncodeReturn> BlockFn for dyn Fn($($t),*) -> R + '_ {
+ type Args = ($($t,)*);
+ type Output = R;
+
+ #[inline]
+ unsafe fn __call_block(
+ invoke: unsafe extern "C-unwind" fn(),
+ block: *mut Block<Self>,
+ ($($a,)*): Self::Args,
+ ) -> Self::Output {
+ // Very similar to `MessageArguments::__invoke`
+ let invoke: unsafe extern "C-unwind" fn(*mut Block<Self> $(, $t)*) -> R = unsafe {
+ mem::transmute(invoke)
+ };
+
+ unsafe { invoke(block $(, $a)*) }
+ }
+ }
+
+ unsafe impl<'f, $($t,)* R, Closure> IntoBlock<'f, ($($t,)*), R> for Closure
+ where
+ $($t: EncodeArgument,)*
+ R: EncodeReturn,
+ Closure: Fn($($t),*) -> R + 'f,
+ {
+ type Dyn = dyn Fn($($t),*) -> R + 'f;
+
+ #[inline]
+ fn __get_invoke_stack_block() -> unsafe extern "C-unwind" fn() {
+ unsafe extern "C-unwind" fn invoke<'f, $($t,)* R, Closure>(
+ block: *mut StackBlock<'f, ($($t,)*), R, Closure>,
+ $($a: $t,)*
+ ) -> R
+ where
+ Closure: Fn($($t),*) -> R + 'f
+ {
+ let closure = unsafe { &*ptr::addr_of!((*block).closure) };
+ (closure)($($a),*)
+ }
+
+ unsafe {
+ mem::transmute::<
+ unsafe extern "C-unwind" fn(*mut StackBlock<'f, ($($t,)*), R, Closure>, $($t,)*) -> R,
+ unsafe extern "C-unwind" fn(),
+ >(invoke)
+ }
+ }
+ }
+ );
+}
+
+impl_traits!();
+impl_traits!(t0: T0);
+impl_traits!(t0: T0, t1: T1);
+impl_traits!(t0: T0, t1: T1, t2: T2);
+impl_traits!(t0: T0, t1: T1, t2: T2, t3: T3);
+impl_traits!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4);
+impl_traits!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5);
+impl_traits!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6);
+impl_traits!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7);
+impl_traits!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8);
+impl_traits!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, t9: T9);
+impl_traits!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, t9: T9, t10: T10);
+impl_traits!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, t9: T9, t10: T10, t11: T11);
+
+/// Interim abstraction to manually provide block encodings for use at compile
+/// time with [`StackBlock::with_encoding`] and [`RcBlock::with_encoding`].
+///
+/// See these functions for examples of how to implement and use this trait,
+/// since its sole purpose is passing values at compile time to them.
+///
+/// As a side note, one might be surprised by the additional [`Self::Arguments`]
+/// and [`Self::Return`] associated types requiring concrete implementations to
+/// specify them while they are not actually used. This is intentional:
+///
+/// * the types are checked at compile-time to be equal to the ones used with
+/// [`RcBlock::with_encoding`] and [`StackBlock::with_encoding`], usually
+/// inferred by the compiler when giving a closure: this should help avoid
+/// some easy oversights;
+/// * the user is forced to write both the standard Rust types and the
+/// encoding string at the same time, so particular attention to the types
+/// is put to the forefront for them;
+/// * reading a block encoding string is tough when not initiated, so these
+/// also serve as self-documentation;
+/// * the safety validation can be moved to the trait implementation, so that
+/// the use can be marked safe.
+///
+/// [`RcBlock::with_encoding`]: crate::RcBlock::with_encoding
+///
+/// # Safety
+///
+/// [`Self::ENCODING_CSTR`] must correspond to the actual signature string a
+/// recent-enough Objective-C compiler would generate for a block taking in
+/// [`Self::Arguments`] as input and returning [`Self::Return`] as output.
+/// This information is actually used by the Objective-C runtime in order to
+/// correctly invoke the block, so specifying a wrong encoding is definitely a
+/// soundness issue: see [this issue comment][i442-sign-check] for more details
+/// about what exactly goes on behind the scenes in order to justify all the
+/// following precautions.
+///
+/// The easiest way to do this is probably to ask Clang; the following program
+/// will print the signature of the block (if you're having trouble linking,
+/// you should be able to find the signature in the assembly output).
+///
+/// ```objective-c
+/// #import <Foundation/Foundation.h>
+///
+/// // Unstable API, but fine for test usage like this.
+/// const char * _Block_signature(void *);
+///
+/// int main() {
+/// // Declare the signature of your block.
+/// // This one takes `id` and `int`, and returns `NSString*`.
+/// id block = ^NSString* (id a, int b) {
+/// return nil;
+/// };
+///
+/// printf("%s\n", _Block_signature((void*)block));
+/// return 0;
+/// }
+/// ```
+///
+/// A more thorough but manual approach is to only follow the rules described
+/// below.
+///
+/// In this process, you may be able to use [`Encoding::to_string`][enc2s] in
+/// order to get the various components of the signature string and then
+/// concatenate them manually with the required numbers (described below)
+/// inserted at their correct place.
+///
+/// [enc2s]: objc2::encode::Encoding#impl-Display-for-Encoding
+/// [i442-sign-check]: https://github.com/madsmtm/objc2/issues/442#issuecomment-2284932726
+///
+/// # Encoding string generation
+///
+/// This is the result of the `@encode` Objective-C compiler directive. The
+/// [Apple documentation] and [GCC documentation] explain how each base type is
+/// encoded into a string representation. See there for a somewhat-formal
+/// specification and a few basic examples. See also [`Encoding`].
+///
+/// See also the [GCC method signatures] section. It is mostly valid for blocks
+/// as well, since they are basically functions with captured environment --
+/// i.e. closures, except that no selector is implicitly sent, only the block
+/// object is. In short, the "signature" is a null-terminated string, composed
+/// of the following, in order:
+///
+/// * The return type, including type qualifiers. For example, a block
+/// returning `int` ([`i32`]) would have `i` here.
+/// * The total size (in bytes) required to pass all the parameters: the call
+/// frame size. This includes the hidden block object parameter that is
+/// passed as a pointer, so at least 4 bytes when under a 32-bit system or
+/// most probably 8 bytes when under a 64-bit one.
+/// * Each argument, with the type encoding, followed by the offset (in bytes)
+/// of the argument in the list of parameters. The first is the always the
+/// hidden block object pointer and is therefore `@?0`.
+///
+/// Examples:
+///
+/// | Objective-C signature | Runtime encoding |
+/// | ------------------------ | ------------------------------------------ |
+/// | `void (^)(void)` | `v8@?0` |
+/// | `int (^)(void)` | `i8@?0` |
+/// | `int (^)(float)` | `i12@?0f8` |
+/// | `int (^)(float, _Bool)` | `i16@?0f8B12` |
+/// | `void (^)(int*)` | `v16@?0^i8` |
+/// | `void (^)(NSError*)` | `v16@?0@8` or `v16@?0@"NSError"8` |
+/// | `NSError* (^)(NSError*)` | `@16@?0@8` or `@"NSError"16@?0@"NSError"8` |
+///
+/// [Apple documentation]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
+/// [`Encoding`]: objc2::encode::Encoding
+/// [GCC documentation]: https://gcc.gnu.org/onlinedocs/gcc/Type-encoding.html
+/// [GCC method signatures]: https://gcc.gnu.org/onlinedocs/gcc/Method-signatures.html
+pub unsafe trait ManualBlockEncoding {
+ /// The function's input argument types.
+ type Arguments: EncodeArguments;
+ /// The function's return type.
+ type Return: EncodeReturn;
+ /// The raw encoding information string.
+ const ENCODING_CSTR: &'static CStr;
+}
+
+/// Particular [`ManualBlockEncoding`] that indicates no actual encoding should
+/// be set in the block's descriptor.
+///
+/// This is used in a bit of a hackish way in order to share more code between
+/// the encoded and non-encoded paths.
+pub(crate) struct NoBlockEncoding<A, R>
+where
+ A: EncodeArguments,
+ R: EncodeReturn,
+{
+ _a: PhantomData<A>,
+ _r: PhantomData<R>,
+}
+
+// SAFETY: The encoding here is incorrect, but it will never be used because
+// we specify `IS_NONE = true` in `ManualBlockEncodingExt`.
+unsafe impl<A, R> ManualBlockEncoding for NoBlockEncoding<A, R>
+where
+ A: EncodeArguments,
+ R: EncodeReturn,
+{
+ type Arguments = A;
+ type Return = R;
+ // TODO: change this to `c""` when the MSRV is at least 1.77.
+ // SAFETY: the byte string is written here and contains exactly one nul byte.
+ const ENCODING_CSTR: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") };
+}
+
+/// Crate-private extension to [`ManualBlockEncoding`].
+pub(crate) trait ManualBlockEncodingExt: ManualBlockEncoding {
+ /// Only `true` for [`NoBlockEncoding`].
+ const IS_NONE: bool;
+}
+
+impl<E: ManualBlockEncoding> ManualBlockEncodingExt for UserSpecified<E> {
+ const IS_NONE: bool = false;
+}
+
+impl<A, R> ManualBlockEncodingExt for NoBlockEncoding<A, R>
+where
+ A: EncodeArguments,
+ R: EncodeReturn,
+{
+ const IS_NONE: bool = true;
+}
+
+/// Dummy newtype used to conditionally implement [`ManualBlockEncodingExt`]
+/// and therefore circumvent the need for specialization.
+#[repr(transparent)]
+pub(crate) struct UserSpecified<E: ManualBlockEncoding>(E);
+
+unsafe impl<E: ManualBlockEncoding> ManualBlockEncoding for UserSpecified<E> {
+ type Arguments = E::Arguments;
+ type Return = E::Return;
+ const ENCODING_CSTR: &'static CStr = E::ENCODING_CSTR;
+}
+
+/// Checks for encoding compatibility between the given generic parameters,
+/// panicking if it is not, but only on `cfg(debug_assertions)` and if `E` is
+/// not none.
+#[cfg_attr(not(debug_assertions), inline(always))]
+#[allow(unused)]
+pub(crate) fn debug_assert_block_encoding<A, R, E>()
+where
+ A: EncodeArguments,
+ R: EncodeReturn,
+ E: ManualBlockEncodingExt<Arguments = A, Return = R>,
+{
+ #[cfg(debug_assertions)]
+ {
+ if !E::IS_NONE {
+ // TODO: relax to check for equivalence instead of strict equality.
+ assert_eq!(
+ E::ENCODING_CSTR,
+ &*crate::encoding::block_signature_string::<A, R>()
+ );
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::ffi::c_char;
+
+ use super::*;
+
+ #[test]
+ fn test_manual_block_encoding_is_none() {
+ // Normal case.
+ struct Enc1;
+ unsafe impl ManualBlockEncoding for Enc1 {
+ type Arguments = (i32, f32);
+ type Return = u8;
+ #[cfg(target_pointer_width = "64")]
+ const ENCODING_CSTR: &'static CStr =
+ // Somehow, using a C string literal seems to fail the MSRV
+ // check here, so use the old way instead here.
+ unsafe { CStr::from_bytes_with_nul_unchecked(b"C16@?0i8f12\0") };
+ #[cfg(not(target_pointer_width = "64"))]
+ const ENCODING_CSTR: &'static CStr =
+ unsafe { CStr::from_bytes_with_nul_unchecked(b"C12@?0i4f8\0") };
+ }
+ // HACK: use `identity` in order to circumvent a Clippy warning.
+ assert!(!core::convert::identity(UserSpecified::<Enc1>::IS_NONE));
+
+ // No input + no output case.
+ struct Enc2;
+ unsafe impl ManualBlockEncoding for Enc2 {
+ type Arguments = ();
+ type Return = ();
+ #[cfg(target_pointer_width = "64")]
+ const ENCODING_CSTR: &'static CStr =
+ unsafe { CStr::from_bytes_with_nul_unchecked(b"v8@?0\0") };
+ #[cfg(not(target_pointer_width = "64"))]
+ const ENCODING_CSTR: &'static CStr =
+ unsafe { CStr::from_bytes_with_nul_unchecked(b"v4@?0\0") };
+ }
+ assert!(!core::convert::identity(UserSpecified::<Enc2>::IS_NONE));
+
+ // Ensure we don't rely on the encoding string's emptiness.
+ struct Enc3;
+ unsafe impl ManualBlockEncoding for Enc3 {
+ type Arguments = ();
+ type Return = ();
+ const ENCODING_CSTR: &'static CStr =
+ unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") };
+ }
+ assert!(!core::convert::identity(UserSpecified::<Enc3>::IS_NONE));
+
+ // Only `NoBlockEncoding` should be `IS_NONE`.
+ assert!(core::convert::identity(NoBlockEncoding::<(), ()>::IS_NONE));
+ assert!(core::convert::identity(
+ NoBlockEncoding::<(i32, f32), u8>::IS_NONE
+ ));
+ assert!(core::convert::identity(
+ NoBlockEncoding::<(*const u8,), *const c_char>::IS_NONE
+ ));
+ }
+}
diff --git a/third_party/rust/block2/translation-config.toml b/third_party/rust/block2/translation-config.toml
@@ -0,0 +1,14 @@
+framework = "block"
+crate = "block2"
+required-crates = ["objc2"]
+link = false
+skipped = true
+is-library = true
+
+macos = "10.6"
+maccatalyst = "13.0"
+ios = "3.2"
+tvos = "9.0"
+watchos = "2.0"
+visionos = "1.0"
+gnustep = true
diff --git a/third_party/rust/objc2-encode/.cargo-checksum.json b/third_party/rust/objc2-encode/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"CHANGELOG.md":"998a091c4482ecaf0d4d2f99cb61ea7edfcae91e30a9c65e419c974c44e97fe4","Cargo.lock":"c2d878335c2920d350d861cec5dd4ee60dab3d2a4481b1c6851d55c7412ec233","Cargo.toml":"cf9a8cc812710344d7fe618a5e615530baeeceae5c2826b9d5bb557dc419f159","README.md":"e980a74b88a62193f7908df3c27e7915f38559870f5e033ddb77fbed65e024a5","src/encoding.rs":"fef7c5d6b4911edf74e49d0c04ff4478211daa47d9a6b442a855907809344f9b","src/encoding_box.rs":"ebeef60bf38dc37ad37a7305978225e12c76274ebf7d399b1dc26aa54afd6366","src/helper.rs":"fb6b0ec5ced5922b144eefd92568e5ff65472af995fcd71457166ca305a9ccff","src/lib.rs":"6d526334bf38275d812335c1f18542adfb5c0a247a4b822342e6770a03c5491d","src/parse.rs":"2b1b995653f09e33b112dad74e97dfcb70c6e760e4c663e3c65c1d767009caaf","src/static_str.rs":"22cc1d7cb7901a05263115efc47b91ddd6f4022f583d5d353556bbbc372f1679"},"package":"ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"}
+\ No newline at end of file
diff --git a/third_party/rust/objc2-encode/CHANGELOG.md b/third_party/rust/objc2-encode/CHANGELOG.md
@@ -0,0 +1,295 @@
+# Changelog
+
+Notable changes to this crate will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
+
+## Unreleased - YYYY-MM-DD
+
+
+## 4.1.0 - 2025-01-22
+
+### Added
+* Added `Encoding::size`, which computes the size of the encoded type for the
+ current target.
+
+### Changed
+* Equivalence comparisons now consider `Encoding::Class`, `Encoding::Object`
+ and `Encoding::Block` as equivalent.
+
+
+## 4.0.3 - 2024-05-21
+
+### Fixed
+* Fixed an issue with publishing using an older version of Cargo that didn't
+ handle the `lints.workspace = true` Cargo setup properly.
+
+
+## 4.0.2 - 2024-05-21 (Yanked)
+
+### Added
+* Added `Encoding::None`, which represents encodings where the host compiler
+ (i.e. `clang`) couldn't generate an encoding for a given type.
+
+ This is useful when working with SIMD types.
+
+
+## 4.0.1 - 2024-04-17
+
+### Changed
+* Build documentation on docs.rs on more Apple platforms.
+
+
+## 4.0.0 - 2023-12-03
+
+### Changed
+* **BREAKING**: Changed the type of `EncodingBox::Struct` and
+ `EncodingBox::Union` to no longer contain an `Option`.
+
+### Fixed
+* Fixed encoding equivalence between empty structs and unions.
+* Parse (and ignore) extended type information.
+
+
+## 3.0.0 - 2023-07-31
+
+### Fixed
+* Bumped version number to ensure that this crate can be compiled together
+ with code that depends on pre-releases of `2.0.0`.
+
+
+## 2.0.0 - 2023-06-20
+
+### Added
+* Improved documentation slightly.
+
+
+## 2.0.0-pre.4 - 2023-02-07
+
+### Added
+* Made the crate `no_std` compatible.
+* Made the crate platform-agnostic.
+
+### Changed
+* **BREAKING**: Moved the `Encode`, `RefEncode`, `EncodeArguments`,
+ `EncodeConvert`, `OptionEncode` to `objc2`.
+* **BREAKING**: `Encoding::BitField` now allows omitting the type using an
+ `Option`.
+* **BREAKING**: Changed length field in `Encoding::Array` from `usize` to
+ `u64` (to be platform-agnostic).
+
+### Fixed
+* Fixed `Encoding::BitField` encoding parsing.
+
+
+## 2.0.0-pre.3 - 2022-12-24
+
+### Added
+* Added `EncodingBox` for dynamically parsing encodings and creating them on
+ the heap.
+* Added `ParseError`, a custom type that represents errors during encoding
+ parsing.
+* Added `Encoding::equivalent_to_box` for comparing `Encoding` and
+ `EncodingBox`.
+* Implemented `Encode` and `RefEncode` for `NonNull<c_void>`.
+* Added `OptionEncode` to help with implementing `Encode` and `RefEncode` for
+ `Option`.
+
+### Changed
+* **BREAKING**: Verify that the name in `Encoding::Struct` and
+ `Encoding::Union` is a valid C identifier.
+
+### Removed
+* **BREAKING**: `Encoding` no longer implements `Copy`, though it is still
+ `Clone`.
+* **BREAKING**: Removed `Encoding::equivalent_to_start_of_str`, since it
+ wasn't really useful.
+
+
+## 2.0.0-pre.2 - 2022-08-28
+
+### Added
+* Added `EncodeConvert` trait to help with correctly handling `BOOL`/`bool`.
+
+### Changed
+* **BREAKING**: Remove the lifetime specifier from `Encoding`, since the non
+ -`'static` version was essentially useless.
+
+### Fixed
+* Fixed the encoding output and comparison of structs behind pointers.
+
+### Removed
+* **BREAKING**: `bool` (and `AtomicBool`) no longer implements `Encode`, since
+ that was difficult to use correctly. See the `EncodeConvert` trait, or use
+ `objc2::runtime::Bool` instead.
+
+
+## 2.0.0-pre.1 - 2022-07-19
+
+### Added
+* Added `Encoding::Atomic`.
+* Implement `Encode` and `RefEncode` for `std::sync::atomic` types.
+
+### Changed
+* **BREAKING**: Renamed `Encoding::C_U_LONG` to `Encoding::C_ULONG`.
+
+
+## 2.0.0-pre.0 - 2022-06-13
+
+### Added
+* Added `Encoding::C_LONG` and `Encoding::C_U_LONG` to help with platform
+ compatibility; use these instead of `c_long::ENCODING` and
+ `c_ulong::ENCODING`.
+* Implement `Encode` and `RefEncode` for `MaybeUninit<T>`, where `T` is
+ properly bound.
+
+### Changed
+* **BREAKING**: Sealed the `EncodeArguments` trait.
+* **BREAKING**: Add type argument to `Encoding::BitField`.
+
+### Removed
+* **BREAKING**: Removed `PartialEq` impl between `str` and `Encoding` since it
+ was incorrect (it violated the trait requirements).
+* **BREAKING**: Removed `Encode` and `RefEncode` implementations for `Pin`
+ since it may not be sound.
+
+
+## 2.0.0-beta.2 - 2022-01-03
+
+### Added
+* Implement `Hash` for `Encoding`.
+
+### Changed
+* Improved documentation.
+
+
+## 2.0.0-beta.1 - 2021-12-22
+
+### Added
+* `Encoding::equivalent_to`, `Encoding::equivalent_to_str` and
+ `Encoding::equivalent_to_start_of_str` methods for more precise comparison
+ semantics.
+* Added `Encode` and `RefEncode` implementations for `Option` function
+ pointers.
+
+### Changed
+* Discourage comparing `str` with `Encoding` using `PartialEq`. This trait
+ impl might get removed in a future version.
+
+
+## 2.0.0-beta.0 - 2021-11-22
+
+### Added
+* **BREAKING**: Add `Encoding::LongDouble`, `Encoding::FloatComplex`,
+ `Encoding::DoubleComplex` and `Encoding::LongDoubleComplex`.
+* Implement `RefEncode` for all number types that implement `Encode` (`bool`,
+ `i8`, `usize`, `f32`, `NonZeroU32`, and so on).
+* Implement `RefEncode` for `*const c_void` and `*mut c_void` (allowing
+ `void**` in C).
+* Implement `Encode` and `RefEncode` for `Wrapping<T>`, where `T` is properly
+ bound.
+
+### Changed
+* **BREAKING**: Make `Encoding` `#[non_exhaustive]`. This will help us in
+ evolving the API while minimizing further breaking changes.
+* Discourage using `bool::ENCODING`; use `objc2::Bool::ENCODING` instead.
+* Discourage using `()::ENCODING` for anything other than as a function return
+ type.
+
+
+## 2.0.0-alpha.1 - 2021-09-01
+
+### Added
+* Improved documentation.
+* Add `RefEncode` trait, which represents types whose pointers has an
+ encoding. This means you now only have to implement `RefEncode`, and not
+ both `&Encode` and `&mut Encode`.
+ Additionally, encodings of pointers to pointers (to pointers, and so on) are
+ now supported.
+* Implement `Encode` for `NonZeroX` and `Option<NonZeroX>` integer types.
+* Implement `RefEncode` for arrays.
+* Implement `Encode` and `RefEncode` for (where `T` is properly bound):
+ - `ManuallyDrop<T>`
+ - `Pin<T>`
+ - `NonNull<T>`
+ - `Option<NonNull<T>>`
+* Add `EncodeArguments` trait, to represent an ordered group of functions
+ arguments, where each argument has an Objective-C type-encoding.
+ Previously in the `objc` crate.
+* Implement `Encode` and `RefEncode` for some `extern "C" fn` pointers.
+
+### Removed
+* **BREAKING**: Removed automatic `*const T: Encode` and `*mut T: Encode`
+ impls when when `&T: Encode` and `&mut T: Encode` was implemented.
+
+ Implement `T: RefEncode` instead!
+
+
+## 2.0.0-alpha.0 - 2021-09-01
+
+### Added
+* Improved documentation.
+* Support for targets with pointer-width 16
+* Implement `Encode` for all array lengths using const-generics.
+* Implement `Encode` for unsized pointer types as well.
+
+### Changed
+* **BREAKING**: Forked the project, so it is now available under the name
+ `objc2-encode`.
+* **BREAKING**: Changed type in `Encoding::BitField` from `u32` to `u8`.
+* **BREAKING**: Changed type in `Encoding::Array` from `u32` to `usize`.
+* **BREAKING**: Loosen `'static` bounds on references implementing `Encode`.
+
+
+## [1.1.0] (`objc-encode` crate) - 2019-10-16
+
+### Added
+* Implement `Encode` for arrays with up to 32 elements.
+
+### Changed
+* Simplify internal encoding comparison.
+
+
+## [1.0.0] (`objc-encode` crate) - 2019-03-25
+
+### Added
+* Implement `PartialEq` between `Encoding` and `&str`.
+
+### Changed
+* **BREAKING**: Make `Encoding` an enum instead of a trait, yielding a vastly
+ different design. This makes use of associated constants.
+* **BREAKING**: Rename `Encode::CODE` to `Encode::ENCODING`.
+* Update to Rust 2018.
+
+### Removed
+* `libc` dependency.
+
+
+## [0.0.3] (`objc-encode` crate) - 2017-04-30
+
+### Fixed
+* Compilation on versions prior to Rust `1.15`.
+
+
+## [0.0.2] (`objc-encode` crate) - 2017-02-20
+
+### Added
+* **BREAKING**: `Display` requirement for encodings.
+* Implement `PartialEq` for encodings.
+* Implement `Encode` for pointers when references do.
+
+### Fixed
+* `IndexEncodingsComparator`.
+* Compilation with older Rust versions.
+
+
+## [0.0.1] (`objc-encode` crate) - 2017-02-19
+
+Initial version.
+
+
+[1.1.0]: https://github.com/madsmtm/objc2/compare/objc-encode-1.0.0...objc-encode-1.1.0
+[1.0.0]: https://github.com/madsmtm/objc2/compare/objc-encode-0.0.3...objc-encode-1.0.0
+[0.0.3]: https://github.com/madsmtm/objc2/compare/objc-encode-0.0.2...objc-encode-0.0.3
+[0.0.2]: https://github.com/madsmtm/objc2/compare/objc-encode-0.0.1...objc-encode-0.0.2
+[0.0.1]: https://github.com/madsmtm/objc2/releases/tag/objc-encode-0.0.1
diff --git a/third_party/rust/objc2-encode/Cargo.lock b/third_party/rust/objc2-encode/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "objc2-encode"
+version = "4.1.0"
diff --git a/third_party/rust/objc2-encode/Cargo.toml b/third_party/rust/objc2-encode/Cargo.toml
@@ -0,0 +1,81 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2021"
+rust-version = "1.71"
+name = "objc2-encode"
+version = "4.1.0"
+authors = ["Mads Marquart <mads@marquart.dk>"]
+build = false
+autolib = false
+autobins = false
+autoexamples = false
+autotests = false
+autobenches = false
+description = "Objective-C type-encoding representation and parsing"
+readme = "README.md"
+keywords = [
+ "objective-c",
+ "macos",
+ "ios",
+ "encode",
+]
+categories = [
+ "development-tools::ffi",
+ "encoding",
+ "no-std",
+ "os::macos-apis",
+]
+license = "MIT"
+repository = "https://github.com/madsmtm/objc2"
+
+[package.metadata.docs.rs]
+default-target = "aarch64-apple-darwin"
+targets = [
+ "aarch64-apple-darwin",
+ "x86_64-apple-darwin",
+ "aarch64-apple-ios",
+ "aarch64-apple-tvos",
+ "aarch64-apple-watchos",
+ "aarch64-apple-ios-macabi",
+ "aarch64-apple-visionos",
+ "x86_64-unknown-linux-gnu",
+ "i686-unknown-linux-gnu",
+]
+
+[package.metadata.release]
+shared-version = false
+tag-prefix = "objc-encode"
+
+[features]
+alloc = []
+default = ["std"]
+std = ["alloc"]
+
+[lib]
+name = "objc2_encode"
+path = "src/lib.rs"
+
+[lints.clippy]
+ptr_as_ptr = "warn"
+redundant_feature_names = "allow"
+
+[lints.clippy.cargo]
+level = "warn"
+priority = -1
+
+[lints.rust]
+elided_lifetimes_in_paths = "warn"
+missing_copy_implementations = "warn"
+non_ascii_idents = "deny"
+unreachable_pub = "warn"
+unsafe_op_in_unsafe_fn = "deny"
diff --git a/third_party/rust/objc2-encode/README.md b/third_party/rust/objc2-encode/README.md
@@ -0,0 +1,16 @@
+# `objc2-encode`
+
+[](https://crates.io/crates/objc2-encode)
+[](https://github.com/madsmtm/objc2/blob/master/LICENSE.txt)
+[](https://docs.rs/objc2-encode/)
+[](https://github.com/madsmtm/objc2/actions/workflows/ci.yml)
+
+Objective-C type-encoding in Rust.
+
+This crates provides types to parse and compare Objective-C type-encodings
+created with the `@encode` directive.
+
+See [the docs](https://docs.rs/objc2-encode/) for a more thorough overview.
+
+This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2),
+see that for related crates.
diff --git a/third_party/rust/objc2-encode/src/encoding.rs b/third_party/rust/objc2-encode/src/encoding.rs
@@ -0,0 +1,726 @@
+use core::fmt;
+
+use crate::helper::{compare_encodings, Helper, NestingLevel};
+use crate::parse::Parser;
+use crate::EncodingBox;
+
+/// An Objective-C type-encoding.
+///
+/// Can be retrieved in Objective-C for a type `T` using the `@encode(T)`
+/// directive.
+/// ```objc
+/// NSLog(@"Encoding of NSException: %s", @encode(NSException));
+/// ```
+///
+/// The [`Display`][`fmt::Display`] implementation converts the [`Encoding`]
+/// into its string representation, that the the `@encode` directive would
+/// return. This can be used conveniently through the `to_string` method:
+///
+/// ```
+/// use objc2_encode::Encoding;
+/// assert_eq!(Encoding::Int.to_string(), "i");
+/// ```
+///
+/// For more information on the string value of an encoding, see [Apple's
+/// documentation][ocrtTypeEncodings].
+///
+/// [ocrtTypeEncodings]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
+///
+/// # Examples
+///
+/// Comparing an encoding to a string from the Objective-C runtime:
+///
+/// ```
+/// use objc2_encode::Encoding;
+/// assert!(Encoding::Array(10, &Encoding::FloatComplex).equivalent_to_str("[10jf]"));
+/// ```
+// Not `Copy`, since this may one day be merged with `EncodingBox`
+#[allow(missing_copy_implementations)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+// See <https://en.cppreference.com/w/c/language/type>
+#[non_exhaustive] // Maybe we're missing some encodings?
+pub enum Encoding {
+ /// A C `char`. Corresponds to the `"c"` code.
+ Char,
+ /// A C `short`. Corresponds to the `"s"` code.
+ Short,
+ /// A C `int`. Corresponds to the `"i"` code.
+ Int,
+ /// A C `long`. Corresponds to the `"l"` code.
+ ///
+ /// This is treated as a 32-bit quantity in 64-bit programs, see
+ /// [`Encoding::C_LONG`].
+ Long,
+ /// A C `long long`. Corresponds to the `"q"` code.
+ LongLong,
+ /// A C `unsigned char`. Corresponds to the `"C"` code.
+ UChar,
+ /// A C `unsigned short`. Corresponds to the `"S"` code.
+ UShort,
+ /// A C `unsigned int`. Corresponds to the `"I"` code.
+ UInt,
+ /// A C `unsigned long`. Corresponds to the `"L"` code.
+ ///
+ /// See [`Encoding::C_ULONG`].
+ ULong,
+ /// A C `unsigned long long`. Corresponds to the `"Q"` code.
+ ULongLong,
+ /// A C `float`. Corresponds to the `"f"` code.
+ Float,
+ /// A C `double`. Corresponds to the `"d"` code.
+ Double,
+ /// A C `long double`. Corresponds to the `"D"` code.
+ LongDouble,
+ /// A C `float _Complex`. Corresponds to the `"j" "f"` code.
+ FloatComplex,
+ /// A C `_Complex` or `double _Complex`. Corresponds to the `"j" "d"` code.
+ DoubleComplex,
+ /// A C `long double _Complex`. Corresponds to the `"j" "D"` code.
+ LongDoubleComplex,
+ /// A C++ `bool` / C99 `_Bool`. Corresponds to the `"B"` code.
+ Bool,
+ /// A C `void`. Corresponds to the `"v"` code.
+ Void,
+ /// A C `char *`. Corresponds to the `"*"` code.
+ String,
+ /// An Objective-C object (`id`). Corresponds to the `"@"` code.
+ ///
+ /// Some compilers may choose to store the name of the class in instance
+ /// variables and properties as `"@" class_name`, see [Extended Type Info
+ /// in Objective-C][ext] (note that this does not include generics).
+ ///
+ /// Such class names are currently ignored by `objc2-encode`.
+ ///
+ /// [ext]: https://bou.io/ExtendedTypeInfoInObjC.html
+ Object,
+ /// An Objective-C block. Corresponds to the `"@" "?"` code.
+ Block,
+ /// An Objective-C class (`Class`). Corresponds to the `"#"` code.
+ Class,
+ /// An Objective-C selector (`SEL`). Corresponds to the `":"` code.
+ Sel,
+ /// An unknown type. Corresponds to the `"?"` code.
+ ///
+ /// This is usually used to encode functions.
+ Unknown,
+ /// A bitfield with the given number of bits, and the given type.
+ ///
+ /// Corresponds to the `"b" size` code.
+ ///
+ /// On GNUStep, this uses the `"b" offset type size` code, so this
+ /// contains an `Option` that should be set for that. Only integral types
+ /// are possible for the type.
+ ///
+ /// A `BitField(_, Some(_))` and a `BitField(_, None)` do _not_ compare
+ /// equal; instead, you should set the bitfield correctly depending on the
+ /// target platform.
+ BitField(u8, Option<&'static (u64, Encoding)>),
+ /// A pointer to the given type.
+ ///
+ /// Corresponds to the `"^" type` code.
+ Pointer(&'static Encoding),
+ /// A C11 [`_Atomic`] type.
+ ///
+ /// Corresponds to the `"A" type` code. Not all encodings are possible in
+ /// this.
+ ///
+ /// [`_Atomic`]: https://en.cppreference.com/w/c/language/atomic
+ Atomic(&'static Encoding),
+ /// An array with the given length and type.
+ ///
+ /// Corresponds to the `"[" length type "]"` code.
+ Array(u64, &'static Encoding),
+ /// A struct with the given name and fields.
+ ///
+ /// The order of the fields must match the order of the order in this.
+ ///
+ /// It is not uncommon for the name to be `"?"`.
+ ///
+ /// Corresponds to the `"{" name "=" fields... "}"` code.
+ ///
+ /// Note that the `=` may be omitted in some situations; this is
+ /// considered equal to the case where there are no fields.
+ Struct(&'static str, &'static [Encoding]),
+ /// A union with the given name and members.
+ ///
+ /// The order of the members must match the order of the order in this.
+ ///
+ /// Corresponds to the `"(" name "=" members... ")"` code.
+ ///
+ /// Note that the `=` may be omitted in some situations; this is
+ /// considered equal to the case where there are no members.
+ Union(&'static str, &'static [Encoding]),
+ /// The type does not have an Objective-C encoding.
+ ///
+ /// This is usually only used on types where Clang fails to generate the
+ /// Objective-C encoding, like SIMD types marked with
+ /// `__attribute__((__ext_vector_type__(1)))`.
+ None,
+ // TODO: "Vector" types have the '!' encoding, but are not implemented in
+ // clang
+
+ // TODO: `t` and `T` codes for i128 and u128?
+}
+
+impl Encoding {
+ /// The encoding of [`c_long`](`std::os::raw::c_long`) on the current
+ /// target.
+ ///
+ /// Ideally the encoding of `long` would be the same as `int` when it's 32
+ /// bits wide and the same as `long long` when it is 64 bits wide; then
+ /// `c_long::ENCODING` would just work.
+ ///
+ /// Unfortunately, `long` have a different encoding than `int` when it is
+ /// 32 bits wide; the [`l`][`Encoding::Long`] encoding.
+ pub const C_LONG: Self = {
+ // TODO once `core::ffi::c_long` is in MSRV
+ // `mem::size_of::<c_long>() == 4`
+ //
+ // That would exactly match what `clang` does:
+ // https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/AST/ASTContext.cpp#L7245
+ if cfg!(any(target_pointer_width = "32", windows)) {
+ // @encode(long) = 'l'
+ Self::Long
+ } else {
+ // @encode(long) = 'q'
+ Self::LongLong
+ }
+ };
+
+ /// The encoding of [`c_ulong`](`std::os::raw::c_ulong`) on the current
+ /// target.
+ ///
+ /// See [`Encoding::C_LONG`] for explanation.
+ pub const C_ULONG: Self = {
+ if cfg!(any(target_pointer_width = "32", windows)) {
+ // @encode(unsigned long) = 'L'
+ Self::ULong
+ } else {
+ // @encode(unsigned long) = 'Q'
+ Self::ULongLong
+ }
+ };
+
+ /// Check if one encoding is equivalent to another.
+ ///
+ /// Currently, equivalence testing mostly requires that the encodings are
+ /// equal, except for:
+ /// - Any leading qualifiers that the encoding may have.
+ /// - Structs or unions behind multiple pointers are considered
+ /// equivalent, since Objective-C compilers strip this information to
+ /// avoid unnecessary nesting.
+ /// - Structs or unions with no fields/members are considered to represent
+ /// "opqaue" types, and will therefore be equivalent to all other
+ /// structs / unions.
+ /// - [`Object`], [`Block`] and [`Class`] compare as equivalent.
+ ///
+ /// The comparison may be changed in the future to e.g. ignore struct
+ /// names or similar changes that may be required because of limitations
+ /// in Objective-C compiler implementations.
+ ///
+ /// For example, you should not rely on two equivalent encodings to have
+ /// the same size or ABI - that is provided on a best-effort basis.
+ ///
+ /// [`Object`]: Self::Object
+ /// [`Block`]: Self::Block
+ /// [`Class`]: Self::Class
+ pub fn equivalent_to(&self, other: &Self) -> bool {
+ compare_encodings(self, other, NestingLevel::new(), false)
+ }
+
+ /// Check if an encoding is equivalent to the given string representation.
+ ///
+ /// See [`Encoding::equivalent_to`] for details about the meaning of
+ /// "equivalence".
+ pub fn equivalent_to_str(&self, s: &str) -> bool {
+ let mut parser = Parser::new(s);
+
+ parser.strip_leading_qualifiers();
+
+ if let Some(()) = parser.expect_encoding(self, NestingLevel::new()) {
+ // if the given encoding can be successfully removed from the
+ // start and an empty string remains, they were fully equivalent!
+ parser.is_empty()
+ } else {
+ false
+ }
+ }
+
+ /// Check if an encoding is equivalent to a boxed encoding.
+ ///
+ /// See [`Encoding::equivalent_to`] for details about the meaning of
+ /// "equivalence".
+ pub fn equivalent_to_box(&self, other: &EncodingBox) -> bool {
+ compare_encodings(self, other, NestingLevel::new(), false)
+ }
+
+ /// Computes the theoretical size in bytes of the represented value type.
+ ///
+ /// The size is only valid for the current target.
+ ///
+ /// This does not currently consider alignment, i.e. everything is
+ /// considered packed, but that may change in the future.
+ pub fn size(&self) -> Option<usize> {
+ Helper::new(self).size(NestingLevel::new())
+ }
+}
+
+/// Formats this [`Encoding`] in a similar way that the `@encode` directive
+/// would ordinarily do.
+///
+/// You should not rely on the output of this to be stable across versions. It
+/// may change if found to be required to be compatible with existing
+/// Objective-C compilers.
+impl fmt::Display for Encoding {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ Helper::new(self).fmt(f, NestingLevel::new())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::static_str::{static_encoding_str_array, static_encoding_str_len};
+ use alloc::boxed::Box;
+ use alloc::string::ToString;
+ use alloc::vec;
+ use core::str::FromStr;
+
+ fn send_sync<T: Send + Sync>() {}
+
+ #[test]
+ fn test_send_sync() {
+ send_sync::<Encoding>();
+ }
+
+ #[test]
+ fn smoke() {
+ assert!(Encoding::Short.equivalent_to_str("s"));
+ }
+
+ #[test]
+ fn qualifiers() {
+ assert!(Encoding::Void.equivalent_to_str("v"));
+ assert!(Encoding::Void.equivalent_to_str("Vv"));
+ assert!(Encoding::String.equivalent_to_str("*"));
+ assert!(Encoding::String.equivalent_to_str("r*"));
+ }
+
+ macro_rules! assert_enc {
+ ($(
+ fn $name:ident() {
+ $encoding:expr;
+ $(
+ ~$equivalent_encoding:expr;
+ )*
+ $(
+ !$not_encoding:expr;
+ )*
+ $string:literal;
+ $(
+ ~$equivalent_string:expr;
+ )*
+ $(
+ !$not_string:literal;
+ )*
+ }
+ )+) => {$(
+ #[test]
+ fn $name() {
+ const E: Encoding = $encoding;
+
+ // Check PartialEq
+ assert_eq!(E, E, "equal");
+
+ // Check Display
+ assert_eq!(E.to_string(), $string, "equal to string");
+
+ // Check encoding box
+ let boxed = EncodingBox::from_str($string).expect("parse");
+ assert_eq!(boxed.to_string(), $string, "parsed");
+
+ // Check equivalence comparisons
+ assert!(E.equivalent_to(&E), "equivalent self");
+ assert!(E.equivalent_to_str($string), "equivalent self string {}", $string);
+ assert!(E.equivalent_to_box(&boxed), "equivalent self boxed");
+ $(
+ assert!(E.equivalent_to(&$equivalent_encoding), "equivalent encoding {}", $equivalent_encoding);
+ assert!(E.equivalent_to_str(&$equivalent_encoding.to_string()), "equivalent encoding string");
+ let boxed = EncodingBox::from_str(&$equivalent_encoding.to_string()).expect("parse equivalent encoding");
+ assert!(E.equivalent_to_box(&boxed), "equivalent encoding boxed");
+ )*
+ $(
+ assert!(E.equivalent_to_str($equivalent_string), "equivalent string {}", $equivalent_string);
+ let boxed = EncodingBox::from_str($equivalent_string).expect("parse equivalent string");
+ assert!(E.equivalent_to_box(&boxed), "equivalent string boxed");
+ )*
+
+ // Negative checks
+ $(
+ assert_ne!(E, $not_encoding, "not equal");
+ assert!(!E.equivalent_to(&$not_encoding), "not equivalent encoding");
+ assert!(!E.equivalent_to_str(&$not_encoding.to_string()), "not equivalent encoding string");
+ let boxed = EncodingBox::from_str(&$not_encoding.to_string()).expect("parse not equivalent encoding");
+ assert!(!E.equivalent_to_box(&boxed), "not equivalent boxed");
+ )*
+ $(
+ assert!(!E.equivalent_to_str(&$not_string), "not equivalent string");
+ )*
+
+ // Check static str
+ const STATIC_ENCODING_DATA: [u8; static_encoding_str_len(&E, NestingLevel::new())] = static_encoding_str_array(&E, NestingLevel::new());
+ const STATIC_ENCODING_STR: &str = unsafe { core::str::from_utf8_unchecked(&STATIC_ENCODING_DATA) };
+ assert_eq!(STATIC_ENCODING_STR, $string, "static");
+ }
+ )+};
+ }
+
+ assert_enc! {
+ fn int() {
+ Encoding::Int;
+ !Encoding::Char;
+ "i";
+ }
+
+ fn char() {
+ Encoding::Char;
+ !Encoding::Int;
+ "c";
+ // Qualifiers
+ ~"rc";
+ ~"nc";
+ ~"Nc";
+ ~"oc";
+ ~"Oc";
+ ~"Rc";
+ ~"Vc";
+
+ !"ri";
+ }
+
+ fn block() {
+ Encoding::Block;
+ ~Encoding::Class;
+ ~Encoding::Object;
+ !Encoding::Unknown;
+ "@?";
+ }
+
+ fn object() {
+ Encoding::Object;
+ ~Encoding::Block;
+ ~Encoding::Class;
+ !Encoding::Sel;
+ "@";
+ ~"@\"AnyClassName\"";
+ ~"@\"\""; // Empty class name
+ ~"@?";
+ ~"#";
+ !"@\"MyClassName";
+ !"@MyClassName\"";
+ }
+
+ fn unknown() {
+ Encoding::Unknown;
+ !Encoding::Block;
+ "?";
+ }
+
+ fn double() {
+ Encoding::Double;
+ "d";
+ }
+
+ fn bitfield() {
+ Encoding::BitField(4, None);
+ !Encoding::Int;
+ !Encoding::BitField(5, None);
+ !Encoding::BitField(4, Some(&(0, Encoding::Bool)));
+ "b4";
+ !"b4a";
+ !"b4c";
+ !"b4B";
+ !"b";
+ !"b-4";
+ !"b0B4";
+ }
+
+ fn bitfield_gnustep() {
+ Encoding::BitField(4, Some(&(16, Encoding::Bool)));
+ !Encoding::Int;
+ !Encoding::BitField(4, None);
+ !Encoding::BitField(5, Some(&(16, Encoding::Bool)));
+ !Encoding::BitField(4, Some(&(20, Encoding::Bool)));
+ !Encoding::BitField(4, Some(&(16, Encoding::Char)));
+ "b16B4";
+ !"b4";
+ !"b16B";
+ !"b20B4";
+ !"b16B5";
+ !"b16c4";
+ !"b4a";
+ !"b";
+ !"b-4";
+ }
+
+ fn atomic() {
+ Encoding::Atomic(&Encoding::Int);
+ !Encoding::Pointer(&Encoding::Int);
+ !Encoding::Atomic(&Encoding::Char);
+ !Encoding::Atomic(&Encoding::Atomic(&Encoding::Int));
+ "Ai";
+ }
+
+ fn atomic_string() {
+ Encoding::Atomic(&Encoding::String);
+ "A*";
+ }
+
+ fn pointer() {
+ Encoding::Pointer(&Encoding::Int);
+ !Encoding::Atomic(&Encoding::Int);
+ !Encoding::Pointer(&Encoding::Char);
+ !Encoding::Pointer(&Encoding::Pointer(&Encoding::Int));
+ "^i";
+ }
+
+ fn array() {
+ Encoding::Array(12, &Encoding::Int);
+ !Encoding::Int;
+ !Encoding::Array(11, &Encoding::Int);
+ !Encoding::Array(12, &Encoding::Char);
+ "[12i]";
+ !"[12i";
+ }
+
+ fn struct_() {
+ Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int]);
+ ~Encoding::Struct("SomeStruct", &[]);
+ !Encoding::Union("SomeStruct", &[Encoding::Char, Encoding::Int]);
+ !Encoding::Int;
+ !Encoding::Struct("SomeStruct", &[Encoding::Int]);
+ !Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int, Encoding::Int]);
+ !Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char]);
+ !Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int]);
+ "{SomeStruct=ci}";
+ ~"{SomeStruct=}";
+ !"{SomeStruct}";
+ !"{SomeStruct=ic}";
+ !"{SomeStruct=malformed";
+ }
+
+ fn pointer_struct() {
+ Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int]));
+ ~Encoding::Pointer(&Encoding::Struct("SomeStruct", &[]));
+ !Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char]));
+ !Encoding::Pointer(&Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int]));
+ "^{SomeStruct=ci}";
+ ~"^{SomeStruct=}";
+ !"^{SomeStruct}";
+ !"^{SomeStruct=ic}";
+ !"^{SomeStruct=malformed";
+ }
+
+ fn pointer_pointer_struct() {
+ Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int])));
+ ~Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("SomeStruct", &[])));
+ ~Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char])));
+ !Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int])));
+ "^^{SomeStruct}";
+ !"^^{SomeStruct=}";
+ !"^^{SomeStruct=ci}";
+ !"^^{SomeStruct=ic}";
+ !"^^{AnotherName=ic}";
+ !"^^{SomeStruct=malformed";
+ }
+
+ fn atomic_struct() {
+ Encoding::Atomic(&Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int]));
+ ~Encoding::Atomic(&Encoding::Struct("SomeStruct", &[]));
+ ~Encoding::Atomic(&Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char]));
+ !Encoding::Atomic(&Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int]));
+ "A{SomeStruct}";
+ !"A{SomeStruct=}";
+ !"A{SomeStruct=ci}";
+ !"A{SomeStruct=ic}";
+ !"A{SomeStruct=malformed";
+ }
+
+ fn empty_struct() {
+ Encoding::Struct("SomeStruct", &[]);
+ "{SomeStruct=}";
+ ~"{SomeStruct=ci}";
+ !"{SomeStruct}";
+ }
+
+ fn union_() {
+ Encoding::Union("Onion", &[Encoding::Char, Encoding::Int]);
+ !Encoding::Struct("Onion", &[Encoding::Char, Encoding::Int]);
+ !Encoding::Int;
+ !Encoding::Union("Onion", &[Encoding::Int, Encoding::Char]);
+ !Encoding::Union("AnotherUnion", &[Encoding::Char, Encoding::Int]);
+ "(Onion=ci)";
+ !"(Onion=ci";
+ }
+
+ fn nested() {
+ Encoding::Struct(
+ "A",
+ &[
+ Encoding::Struct("B", &[Encoding::Int]),
+ Encoding::Pointer(&Encoding::Struct("C", &[Encoding::Double])),
+ Encoding::Char,
+ ],
+ );
+ ~Encoding::Struct(
+ "A",
+ &[
+ Encoding::Struct("B", &[Encoding::Int]),
+ Encoding::Pointer(&Encoding::Struct("C", &[])),
+ Encoding::Char,
+ ],
+ );
+ "{A={B=i}^{C}c}";
+ !"{A={B=i}^{C=d}c}";
+ !"{A={B=i}^{C=i}c}";
+ !"{A={B=i}^{C=d}c";
+ }
+
+ fn nested_pointer() {
+ Encoding::Pointer(&Encoding::Struct(
+ "A",
+ &[
+ Encoding::Struct("B", &[Encoding::Int]),
+ Encoding::Pointer(&Encoding::Struct("C", &[Encoding::Double])),
+ ],
+ ));
+ "^{A={B=i}^{C}}";
+ !"^{A={B}^{C}}";
+ !"^{A={B=i}^{C=d}}";
+ }
+
+ fn various() {
+ Encoding::Struct(
+ "abc",
+ &[
+ Encoding::Pointer(&Encoding::Array(8, &Encoding::Bool)),
+ Encoding::Union("def", &[Encoding::Block]),
+ Encoding::Pointer(&Encoding::Pointer(&Encoding::BitField(255, None))),
+ Encoding::Char,
+ Encoding::Unknown,
+ ]
+ );
+ "{abc=^[8B](def=@?)^^b255c?}";
+ ~"{abc=}";
+ !"{abc}";
+ }
+
+ fn identifier() {
+ Encoding::Struct("_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", &[]);
+ "{_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=}";
+ }
+
+ // Regression test. The encoding of the `CGLContextObj` object changed
+ // between versions of macOS. As such, this is something that we must
+ // be prepared to handle.
+ fn cgl_context_obj() {
+ Encoding::Pointer(&Encoding::Struct("_CGLContextObject", &[]));
+ "^{_CGLContextObject=}";
+ ~"^{_CGLContextObject=^{__GLIContextRec}{__GLIFunctionDispatchRec=^?^?^?^?^?}^{_CGLPrivateObject}^v}";
+ !"^{_CGLContextObject}";
+ !"^{SomeOtherStruct=}";
+ }
+
+ fn none() {
+ Encoding::None;
+ "";
+ !"?";
+ }
+
+ fn none_in_array() {
+ Encoding::Array(42, &Encoding::None);
+ !Encoding::Array(42, &Encoding::Unknown);
+ "[42]";
+ !"[42i]";
+ }
+
+ fn none_in_pointer() {
+ Encoding::Pointer(&Encoding::None);
+ !Encoding::Pointer(&Encoding::Unknown);
+ "^";
+ !"";
+ !"^i";
+ }
+
+ fn none_in_pointer_in_array() {
+ Encoding::Array(42, &Encoding::Pointer(&Encoding::None));
+ "[42^]";
+ }
+
+ fn class() {
+ Encoding::Class;
+ ~Encoding::Object;
+ ~Encoding::Block;
+ !Encoding::Sel;
+ "#";
+ ~"@?";
+ ~"@";
+ !"a";
+ }
+ }
+
+ #[test]
+ #[should_panic = "Struct name was not a valid identifier"]
+ fn struct_empty() {
+ let _ = Encoding::Struct("", &[]).to_string();
+ }
+
+ #[test]
+ #[should_panic = "Struct name was not a valid identifier"]
+ fn struct_unicode() {
+ let _ = Encoding::Struct("☃", &[Encoding::Char]).to_string();
+ }
+
+ #[test]
+ #[should_panic = "Union name was not a valid identifier"]
+ fn union_invalid_identifier() {
+ let _ = Encoding::Union("a-b", &[Encoding::Char]).equivalent_to_str("(☃=c)");
+ }
+
+ // Note: A raw `?` cannot happen in practice, since functions can only
+ // be accessed through pointers, and that will yield `^?`
+ #[test]
+ fn object_unknown_in_struct() {
+ let enc = Encoding::Struct("S", &[Encoding::Block, Encoding::Object, Encoding::Unknown]);
+ let s = "{S=@?@?}";
+
+ assert_eq!(&enc.to_string(), s);
+
+ let parsed = EncodingBox::from_str(s).unwrap();
+ let expected = EncodingBox::Struct(
+ "S".to_string(),
+ vec![EncodingBox::Block, EncodingBox::Block],
+ );
+ assert_eq!(parsed, expected);
+
+ assert!(!enc.equivalent_to_box(&expected));
+ }
+
+ // Similar to `?`, `` cannot be accurately represented inside pointers
+ // inside structs, and may be parsed incorrectly.
+ #[test]
+ fn none_in_struct() {
+ let enc = Encoding::Struct("?", &[Encoding::Pointer(&Encoding::None), Encoding::Int]);
+ let s = "{?=^i}";
+ assert_eq!(&enc.to_string(), s);
+
+ let parsed = EncodingBox::from_str(s).unwrap();
+ let expected = EncodingBox::Struct(
+ "?".to_string(),
+ vec![EncodingBox::Pointer(Box::new(EncodingBox::Int))],
+ );
+ assert_eq!(parsed, expected);
+
+ assert!(!enc.equivalent_to_box(&expected));
+ }
+}
diff --git a/third_party/rust/objc2-encode/src/encoding_box.rs b/third_party/rust/objc2-encode/src/encoding_box.rs
@@ -0,0 +1,265 @@
+use alloc::boxed::Box;
+use alloc::string::String;
+use alloc::vec::Vec;
+use core::fmt;
+use core::str::FromStr;
+
+use crate::helper::{compare_encodings, Helper, NestingLevel};
+use crate::parse::{ErrorKind, ParseError, Parser};
+use crate::Encoding;
+
+/// The boxed version of [`Encoding`].
+///
+/// This has exactly the same items as `Encoding`, the only difference is in
+/// where the contents of the more complex encodings like [`Struct`] are
+/// stored.
+///
+/// In `Encoding`, the data is stored in static memory, while in `EncodingBox`
+/// it is stored on the heap. The former allows storing in constants (which is
+/// required by the `objc2::encode::Encode` and `objc2::encode::RefEncode`
+/// traits), while the latter allows dynamic creation, such as in the case of
+/// parsing encodings.
+///
+/// **This should be considered a _temporary_ restriction**. `Encoding` and
+/// `EncodingBox` will become equivalent once heap allocation in constants
+/// is possible.
+///
+/// [`Struct`]: Self::Struct
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+#[non_exhaustive] // Maybe we're missing some encodings?
+pub enum EncodingBox {
+ /// Same as [`Encoding::Char`].
+ Char,
+ /// Same as [`Encoding::Short`].
+ Short,
+ /// Same as [`Encoding::Int`].
+ Int,
+ /// Same as [`Encoding::Long`].
+ Long,
+ /// Same as [`Encoding::LongLong`].
+ LongLong,
+ /// Same as [`Encoding::UChar`].
+ UChar,
+ /// Same as [`Encoding::UShort`].
+ UShort,
+ /// Same as [`Encoding::UInt`].
+ UInt,
+ /// Same as [`Encoding::ULong`].
+ ULong,
+ /// Same as [`Encoding::ULongLong`].
+ ULongLong,
+ /// Same as [`Encoding::Float`].
+ Float,
+ /// Same as [`Encoding::Double`].
+ Double,
+ /// Same as [`Encoding::LongDouble`].
+ LongDouble,
+ /// Same as [`Encoding::FloatComplex`].
+ FloatComplex,
+ /// Same as [`Encoding::DoubleComplex`].
+ DoubleComplex,
+ /// Same as [`Encoding::LongDoubleComplex`].
+ LongDoubleComplex,
+ /// Same as [`Encoding::Bool`].
+ Bool,
+ /// Same as [`Encoding::Void`].
+ Void,
+ /// Same as [`Encoding::String`].
+ String,
+ /// Same as [`Encoding::Object`].
+ Object,
+ /// Same as [`Encoding::Block`].
+ Block,
+ /// Same as [`Encoding::Class`].
+ Class,
+ /// Same as [`Encoding::Sel`].
+ Sel,
+ /// Same as [`Encoding::Unknown`].
+ Unknown,
+ /// Same as [`Encoding::BitField`].
+ BitField(u8, Option<Box<(u64, Self)>>),
+ /// Same as [`Encoding::Pointer`].
+ Pointer(Box<Self>),
+ /// Same as [`Encoding::Atomic`].
+ Atomic(Box<Self>),
+ /// Same as [`Encoding::Array`].
+ Array(u64, Box<Self>),
+ /// Same as [`Encoding::Struct`].
+ Struct(String, Vec<Self>),
+ /// Same as [`Encoding::Union`].
+ Union(String, Vec<Self>),
+ /// Same as [`Encoding::None`].
+ None,
+}
+
+impl EncodingBox {
+ /// Same as [`Encoding::C_LONG`].
+ pub const C_LONG: Self = match Encoding::C_LONG {
+ Encoding::Long => Self::Long,
+ Encoding::LongLong => Self::LongLong,
+ _ => unreachable!(),
+ };
+
+ /// Same as [`Encoding::C_ULONG`].
+ pub const C_ULONG: Self = match Encoding::C_ULONG {
+ Encoding::ULong => Self::ULong,
+ Encoding::ULongLong => Self::ULongLong,
+ _ => unreachable!(),
+ };
+
+ /// Parse and consume an encoding from the start of a string.
+ ///
+ /// This is can be used to parse concatenated encodings, such as those
+ /// returned by `method_getTypeEncoding`.
+ ///
+ /// [`from_str`][Self::from_str] is simpler, use that instead if you can.
+ ///
+ ///
+ /// # Errors
+ ///
+ /// Returns an error if the string was an ill-formatted encoding string.
+ pub fn from_start_of_str(s: &mut &str) -> Result<Self, ParseError> {
+ let mut parser = Parser::new(s);
+ parser.strip_leading_qualifiers();
+
+ match parser.parse_encoding_or_none() {
+ Err(ErrorKind::Unknown(b'0'..=b'9')) => {
+ let remaining = parser.remaining();
+ *s = remaining;
+
+ Ok(EncodingBox::None)
+ }
+ Err(err) => Err(ParseError::new(parser, err)),
+ Ok(encoding) => {
+ let remaining = parser.remaining();
+ *s = remaining;
+
+ Ok(encoding)
+ }
+ }
+ }
+}
+
+/// Same formatting as [`Encoding`]'s `Display` implementation.
+impl fmt::Display for EncodingBox {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ Helper::from_box(self).fmt(f, NestingLevel::new())
+ }
+}
+
+impl PartialEq<Encoding> for EncodingBox {
+ fn eq(&self, other: &Encoding) -> bool {
+ compare_encodings(self, other, NestingLevel::new(), true)
+ }
+}
+
+impl PartialEq<EncodingBox> for Encoding {
+ fn eq(&self, other: &EncodingBox) -> bool {
+ other.eq(self)
+ }
+}
+
+impl FromStr for EncodingBox {
+ type Err = ParseError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let mut parser = Parser::new(s);
+ parser.strip_leading_qualifiers();
+
+ parser
+ .parse_encoding_or_none()
+ .and_then(|enc| parser.expect_empty().map(|()| enc))
+ .map_err(|err| ParseError::new(parser, err))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use alloc::string::ToString;
+ use alloc::vec;
+
+ #[test]
+ fn eq_encodings() {
+ let enc1 = Encoding::Char;
+ let enc2 = EncodingBox::Char;
+ let enc3 = EncodingBox::String;
+ assert_eq!(enc1, enc2);
+ assert_ne!(enc1, enc3);
+ }
+
+ #[test]
+ fn eq_complex_encodings() {
+ let enc1 = Encoding::Atomic(&Encoding::Struct(
+ "test",
+ &[Encoding::Array(2, &Encoding::Int)],
+ ));
+ let enc2 = EncodingBox::Atomic(Box::new(EncodingBox::Struct(
+ "test".to_string(),
+ vec![EncodingBox::Array(2, Box::new(EncodingBox::Int))],
+ )));
+ let enc3 = EncodingBox::Atomic(Box::new(EncodingBox::Struct(
+ "test".to_string(),
+ vec![EncodingBox::Array(2, Box::new(EncodingBox::Char))],
+ )));
+ assert_eq!(enc1, enc2);
+ assert_ne!(enc1, enc3);
+ }
+
+ #[test]
+ fn struct_nested_in_pointer() {
+ let enc1 = EncodingBox::Struct("test".to_string(), vec![EncodingBox::Char]);
+ let enc2 = EncodingBox::Struct("test".to_string(), vec![EncodingBox::Int]);
+ const ENC3A: Encoding = Encoding::Struct("test", &[Encoding::Char]);
+ assert_ne!(enc1, enc2);
+ assert!(ENC3A.equivalent_to_box(&enc1));
+ assert!(!ENC3A.equivalent_to_box(&enc2));
+
+ let enc1 = EncodingBox::Pointer(Box::new(enc1));
+ let enc2 = EncodingBox::Pointer(Box::new(enc2));
+ const ENC3B: Encoding = Encoding::Pointer(&ENC3A);
+ assert_ne!(enc1, enc2);
+ assert!(ENC3B.equivalent_to_box(&enc1));
+ assert!(!ENC3B.equivalent_to_box(&enc2));
+
+ let enc1 = EncodingBox::Pointer(Box::new(enc1));
+ let enc2 = EncodingBox::Pointer(Box::new(enc2));
+ const ENC3C: Encoding = Encoding::Pointer(&ENC3B);
+ assert_ne!(enc1, enc2);
+ assert!(ENC3C.equivalent_to_box(&enc1));
+ assert!(ENC3C.equivalent_to_box(&enc2), "now they're equivalent");
+ }
+
+ #[test]
+ fn parse_atomic_struct() {
+ let expected = EncodingBox::Atomic(Box::new(EncodingBox::Atomic(Box::new(
+ EncodingBox::Struct("a".into(), vec![]),
+ ))));
+ let actual = EncodingBox::from_str("AA{a=}").unwrap();
+ assert_eq!(expected, actual);
+ assert_eq!(expected.to_string(), "AA{a}");
+
+ let actual = EncodingBox::from_str("AA{a}").unwrap();
+ assert_eq!(expected, actual);
+ assert_eq!(expected.to_string(), "AA{a}");
+ }
+
+ #[test]
+ fn parse_part_of_string() {
+ let mut s = "{a}cb0i16";
+
+ let expected = EncodingBox::Struct("a".into(), vec![]);
+ let actual = EncodingBox::from_start_of_str(&mut s).unwrap();
+ assert_eq!(expected, actual);
+
+ let expected = EncodingBox::Char;
+ let actual = EncodingBox::from_start_of_str(&mut s).unwrap();
+ assert_eq!(expected, actual);
+
+ let expected = EncodingBox::BitField(16, Some(Box::new((0, EncodingBox::Int))));
+ let actual = EncodingBox::from_start_of_str(&mut s).unwrap();
+ assert_eq!(expected, actual);
+
+ assert_eq!(s, "");
+ }
+}
diff --git a/third_party/rust/objc2-encode/src/helper.rs b/third_party/rust/objc2-encode/src/helper.rs
@@ -0,0 +1,499 @@
+use core::ffi;
+use core::fmt;
+use core::mem;
+use core::slice;
+use core::write;
+
+use crate::parse::verify_name;
+use crate::Encoding;
+use crate::EncodingBox;
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub(crate) enum NestingLevel {
+ Top,
+ Within,
+ Bottom,
+}
+
+impl NestingLevel {
+ pub(crate) const fn new() -> Self {
+ Self::Top
+ }
+
+ pub(crate) const fn bitfield(self) -> Self {
+ // This is a bit irrelevant, since bitfields can only contain integral
+ // types
+ self
+ }
+
+ pub(crate) const fn indirection(self, kind: IndirectionKind) -> Self {
+ match kind {
+ // Move all the way down
+ IndirectionKind::Atomic => Self::Bottom,
+ // Move one step down
+ IndirectionKind::Pointer => match self {
+ Self::Top => Self::Within,
+ Self::Bottom | Self::Within => Self::Bottom,
+ },
+ }
+ }
+
+ pub(crate) const fn array(self) -> Self {
+ // TODO: Is this correct?
+ self
+ }
+
+ pub(crate) const fn container_include_fields(self) -> Option<Self> {
+ match self {
+ Self::Top | Self::Within => {
+ // Move top one step down
+ Some(Self::Within)
+ }
+ Self::Bottom => None,
+ }
+ }
+}
+
+pub(crate) fn compare_encodings<E1: EncodingType, E2: EncodingType>(
+ enc1: &E1,
+ enc2: &E2,
+ level: NestingLevel,
+ include_all: bool,
+) -> bool {
+ use Helper::*;
+ // Note: Ideally `Block` and sequence of `Object, Unknown` in struct
+ // should compare equivalent, but we don't bother since in practice a
+ // plain `Unknown` will never appear.
+
+ let level = if include_all {
+ NestingLevel::new()
+ } else {
+ level
+ };
+
+ match (enc1.helper(), enc2.helper()) {
+ (Primitive(p1), Primitive(p2)) => p1.equivalents().contains(&p2),
+ (BitField(size1, Some((offset1, type1))), BitField(size2, Some((offset2, type2)))) => {
+ size1 == size2
+ && offset1 == offset2
+ && compare_encodings(type1, type2, level.bitfield(), include_all)
+ }
+ (BitField(size1, None), BitField(size2, None)) => size1 == size2,
+ // The type-encoding of a bitfield is always either available, or it
+ // is not (depends on platform); so if it was available in one, but
+ // not the other, we should compare the encodings unequal.
+ (BitField(_, _), BitField(_, _)) => false,
+ (Indirection(kind1, t1), Indirection(kind2, t2)) => {
+ kind1 == kind2 && compare_encodings(t1, t2, level.indirection(kind1), include_all)
+ }
+ (Array(len1, item1), Array(len2, item2)) => {
+ len1 == len2 && compare_encodings(item1, item2, level.array(), include_all)
+ }
+ (Container(kind1, name1, items1), Container(kind2, name2, items2)) => {
+ kind1 == kind2 && name1 == name2 && {
+ if let Some(level) = level.container_include_fields() {
+ // If either container is empty, then they are equivalent
+ if items1.is_empty() || items2.is_empty() {
+ return true;
+ }
+ if items1.len() != items2.len() {
+ return false;
+ }
+ for (item1, item2) in items1.iter().zip(items2.iter()) {
+ if !compare_encodings(item1, item2, level, include_all) {
+ return false;
+ }
+ }
+ true
+ } else {
+ true
+ }
+ }
+ }
+ (NoneInvalid, NoneInvalid) => true,
+ (_, _) => false,
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+#[non_exhaustive]
+pub(crate) enum Primitive {
+ Char,
+ Short,
+ Int,
+ Long,
+ LongLong,
+ UChar,
+ UShort,
+ UInt,
+ ULong,
+ ULongLong,
+ Float,
+ Double,
+ LongDouble,
+ FloatComplex,
+ DoubleComplex,
+ LongDoubleComplex,
+ Bool,
+ Void,
+ String,
+ Object,
+ Block,
+ Class,
+ Sel,
+ Unknown,
+}
+
+impl Primitive {
+ pub(crate) const fn to_str(self) -> &'static str {
+ use Primitive::*;
+ match self {
+ Char => "c",
+ Short => "s",
+ Int => "i",
+ Long => "l",
+ LongLong => "q",
+ UChar => "C",
+ UShort => "S",
+ UInt => "I",
+ ULong => "L",
+ ULongLong => "Q",
+ Float => "f",
+ Double => "d",
+ LongDouble => "D",
+ FloatComplex => "jf",
+ DoubleComplex => "jd",
+ LongDoubleComplex => "jD",
+ Bool => "B",
+ Void => "v",
+ String => "*",
+ Object => "@",
+ Block => "@?",
+ Class => "#",
+ Sel => ":",
+ Unknown => "?",
+ }
+ }
+
+ /// Classes, blocks and objects can all be used in places where `id` is
+ /// expected (i.e. where the encoding is for objects).
+ ///
+ /// So to support those use-cases, we compare them as equivalent.
+ pub(crate) fn equivalents(&self) -> &[Self] {
+ match self {
+ Self::Block | Self::Object | Self::Class => &[Self::Block, Self::Object, Self::Class],
+ _ => slice::from_ref(self),
+ }
+ }
+
+ pub(crate) const fn size(self) -> Option<usize> {
+ match self {
+ // Under all the considered targets, `_Bool` is sized and aligned
+ // to a single byte. See:
+ // https://github.com/search?q=repo%3Allvm%2Fllvm-project+path%3Aclang%2Flib%2FBasic%2FTargets+BoolWidth&type=code
+ // Obj-C's `BOOL` is `signed char`, i.e. `c`, so will fall in the
+ // below case. See: https://developer.apple.com/documentation/objectivec/bool?language=objc
+ Self::Bool => Some(1),
+ // Numbers.
+ Self::Char => Some(mem::size_of::<ffi::c_char>()),
+ Self::UChar => Some(mem::size_of::<ffi::c_uchar>()),
+ Self::Short => Some(mem::size_of::<ffi::c_short>()),
+ Self::UShort => Some(mem::size_of::<ffi::c_ushort>()),
+ Self::Int => Some(mem::size_of::<ffi::c_int>()),
+ Self::UInt => Some(mem::size_of::<ffi::c_uint>()),
+ Self::Long => Some(mem::size_of::<ffi::c_long>()),
+ Self::ULong => Some(mem::size_of::<ffi::c_ulong>()),
+ Self::LongLong => Some(mem::size_of::<ffi::c_longlong>()),
+ Self::ULongLong => Some(mem::size_of::<ffi::c_ulonglong>()),
+ Self::Float => Some(mem::size_of::<ffi::c_float>()),
+ Self::Double => Some(mem::size_of::<ffi::c_double>()),
+ // https://github.com/search?q=repo%3Allvm%2Fllvm-project+path%3Aclang%2Flib%2FBasic%2FTargets+LongDoubleWidth&type=code
+ #[cfg(any(
+ target_arch = "x86_64",
+ all(target_arch = "x86", target_vendor = "apple"),
+ all(target_arch = "aarch64", not(target_vendor = "apple")),
+ ))]
+ Self::LongDouble => Some(16),
+ #[cfg(all(target_arch = "x86", not(target_vendor = "apple")))]
+ Self::LongDouble => Some(12),
+ #[cfg(any(
+ target_arch = "arm",
+ all(target_arch = "aarch64", target_vendor = "apple"),
+ ))]
+ Self::LongDouble => Some(8),
+ Self::FloatComplex => Some(mem::size_of::<ffi::c_float>() * 2),
+ Self::DoubleComplex => Some(mem::size_of::<ffi::c_double>() * 2),
+ Self::LongDoubleComplex => match Self::LongDouble.size() {
+ Some(size) => Some(size * 2),
+ None => None,
+ },
+ // Pointers.
+ Self::String | Self::Object | Self::Block | Self::Class | Self::Sel => {
+ Some(mem::size_of::<*const ()>())
+ }
+ // Nothing.
+ Self::Void | Self::Unknown => None,
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub(crate) enum IndirectionKind {
+ Atomic,
+ Pointer,
+}
+
+impl IndirectionKind {
+ pub(crate) const fn prefix(self) -> char {
+ self.prefix_byte() as char
+ }
+
+ pub(crate) const fn prefix_byte(self) -> u8 {
+ match self {
+ Self::Atomic => b'A',
+ Self::Pointer => b'^',
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub(crate) enum ContainerKind {
+ Struct,
+ Union,
+}
+
+impl ContainerKind {
+ pub(crate) const fn start(self) -> char {
+ self.start_byte() as char
+ }
+
+ pub(crate) const fn end(self) -> char {
+ self.end_byte() as char
+ }
+
+ pub(crate) const fn start_byte(self) -> u8 {
+ match self {
+ Self::Struct => b'{',
+ Self::Union => b'(',
+ }
+ }
+
+ pub(crate) const fn end_byte(self) -> u8 {
+ match self {
+ Self::Struct => b'}',
+ Self::Union => b')',
+ }
+ }
+}
+
+impl fmt::Display for ContainerKind {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::Struct => write!(f, "struct"),
+ Self::Union => write!(f, "union"),
+ }
+ }
+}
+
+pub(crate) trait EncodingType: Sized + fmt::Debug {
+ fn helper(&self) -> Helper<'_, Self>;
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+#[non_exhaustive]
+pub(crate) enum Helper<'a, E = Encoding> {
+ Primitive(Primitive),
+ BitField(u8, Option<&'a (u64, E)>),
+ Indirection(IndirectionKind, &'a E),
+ Array(u64, &'a E),
+ Container(ContainerKind, &'a str, &'a [E]),
+ NoneInvalid,
+}
+
+impl<E: EncodingType> Helper<'_, E> {
+ pub(crate) fn fmt(&self, f: &mut fmt::Formatter<'_>, level: NestingLevel) -> fmt::Result {
+ match self {
+ Self::Primitive(primitive) => {
+ write!(f, "{}", primitive.to_str())?;
+ }
+ Self::BitField(size, None) => {
+ write!(f, "b{size}")?;
+ }
+ Self::BitField(size, Some((offset, t))) => {
+ write!(f, "b{offset}")?;
+ t.helper().fmt(f, level.bitfield())?;
+ write!(f, "{size}")?;
+ }
+ Self::Indirection(kind, t) => {
+ write!(f, "{}", kind.prefix())?;
+ t.helper().fmt(f, level.indirection(*kind))?;
+ }
+ Self::Array(len, item) => {
+ write!(f, "[")?;
+ write!(f, "{len}")?;
+ item.helper().fmt(f, level.array())?;
+ write!(f, "]")?;
+ }
+ Self::Container(kind, name, items) => {
+ write!(f, "{}", kind.start())?;
+ write!(f, "{name}")?;
+ if let Some(level) = level.container_include_fields() {
+ write!(f, "=")?;
+ for item in *items {
+ item.helper().fmt(f, level)?;
+ }
+ }
+ write!(f, "{}", kind.end())?;
+ }
+ Self::NoneInvalid => {}
+ }
+ Ok(())
+ }
+
+ pub(crate) fn size(&self, level: NestingLevel) -> Option<usize> {
+ // TODO: alignment?
+ match self {
+ Self::NoneInvalid => None,
+ Self::Primitive(prim) => prim.size(),
+ Self::BitField(size_bits, off_typ) => Some(
+ (usize::from(*size_bits).next_power_of_two().max(8) / 8).max(
+ off_typ
+ .and_then(|(_, typ)| typ.helper().size(level.bitfield()))
+ .unwrap_or_default(),
+ ),
+ ),
+ Self::Indirection(kind, typ) => match kind {
+ IndirectionKind::Pointer => Some(mem::size_of::<*const ()>()),
+ IndirectionKind::Atomic => typ.helper().size(level.indirection(*kind)),
+ },
+ Self::Array(len, typ) => typ
+ .helper()
+ .size(level.array())
+ .map(|typ_size| *len as usize * typ_size),
+ Self::Container(kind, _, fields) => {
+ level
+ .container_include_fields()
+ .and_then(|level| match kind {
+ ContainerKind::Struct => {
+ fields.iter().map(|field| field.helper().size(level)).sum()
+ }
+ ContainerKind::Union => fields
+ .iter()
+ .map(|field| field.helper().size(level))
+ .max()
+ .flatten(),
+ })
+ }
+ }
+ }
+}
+
+impl Helper<'_> {
+ pub(crate) const fn new(encoding: &Encoding) -> Self {
+ use Encoding::*;
+ match encoding {
+ Char => Self::Primitive(Primitive::Char),
+ Short => Self::Primitive(Primitive::Short),
+ Int => Self::Primitive(Primitive::Int),
+ Long => Self::Primitive(Primitive::Long),
+ LongLong => Self::Primitive(Primitive::LongLong),
+ UChar => Self::Primitive(Primitive::UChar),
+ UShort => Self::Primitive(Primitive::UShort),
+ UInt => Self::Primitive(Primitive::UInt),
+ ULong => Self::Primitive(Primitive::ULong),
+ ULongLong => Self::Primitive(Primitive::ULongLong),
+ Float => Self::Primitive(Primitive::Float),
+ Double => Self::Primitive(Primitive::Double),
+ LongDouble => Self::Primitive(Primitive::LongDouble),
+ FloatComplex => Self::Primitive(Primitive::FloatComplex),
+ DoubleComplex => Self::Primitive(Primitive::DoubleComplex),
+ LongDoubleComplex => Self::Primitive(Primitive::LongDoubleComplex),
+ Bool => Self::Primitive(Primitive::Bool),
+ Void => Self::Primitive(Primitive::Void),
+ String => Self::Primitive(Primitive::String),
+ Object => Self::Primitive(Primitive::Object),
+ Block => Self::Primitive(Primitive::Block),
+ Class => Self::Primitive(Primitive::Class),
+ Sel => Self::Primitive(Primitive::Sel),
+ Unknown => Self::Primitive(Primitive::Unknown),
+ BitField(b, t) => Self::BitField(*b, *t),
+ Pointer(t) => Self::Indirection(IndirectionKind::Pointer, t),
+ Atomic(t) => Self::Indirection(IndirectionKind::Atomic, t),
+ Array(len, item) => Self::Array(*len, item),
+ Struct(name, fields) => {
+ if !verify_name(name) {
+ panic!("Struct name was not a valid identifier");
+ }
+ Self::Container(ContainerKind::Struct, name, fields)
+ }
+ Union(name, members) => {
+ if !verify_name(name) {
+ panic!("Union name was not a valid identifier");
+ }
+ Self::Container(ContainerKind::Union, name, members)
+ }
+ None => Self::NoneInvalid,
+ }
+ }
+}
+
+impl<'a> Helper<'a, EncodingBox> {
+ pub(crate) fn from_box(encoding: &'a EncodingBox) -> Self {
+ use EncodingBox::*;
+ match encoding {
+ Char => Self::Primitive(Primitive::Char),
+ Short => Self::Primitive(Primitive::Short),
+ Int => Self::Primitive(Primitive::Int),
+ Long => Self::Primitive(Primitive::Long),
+ LongLong => Self::Primitive(Primitive::LongLong),
+ UChar => Self::Primitive(Primitive::UChar),
+ UShort => Self::Primitive(Primitive::UShort),
+ UInt => Self::Primitive(Primitive::UInt),
+ ULong => Self::Primitive(Primitive::ULong),
+ ULongLong => Self::Primitive(Primitive::ULongLong),
+ Float => Self::Primitive(Primitive::Float),
+ Double => Self::Primitive(Primitive::Double),
+ LongDouble => Self::Primitive(Primitive::LongDouble),
+ FloatComplex => Self::Primitive(Primitive::FloatComplex),
+ DoubleComplex => Self::Primitive(Primitive::DoubleComplex),
+ LongDoubleComplex => Self::Primitive(Primitive::LongDoubleComplex),
+ Bool => Self::Primitive(Primitive::Bool),
+ Void => Self::Primitive(Primitive::Void),
+ String => Self::Primitive(Primitive::String),
+ Object => Self::Primitive(Primitive::Object),
+ Block => Self::Primitive(Primitive::Block),
+ Class => Self::Primitive(Primitive::Class),
+ Sel => Self::Primitive(Primitive::Sel),
+ Unknown => Self::Primitive(Primitive::Unknown),
+ BitField(b, t) => Self::BitField(*b, t.as_deref()),
+ Pointer(t) => Self::Indirection(IndirectionKind::Pointer, t),
+ Atomic(t) => Self::Indirection(IndirectionKind::Atomic, t),
+ Array(len, item) => Self::Array(*len, item),
+ Struct(name, fields) => {
+ if !verify_name(name) {
+ panic!("Struct name was not a valid identifier");
+ }
+ Self::Container(ContainerKind::Struct, name, fields)
+ }
+ Union(name, members) => {
+ if !verify_name(name) {
+ panic!("Union name was not a valid identifier");
+ }
+ Self::Container(ContainerKind::Union, name, members)
+ }
+ None => Self::NoneInvalid,
+ }
+ }
+}
+
+impl EncodingType for Encoding {
+ fn helper(&self) -> Helper<'_, Self> {
+ Helper::new(self)
+ }
+}
+
+impl EncodingType for EncodingBox {
+ fn helper(&self) -> Helper<'_, Self> {
+ Helper::from_box(self)
+ }
+}
diff --git a/third_party/rust/objc2-encode/src/lib.rs b/third_party/rust/objc2-encode/src/lib.rs
@@ -0,0 +1,64 @@
+//! # Objective-C type-encoding
+//!
+//! The Objective-C directive `@encode` encodes types as strings, and this is
+//! used in various places in the runtime.
+//!
+//! This crate provides the [`Encoding`] type to describe and compare these
+//! type-encodings, and the [`EncodingBox`] type which does the same, except
+//! it can be parsed from an encoding at runtime.
+//!
+//! The types from this crate is exported under the [`objc2`] crate as
+//! `objc2::encode`, so usually you would use it from there.
+//!
+//! [`objc2`]: https://crates.io/crates/objc2
+//!
+//!
+//! ## Example
+//!
+//! Parse an encoding from a string and compare it to a known encoding.
+//!
+//! ```rust
+//! use objc2_encode::{Encoding, EncodingBox};
+//! let s = "{s=i}";
+//! let enc = Encoding::Struct("s", &[Encoding::Int]);
+//! let parsed: EncodingBox = s.parse()?;
+//! assert!(enc.equivalent_to_box(&parsed));
+//! assert_eq!(enc.to_string(), s);
+//! # Ok::<(), objc2_encode::ParseError>(())
+//! ```
+//!
+//!
+//! ## Further resources
+//!
+//! - [Objective-C, Encoding and You](https://dmaclach.medium.com/objective-c-encoding-and-you-866624cc02de).
+//! - [Apple's documentation on Type Encodings](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html).
+//! - [How are the digits in ObjC method type encoding calculated?](https://stackoverflow.com/a/11527925)
+//! - [`clang`'s source code for generating `@encode`](https://github.com/llvm/llvm-project/blob/fae0dfa6421ea6c02f86ba7292fa782e1e2b69d1/clang/lib/AST/ASTContext.cpp#L7500-L7850).
+
+#![no_std]
+#![warn(missing_docs)]
+#![warn(missing_debug_implementations)]
+#![warn(clippy::missing_errors_doc)]
+#![warn(clippy::missing_panics_doc)]
+// Update in Cargo.toml as well.
+#![doc(html_root_url = "https://docs.rs/objc2-encode/4.1.0")]
+
+#[cfg(not(feature = "alloc"))]
+compile_error!("the `alloc` feature currently must be enabled");
+
+extern crate alloc;
+#[cfg(any(feature = "std", doc))]
+extern crate std;
+
+mod encoding;
+mod encoding_box;
+mod helper;
+mod parse;
+
+// Will be used at some point when generic constants are available
+#[allow(dead_code)]
+mod static_str;
+
+pub use self::encoding::Encoding;
+pub use self::encoding_box::EncodingBox;
+pub use self::parse::ParseError;
diff --git a/third_party/rust/objc2-encode/src/parse.rs b/third_party/rust/objc2-encode/src/parse.rs
@@ -0,0 +1,637 @@
+//! Parsing encodings from their string representation.
+#![deny(unsafe_code)]
+use alloc::boxed::Box;
+use alloc::string::{String, ToString};
+use alloc::vec::Vec;
+use core::fmt;
+
+use crate::helper::{ContainerKind, EncodingType, Helper, NestingLevel, Primitive};
+use crate::{Encoding, EncodingBox};
+
+/// Check whether a struct or union name is a valid identifier
+pub(crate) const fn verify_name(name: &str) -> bool {
+ let bytes = name.as_bytes();
+
+ if let b"?" = bytes {
+ return true;
+ }
+
+ if bytes.is_empty() {
+ return false;
+ }
+
+ let mut i = 0;
+ while i < bytes.len() {
+ let byte = bytes[i];
+ if !(byte.is_ascii_alphanumeric() || byte == b'_') {
+ return false;
+ }
+ i += 1;
+ }
+ true
+}
+
+/// The error that was encountered while parsing an encoding string.
+#[derive(Debug, PartialEq, Eq, Hash)]
+pub struct ParseError {
+ kind: ErrorKind,
+ data: String,
+ split_point: usize,
+}
+
+impl ParseError {
+ pub(crate) fn new(parser: Parser<'_>, kind: ErrorKind) -> Self {
+ Self {
+ kind,
+ data: parser.data.to_string(),
+ split_point: parser.split_point,
+ }
+ }
+}
+
+impl fmt::Display for ParseError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "failed parsing encoding: {} at byte-index {} in {:?}",
+ self.kind, self.split_point, self.data,
+ )
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for ParseError {}
+
+#[derive(Debug, PartialEq, Eq, Hash)]
+pub(crate) enum ErrorKind {
+ UnexpectedEnd,
+ Unknown(u8),
+ UnknownAfterComplex(u8),
+ ExpectedInteger,
+ IntegerTooLarge,
+ WrongEndArray,
+ WrongEndContainer(ContainerKind),
+ InvalidIdentifier(ContainerKind),
+ NotAllConsumed,
+}
+
+impl fmt::Display for ErrorKind {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::UnexpectedEnd => write!(f, "unexpected end"),
+ Self::Unknown(b) => {
+ write!(f, "unknown encoding character {}", *b as char)
+ }
+ Self::UnknownAfterComplex(b) => {
+ write!(f, "unknown encoding character {} after complex", *b as char,)
+ }
+ Self::ExpectedInteger => write!(f, "expected integer"),
+ Self::IntegerTooLarge => write!(f, "integer too large"),
+ Self::WrongEndArray => write!(f, "expected array to be closed"),
+ Self::WrongEndContainer(kind) => {
+ write!(f, "expected {kind} to be closed")
+ }
+ Self::InvalidIdentifier(kind) => {
+ write!(f, "got invalid identifier in {kind}")
+ }
+ Self::NotAllConsumed => {
+ write!(f, "remaining contents after parsing")
+ }
+ }
+ }
+}
+
+type Result<T, E = ErrorKind> = core::result::Result<T, E>;
+
+enum ParseInner {
+ Empty,
+ Encoding(EncodingBox),
+ ContainerEnd(ContainerKind),
+ ArrayEnd,
+}
+
+#[derive(Debug, PartialEq, Eq, Hash, Clone)]
+pub(crate) struct Parser<'a> {
+ data: &'a str,
+ // Always "behind"/"at" the current character
+ split_point: usize,
+}
+
+impl<'a> Parser<'a> {
+ pub(crate) fn new(data: &'a str) -> Self {
+ Self {
+ split_point: 0,
+ data,
+ }
+ }
+
+ pub(crate) fn remaining(&self) -> &'a str {
+ &self.data[self.split_point..]
+ }
+
+ fn peek(&self) -> Result<u8> {
+ self.try_peek().ok_or(ErrorKind::UnexpectedEnd)
+ }
+
+ fn try_peek(&self) -> Option<u8> {
+ self.data.as_bytes().get(self.split_point).copied()
+ }
+
+ fn try_peek2(&self) -> Option<(u8, u8)> {
+ let bytes = self.data.as_bytes();
+ Some((
+ *bytes.get(self.split_point)?,
+ *bytes.get(self.split_point + 1)?,
+ ))
+ }
+
+ fn advance(&mut self) {
+ self.split_point += 1;
+ }
+
+ fn rollback(&mut self) {
+ self.split_point -= 1;
+ }
+
+ fn consume_while(&mut self, mut condition: impl FnMut(u8) -> bool) {
+ while let Some(b) = self.try_peek() {
+ if condition(b) {
+ self.advance();
+ } else {
+ break;
+ }
+ }
+ }
+
+ pub(crate) fn is_empty(&self) -> bool {
+ self.try_peek().is_none()
+ }
+
+ pub(crate) fn expect_empty(&self) -> Result<()> {
+ if self.is_empty() {
+ Ok(())
+ } else {
+ Err(ErrorKind::NotAllConsumed)
+ }
+ }
+}
+
+impl Parser<'_> {
+ /// Strip leading qualifiers, if any.
+ pub(crate) fn strip_leading_qualifiers(&mut self) {
+ // TODO: Add API for accessing and outputting qualifiers.
+ #[allow(clippy::byte_char_slices)]
+ const QUALIFIERS: &[u8] = &[
+ b'r', // const
+ b'n', // in
+ b'N', // inout
+ b'o', // out
+ b'O', // bycopy
+ b'R', // byref
+ b'V', // oneway
+ ];
+ // TODO: b'|', // GCINVISIBLE
+
+ self.consume_while(|b| QUALIFIERS.contains(&b));
+ }
+
+ /// Chomp until we hit a non-digit.
+ ///
+ /// + and - prefixes are not supported.
+ fn chomp_digits(&mut self) -> Result<&str> {
+ let old_split_point = self.split_point;
+
+ // Parse first digit (which must be present).
+ if !self.peek()?.is_ascii_digit() {
+ return Err(ErrorKind::ExpectedInteger);
+ }
+
+ // Parse the rest, stopping if we hit a non-digit.
+ self.consume_while(|b| b.is_ascii_digit());
+
+ Ok(&self.data[old_split_point..self.split_point])
+ }
+
+ fn parse_u64(&mut self) -> Result<u64> {
+ self.chomp_digits()?
+ .parse()
+ .map_err(|_| ErrorKind::IntegerTooLarge)
+ }
+
+ fn parse_u8(&mut self) -> Result<u8> {
+ self.chomp_digits()?
+ .parse()
+ .map_err(|_| ErrorKind::IntegerTooLarge)
+ }
+}
+
+/// Check if the data matches an expected value.
+///
+/// The errors here aren't currently used, so they're hackily set up.
+impl Parser<'_> {
+ fn expect_byte(&mut self, byte: u8) -> Option<()> {
+ if self.try_peek()? == byte {
+ self.advance();
+ Some(())
+ } else {
+ None
+ }
+ }
+
+ fn expect_one_of_str<'a>(&mut self, strings: impl IntoIterator<Item = &'a str>) -> Option<()> {
+ for s in strings {
+ if self.remaining().starts_with(s) {
+ for b in s.as_bytes() {
+ self.expect_byte(*b).unwrap();
+ }
+ return Some(());
+ }
+ }
+ None
+ }
+
+ fn expect_u64(&mut self, int: u64) -> Option<()> {
+ if self.parse_u64().ok()? == int {
+ Some(())
+ } else {
+ None
+ }
+ }
+
+ fn expect_u8(&mut self, int: u8) -> Option<()> {
+ if self.parse_u8().ok()? == int {
+ Some(())
+ } else {
+ None
+ }
+ }
+
+ pub(crate) fn expect_encoding(&mut self, enc: &Encoding, level: NestingLevel) -> Option<()> {
+ match enc.helper() {
+ Helper::Primitive(primitive) => {
+ self.expect_one_of_str(primitive.equivalents().iter().map(|p| p.to_str()))?;
+
+ if primitive == Primitive::Object && self.try_peek() == Some(b'"') {
+ self.advance();
+ self.consume_while(|b| b != b'"');
+ self.expect_byte(b'"')?;
+ }
+ Some(())
+ }
+ Helper::BitField(size, Some((offset, t))) => {
+ self.expect_byte(b'b')?;
+ self.expect_u64(*offset)?;
+ self.expect_encoding(t, level.bitfield())?;
+ self.expect_u8(size)
+ }
+ Helper::BitField(size, None) => {
+ self.expect_byte(b'b')?;
+ self.expect_u8(size)
+ }
+ Helper::Indirection(kind, t) => {
+ self.expect_byte(kind.prefix_byte())?;
+ self.expect_encoding(t, level.indirection(kind))
+ }
+ Helper::Array(len, item) => {
+ self.expect_byte(b'[')?;
+ self.expect_u64(len)?;
+ self.expect_encoding(item, level.array())?;
+ self.expect_byte(b']')
+ }
+ Helper::Container(kind, name, items) => {
+ self.expect_byte(kind.start_byte())?;
+ self.expect_one_of_str([name])?;
+ if let Some(level) = level.container_include_fields() {
+ self.expect_byte(b'=')?;
+ // Parse as equal if the container is empty
+ if items.is_empty() {
+ loop {
+ match self.parse_inner().ok()? {
+ ParseInner::Empty => {
+ // Require the container to have an end
+ return None;
+ }
+ ParseInner::Encoding(_) => {}
+ ParseInner::ContainerEnd(parsed_kind) => {
+ if parsed_kind == kind {
+ return Some(());
+ } else {
+ return None;
+ }
+ }
+ ParseInner::ArrayEnd => {
+ return None;
+ }
+ }
+ }
+ }
+ // Parse as equal if the string's container is empty
+ if self.try_peek() == Some(kind.end_byte()) {
+ self.advance();
+ return Some(());
+ }
+ for item in items {
+ self.expect_encoding(item, level)?;
+ }
+ }
+ self.expect_byte(kind.end_byte())
+ }
+ Helper::NoneInvalid => Some(()),
+ }
+ }
+}
+
+impl Parser<'_> {
+ fn parse_container(&mut self, kind: ContainerKind) -> Result<(&str, Vec<EncodingBox>)> {
+ let old_split_point = self.split_point;
+
+ // Parse name until hits `=` or `}`/`)`
+ let has_items = loop {
+ let b = self.try_peek().ok_or(ErrorKind::WrongEndContainer(kind))?;
+ if b == b'=' {
+ break true;
+ } else if b == kind.end_byte() {
+ break false;
+ }
+ self.advance();
+ };
+
+ let s = &self.data[old_split_point..self.split_point];
+
+ if !verify_name(s) {
+ return Err(ErrorKind::InvalidIdentifier(kind));
+ }
+
+ if has_items {
+ self.advance();
+ }
+
+ let mut items = Vec::new();
+ // Parse items until hits end
+ loop {
+ match self.parse_inner()? {
+ ParseInner::Empty => {
+ return Err(ErrorKind::WrongEndContainer(kind));
+ }
+ ParseInner::Encoding(enc) => {
+ items.push(enc);
+ }
+ ParseInner::ContainerEnd(parsed_kind) => {
+ if parsed_kind == kind {
+ return Ok((s, items));
+ } else {
+ return Err(ErrorKind::Unknown(parsed_kind.end_byte()));
+ }
+ }
+ ParseInner::ArrayEnd => {
+ return Err(ErrorKind::Unknown(b']'));
+ }
+ }
+ }
+ }
+
+ pub(crate) fn parse_encoding_or_none(&mut self) -> Result<EncodingBox> {
+ match self.parse_inner()? {
+ ParseInner::Empty => Ok(EncodingBox::None),
+ ParseInner::Encoding(enc) => Ok(enc),
+ ParseInner::ContainerEnd(kind) => Err(ErrorKind::Unknown(kind.end_byte())),
+ ParseInner::ArrayEnd => Err(ErrorKind::Unknown(b']')),
+ }
+ }
+
+ fn parse_inner(&mut self) -> Result<ParseInner> {
+ if self.is_empty() {
+ return Ok(ParseInner::Empty);
+ }
+ let b = self.peek()?;
+ self.advance();
+
+ Ok(ParseInner::Encoding(match b {
+ b'c' => EncodingBox::Char,
+ b's' => EncodingBox::Short,
+ b'i' => EncodingBox::Int,
+ b'l' => EncodingBox::Long,
+ b'q' => EncodingBox::LongLong,
+ b'C' => EncodingBox::UChar,
+ b'S' => EncodingBox::UShort,
+ b'I' => EncodingBox::UInt,
+ b'L' => EncodingBox::ULong,
+ b'Q' => EncodingBox::ULongLong,
+ b'f' => EncodingBox::Float,
+ b'd' => EncodingBox::Double,
+ b'D' => EncodingBox::LongDouble,
+ b'j' => {
+ let res = match self.peek()? {
+ b'f' => EncodingBox::FloatComplex,
+ b'd' => EncodingBox::DoubleComplex,
+ b'D' => EncodingBox::LongDoubleComplex,
+ b => return Err(ErrorKind::UnknownAfterComplex(b)),
+ };
+ self.advance();
+ res
+ }
+ b'B' => EncodingBox::Bool,
+ b'v' => EncodingBox::Void,
+ b'*' => EncodingBox::String,
+ b'@' => match self.try_peek() {
+ // Special handling for blocks
+ Some(b'?') => {
+ self.advance();
+ EncodingBox::Block
+ }
+ // Parse class name if present
+ Some(b'"') => {
+ self.advance();
+ self.consume_while(|b| b != b'"');
+ self.expect_byte(b'"').ok_or(ErrorKind::UnexpectedEnd)?;
+ EncodingBox::Object
+ }
+ _ => EncodingBox::Object,
+ },
+ b'#' => EncodingBox::Class,
+ b':' => EncodingBox::Sel,
+ b'?' => EncodingBox::Unknown,
+
+ b'b' => {
+ let size_or_offset = self.parse_u64()?;
+ if let Some((size, ty)) = self.try_parse_bitfield_gnustep()? {
+ let offset = size_or_offset;
+ EncodingBox::BitField(size, Some(Box::new((offset, ty))))
+ } else {
+ let size = size_or_offset
+ .try_into()
+ .map_err(|_| ErrorKind::IntegerTooLarge)?;
+ EncodingBox::BitField(size, None)
+ }
+ }
+ b'^' => EncodingBox::Pointer(Box::new(match self.parse_inner()? {
+ ParseInner::Empty => EncodingBox::None,
+ ParseInner::Encoding(enc) => enc,
+ ParseInner::ContainerEnd(_) | ParseInner::ArrayEnd => {
+ self.rollback();
+ EncodingBox::None
+ }
+ })),
+ b'A' => EncodingBox::Atomic(Box::new(match self.parse_inner()? {
+ ParseInner::Empty => EncodingBox::None,
+ ParseInner::Encoding(enc) => enc,
+ ParseInner::ContainerEnd(_) | ParseInner::ArrayEnd => {
+ self.rollback();
+ EncodingBox::None
+ }
+ })),
+ b'[' => {
+ let len = self.parse_u64()?;
+ match self.parse_inner()? {
+ ParseInner::Empty => {
+ return Err(ErrorKind::WrongEndArray);
+ }
+ ParseInner::Encoding(item) => {
+ self.expect_byte(b']').ok_or(ErrorKind::WrongEndArray)?;
+ EncodingBox::Array(len, Box::new(item))
+ }
+ ParseInner::ArrayEnd => EncodingBox::Array(len, Box::new(EncodingBox::None)),
+ ParseInner::ContainerEnd(kind) => {
+ return Err(ErrorKind::Unknown(kind.end_byte()))
+ }
+ }
+ }
+ b']' => {
+ return Ok(ParseInner::ArrayEnd);
+ }
+ b'{' => {
+ let kind = ContainerKind::Struct;
+ let (name, items) = self.parse_container(kind)?;
+ EncodingBox::Struct(name.to_string(), items)
+ }
+ b'}' => {
+ return Ok(ParseInner::ContainerEnd(ContainerKind::Struct));
+ }
+ b'(' => {
+ let kind = ContainerKind::Union;
+ let (name, items) = self.parse_container(kind)?;
+ EncodingBox::Union(name.to_string(), items)
+ }
+ b')' => {
+ return Ok(ParseInner::ContainerEnd(ContainerKind::Union));
+ }
+ b => return Err(ErrorKind::Unknown(b)),
+ }))
+ }
+
+ fn try_parse_bitfield_gnustep(&mut self) -> Result<Option<(u8, EncodingBox)>> {
+ if let Some((b1, b2)) = self.try_peek2() {
+ // Try to parse the encoding.
+ //
+ // The encoding is always an integral type.
+ let ty = match b1 {
+ b'c' => EncodingBox::Char,
+ b's' => EncodingBox::Short,
+ b'i' => EncodingBox::Int,
+ b'l' => EncodingBox::Long,
+ b'q' => EncodingBox::LongLong,
+ b'C' => EncodingBox::UChar,
+ b'S' => EncodingBox::UShort,
+ b'I' => EncodingBox::UInt,
+ b'L' => EncodingBox::ULong,
+ b'Q' => EncodingBox::ULongLong,
+ b'B' => EncodingBox::Bool,
+ _ => return Ok(None),
+ };
+ // And then check if a digit follows that (which the size would
+ // always contain).
+ if !b2.is_ascii_digit() {
+ return Ok(None);
+ }
+ // We have a size; so let's advance...
+ self.advance();
+ // ...and parse it for real.
+ let size = self.parse_u8()?;
+ Ok(Some((size, ty)))
+ } else {
+ Ok(None)
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use alloc::vec;
+
+ #[test]
+ fn parse_container() {
+ const KIND: ContainerKind = ContainerKind::Struct;
+
+ #[track_caller]
+ fn assert_name(enc: &str, expected: Result<(&str, Vec<EncodingBox>)>) {
+ let mut parser = Parser::new(enc);
+ assert_eq!(parser.parse_container(KIND), expected);
+ }
+
+ assert_name("abc=}", Ok(("abc", vec![])));
+ assert_name(
+ "abc=ii}",
+ Ok(("abc", vec![EncodingBox::Int, EncodingBox::Int])),
+ );
+ assert_name("_=}.a'", Ok(("_", vec![])));
+ assert_name("abc}def", Ok(("abc", vec![])));
+ assert_name("=def}", Err(ErrorKind::InvalidIdentifier(KIND)));
+ assert_name(".=def}", Err(ErrorKind::InvalidIdentifier(KIND)));
+ assert_name("}xyz", Err(ErrorKind::InvalidIdentifier(KIND)));
+ assert_name("", Err(ErrorKind::WrongEndContainer(KIND)));
+ assert_name("abc", Err(ErrorKind::WrongEndContainer(KIND)));
+ assert_name("abc)def", Err(ErrorKind::WrongEndContainer(KIND)));
+ }
+
+ #[test]
+ fn parse_bitfield() {
+ #[track_caller]
+ fn assert_bitfield(enc: &str, expected: Result<EncodingBox>) {
+ let mut parser = Parser::new(enc);
+ assert_eq!(
+ parser
+ .parse_encoding_or_none()
+ .and_then(|enc| parser.expect_empty().map(|()| enc)),
+ expected
+ );
+ }
+
+ assert_bitfield("b8", Ok(EncodingBox::BitField(8, None)));
+ assert_bitfield("b8C", Err(ErrorKind::NotAllConsumed));
+ assert_bitfield(
+ "b8C4",
+ Ok(EncodingBox::BitField(
+ 4,
+ Some(Box::new((8, EncodingBox::UChar))),
+ )),
+ );
+
+ assert_bitfield(
+ "{s=b8C}",
+ Ok(EncodingBox::Struct(
+ "s".into(),
+ vec![EncodingBox::BitField(8, None), EncodingBox::UChar],
+ )),
+ );
+
+ assert_bitfield("b2000", Err(ErrorKind::IntegerTooLarge));
+ assert_bitfield(
+ "b2000c100",
+ Ok(EncodingBox::BitField(
+ 100,
+ Some(Box::new((2000, EncodingBox::Char))),
+ )),
+ );
+ assert_bitfield("b2000C257", Err(ErrorKind::IntegerTooLarge));
+ }
+
+ #[test]
+ fn parse_closing() {
+ let mut parser = Parser::new("]");
+ assert_eq!(
+ parser.parse_encoding_or_none(),
+ Err(ErrorKind::Unknown(b']'))
+ );
+ }
+}
diff --git a/third_party/rust/objc2-encode/src/static_str.rs b/third_party/rust/objc2-encode/src/static_str.rs
@@ -0,0 +1,246 @@
+use crate::helper::{Helper, NestingLevel};
+
+use super::Encoding;
+
+pub(crate) const fn static_int_str_len(mut n: u64) -> usize {
+ let mut i = 0;
+ if n == 0 {
+ return 1;
+ }
+ while n > 0 {
+ n /= 10;
+ i += 1;
+ }
+ i
+}
+
+pub(crate) const fn static_int_str_array<const RES: usize>(mut n: u64) -> [u8; RES] {
+ let mut res: [u8; RES] = [0; RES];
+ let mut i = 0;
+ if n == 0 {
+ res[0] = b'0';
+ return res;
+ }
+ while n > 0 {
+ res[i] = b'0' + (n % 10) as u8;
+ n /= 10;
+ i += 1;
+ }
+
+ let mut rev: [u8; RES] = [0; RES];
+ let mut rev_i = 0;
+ while 0 < i {
+ i -= 1;
+ rev[rev_i] = res[i];
+ n /= 10;
+ rev_i += 1;
+ }
+ rev
+}
+
+pub(crate) const fn static_encoding_str_len(encoding: &Encoding, level: NestingLevel) -> usize {
+ use Helper::*;
+
+ match Helper::new(encoding) {
+ Primitive(primitive) => primitive.to_str().len(),
+ BitField(size, None) => 1 + static_int_str_len(size as u64),
+ BitField(size, Some((offset, t))) => {
+ 1 + static_int_str_len(*offset)
+ + static_encoding_str_len(t, level.bitfield())
+ + static_int_str_len(size as u64)
+ }
+ Indirection(kind, t) => 1 + static_encoding_str_len(t, level.indirection(kind)),
+ Array(len, item) => {
+ 1 + static_int_str_len(len) + static_encoding_str_len(item, level.array()) + 1
+ }
+ Container(_, name, items) => {
+ let mut res = 1 + name.len();
+ if let Some(level) = level.container_include_fields() {
+ res += 1;
+ let mut i = 0;
+ while i < items.len() {
+ res += static_encoding_str_len(&items[i], level);
+ i += 1;
+ }
+ }
+ res + 1
+ }
+ NoneInvalid => 0,
+ }
+}
+
+pub(crate) const fn static_encoding_str_array<const LEN: usize>(
+ encoding: &Encoding,
+ level: NestingLevel,
+) -> [u8; LEN] {
+ use Helper::*;
+
+ let mut res: [u8; LEN] = [0; LEN];
+ let mut res_i = 0;
+
+ match Helper::new(encoding) {
+ Primitive(primitive) => {
+ let s = primitive.to_str().as_bytes();
+ let mut i = 0;
+ while i < s.len() {
+ res[i] = s[i];
+ i += 1;
+ }
+ }
+ BitField(size, None) => {
+ res[res_i] = b'b';
+ res_i += 1;
+
+ let mut i = 0;
+ // We use 3 even though it creates an oversized array
+ let arr = static_int_str_array::<3>(size as u64);
+ while i < static_int_str_len(size as u64) {
+ res[res_i] = arr[i];
+ res_i += 1;
+ i += 1;
+ }
+ }
+ BitField(size, Some((offset, t))) => {
+ let level = level.bitfield();
+ res[res_i] = b'b';
+ res_i += 1;
+
+ let mut i = 0;
+ // We use 20 even though it creates an oversized array
+ let arr = static_int_str_array::<20>(*offset);
+ while i < static_int_str_len(*offset) {
+ res[res_i] = arr[i];
+ res_i += 1;
+ i += 1;
+ }
+
+ let mut i = 0;
+ // We use LEN even though it creates an oversized array
+ // This could probably be reduced to 1
+ let arr = static_encoding_str_array::<LEN>(t, level);
+ while i < static_encoding_str_len(t, level) {
+ res[res_i] = arr[i];
+ res_i += 1;
+ i += 1;
+ }
+
+ let mut i = 0;
+ // We use 3 even though it creates an oversized array
+ let arr = static_int_str_array::<3>(size as u64);
+ while i < static_int_str_len(size as u64) {
+ res[res_i] = arr[i];
+ res_i += 1;
+ i += 1;
+ }
+ }
+ Indirection(kind, t) => {
+ let level = level.indirection(kind);
+ res[res_i] = kind.prefix_byte();
+ res_i += 1;
+
+ let mut i = 0;
+ // We use LEN even though it creates an oversized array
+ let arr = static_encoding_str_array::<LEN>(t, level);
+ while i < static_encoding_str_len(t, level) {
+ res[res_i] = arr[i];
+ res_i += 1;
+ i += 1;
+ }
+ }
+ Array(len, item) => {
+ let level = level.array();
+ let mut res_i = 0;
+
+ res[res_i] = b'[';
+ res_i += 1;
+
+ let mut i = 0;
+ // We use 20 even though it creates an oversized array
+ let arr = static_int_str_array::<20>(len);
+ while i < static_int_str_len(len) {
+ res[res_i] = arr[i];
+ res_i += 1;
+ i += 1;
+ }
+
+ let mut i = 0;
+ // We use LEN even though it creates an oversized array
+ let arr = static_encoding_str_array::<LEN>(item, level);
+ while i < static_encoding_str_len(item, level) {
+ res[res_i] = arr[i];
+ res_i += 1;
+ i += 1;
+ }
+
+ res[res_i] = b']';
+ }
+ Container(kind, name, items) => {
+ let mut res_i = 0;
+
+ res[res_i] = kind.start_byte();
+ res_i += 1;
+
+ let mut name_i = 0;
+ let name = name.as_bytes();
+ while name_i < name.len() {
+ res[res_i] = name[name_i];
+ res_i += 1;
+ name_i += 1;
+ }
+
+ if let Some(level) = level.container_include_fields() {
+ res[res_i] = b'=';
+ res_i += 1;
+
+ let mut items_i = 0;
+ while items_i < items.len() {
+ // We use LEN even though it creates an oversized array
+ let field_res = static_encoding_str_array::<LEN>(&items[items_i], level);
+
+ let mut item_res_i = 0;
+ while item_res_i < static_encoding_str_len(&items[items_i], level) {
+ res[res_i] = field_res[item_res_i];
+ res_i += 1;
+ item_res_i += 1;
+ }
+ items_i += 1;
+ }
+ }
+
+ res[res_i] = kind.end_byte();
+ }
+ NoneInvalid => {}
+ };
+ res
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ macro_rules! const_int_str {
+ ($n:expr) => {{
+ const X: [u8; static_int_str_len($n)] = static_int_str_array($n);
+ unsafe { core::str::from_utf8_unchecked(&X) }
+ }};
+ }
+
+ #[test]
+ fn test_const_int_str() {
+ const STR_0: &str = const_int_str!(0);
+ const STR_4: &str = const_int_str!(4);
+ const STR_42: &str = const_int_str!(42);
+ const STR_100: &str = const_int_str!(100);
+ const STR_999: &str = const_int_str!(999);
+ const STR_1236018655: &str = const_int_str!(1236018655);
+
+ assert_eq!(STR_0, "0");
+ assert_eq!(STR_4, "4");
+ assert_eq!(STR_42, "42");
+ assert_eq!(STR_100, "100");
+ assert_eq!(STR_999, "999");
+ assert_eq!(STR_1236018655, "1236018655");
+ }
+
+ // static encoding tests are in `encoding.rs`
+}
diff --git a/third_party/rust/objc2/.cargo-checksum.json b/third_party/rust/objc2/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"CHANGELOG.md":"596d3653e59d147af4d490af5eaa548bff243672353f25373d2da7ad042c7108","Cargo.lock":"bc2586bf6dab210e95b518cefd27ca6c7903a41b3df7830b66a2a715dcb12242","Cargo.toml":"97d59aa7ac2bcad4a2752fd7ebd96fb0eabf7fec268a14c987a5f5de12918932","README.md":"5da56f1508a87b982a23e6bad2ff09b329f7fcf9181ea15eac17788f8d1c6824","benches/autorelease.rs":"6f9f81edbeb5cd4cf7798bd886f430c5c8e5666e2e238f7c0f874148d2d5dbd3","build.rs":"f13d2effabc1cfa07fa5018c78eadc645914d676c034adf78acb24b8b419ce7a","examples/class_with_lifetime.rs":"c024a68f936d8214cb8f721d55fb2edec76cbd3664224982d5fb6184575afc7e","examples/encode_core_graphics.rs":"2956272edc7dfd8dcaaeebc8a746fe8ad9ac18e25e80be421d854bf0a7c5e127","examples/encode_nsstring.rs":"28ca48e1436d5029cf501d3399ad66b6797db51bf17541050794ca24703f1259","examples/encode_nsuinteger.rs":"13c6e9698aa9784b5ea4442e07fdadd22e7225b96658d8e71dfea8ce11305b92","examples/encode_opaque_type.rs":"22e342d68e588194809c03d74387d6f1dfa12a2a7f95cb2361982c91dc7f59fb","examples/hello_world_app.rs":"2ee658a67711ade00203ee3b82b4dfd1a4b25da165419f424e61d22d2c2ccdb2","examples/introspection.rs":"c7bfa73b4ab3c46c43d36f649c65746ea6551a2c9bcf23b42acf772fea37b052","src/__framework_prelude.rs":"3b43544dfb4cd70a1925e0ae525bad71ec9c774e7949434e2e4f1186790fb5e5","src/__macro_helpers/cache.rs":"c6ca0617b800e62d48a37012a91c0416f7a3204af054c517478fbd54939a7392","src/__macro_helpers/class.rs":"d6b9eaba1c76755c64afa5f807e374f1c342ea9ec2804c1682ae8702a2bad7b4","src/__macro_helpers/common_selectors.rs":"c3155ded8b0708f9d238871c3c5d9630ad79563ed9463c0fc8a0c61480a3f6f5","src/__macro_helpers/convert.rs":"9c9a65a2f843edaf108b9d5463b4d2d8dc70463646d9c1d6518a96d40aec4837","src/__macro_helpers/define_class.rs":"6a41892d99b97f7547ea69d787cb3025e9aaadf159ae626ae08d26e8b35253d1","src/__macro_helpers/defined_ivars.rs":"0895c4f46aeb191f9c0f94a3e3ec6788e74f1250e2a4b561e0f1e254b0700123","src/__macro_helpers/image_info.rs":"60b5184a019f9c22cbcbaadf075fbdc26867f18bc5ecc30b775d72a7b74cfe70","src/__macro_helpers/method_family.rs":"63fa84b3c6c90cc308e337b1e00ee30dca50dbdcd68570f9a5de0b585be63c67","src/__macro_helpers/mod.rs":"98375f91b0bb8235cb9d3e299d9f6c54a8ce9ec680c81da378f88500a736c8c8","src/__macro_helpers/module_info.rs":"79c7db4ba7b31136b2fd977083d3b73ba15412f688e57005f3bc5eef81712144","src/__macro_helpers/msg_send_retained.rs":"8b7c1495cca13c9a1011882c4726b7060523069278455adbc93b7712eed47387","src/__macro_helpers/null_error.rs":"8485815fca5a4d3a088e363df7d7a8f0ab7960523f3eda6cef501d9a6e7306e2","src/__macro_helpers/os_version.rs":"1b1d06cd9da04159fbda4553c74b619c78b9b5d5641ed880f2342ab7cdf890c9","src/__macro_helpers/os_version/apple.rs":"e08dcc685ff32f8219e5823b0d7e2a70c5534ec13c39d26bf08c9fea703dcb1c","src/__macro_helpers/retain_semantics.rs":"1ba51abbc11ab6d7e59390a3979d491dde4361f8ec25d748a05ae1217b6566e4","src/__macro_helpers/sync_unsafe_cell.rs":"5b6f6200c45721ae759464b47016fb7738c6181684bbed9b85faf27fc4e2c5fc","src/__macro_helpers/writeback.rs":"a0dfe60e4cbd763039b9daf67df2f4722e6e0cfede6e2b96e154b9246a2a01a9","src/downcast.rs":"c14c9fd8afb52de1f4e30ca9019e5cdb788dc70fc004ad76b2a427932d897c44","src/encode.rs":"7e50e644d065b07afb2528dbef6d8fe8c4a90adaf947b93240eec93866cc2f60","src/exception.rs":"495502e59fef75d7a1eac3bfb7f6ea83c21d46d42bc39f4a700cac28c2ed88c6","src/ffi/class.rs":"3684bfb7d5a9e2ad687b838d0ebabccd8ccf5a86e03c68c95d4a6c8ddf27d59e","src/ffi/constants.rs":"8b455a460298406039e45ebb4cd9e3b5efd3707f70bd535965b79076e7987015","src/ffi/exception.rs":"72da85269cc5a813b7f1b208e91c1500f8d3560d5975a812f15a129d1aa1def5","src/ffi/libc.rs":"74010389a3b1b37923fb26259edf8ec9fc633d3fb9e95590f666534db265da46","src/ffi/message.rs":"4d44f8e84dc37273172d68e50f3376b6617c0ab6e943352b4b899c29ac8da5b0","src/ffi/method.rs":"b83c2332b7feb59358162e7ffcd842a07a20dd81c92ba8aafe19aa0fb3dad966","src/ffi/mod.rs":"aa5bc001dd11f17266850b591d138648ce0c756f2d64a3caab2d37231e84abbf","src/ffi/object.rs":"190e54d96a4102babc3eaaf9993eea9cfaf5b7354602bc173d12c423e0f2c67e","src/ffi/property.rs":"f84371a8cda2adc46a912d964e3c485df64084ac304efaa084547a1045588701","src/ffi/protocol.rs":"3dce40d519d164481276025355c6f152fd074a950d25846e914d683f6f3bb1b3","src/ffi/rc.rs":"054d470dbbc03865bb855f0fadf8ea7acab3468c8445b02e8d21bf01363f3d2d","src/ffi/selector.rs":"6cfe0fe35af9b5183124bf896d49e32ccfdd14f56d2fb5ad841a14aa9ecfcf81","src/ffi/types.rs":"40a2f02aee7bced6527b1a689ca24c7a2c99f3952af6381c2ed1c75436ac0682","src/ffi/various.rs":"eb06351c93c05d30c8420e0b115bd7b9f31a544d1eda2709c61b1d29b078e909","src/lib.rs":"f885891640bf8d9db339a63130cdef0f2c9bd660bcbf7c818c415e23e7e74a0b","src/macros/__attribute_helpers.rs":"9c847b0565fd126b558b16bbc3153e502a943b39f51e0bf268c26b23fc5c7e9d","src/macros/__fallback.rs":"ecff35fcff4de9cea0abaf298a36fb1b2904548614d0fad07987674768232b44","src/macros/__method_msg_send.rs":"d0e5bfc8fc7a18f8298e1dbac53b9baa0669e5165a119ed6219c8ba2e6eeddec","src/macros/__msg_send_parse.rs":"0b24e4f4fe0cb75eb5833c5ca618082d7d6b9792d2d78d9d42c1a272066a3895","src/macros/__rewrite_self_param.rs":"d154cf8c13f8a3f070e57cdb55a3c26afbebe5c385420bfae7005b702009cee3","src/macros/available.rs":"f4460527852761a3c6dfb26d31f1a40d6c4bb8abb13023314cc6a8b879b7bc9a","src/macros/cf_objc2_type.rs":"352b1c68fbb8cb998a5e71952551af90776ab1cee775e15fc5458c77c8c56cd7","src/macros/define_class.rs":"dc3477e30e4ef58e021b6bdc0a0327ac307e4043d87435b32dea6958c5e9e86a","src/macros/extern_class.rs":"7887d0921c9c43e4cfb51de42e8b0e255e154632d85e1680f57d7fcf44cfaca4","src/macros/extern_conformance.rs":"143ef0bbce2b3b2c448bcfd4ab38678759afb3ac794fa9d2ccfda018e372611d","src/macros/extern_methods.rs":"7dc7fb5d2b9f639c94b1b2fda69d6bd00d8944560415aef9698ff826ee028456","src/macros/extern_protocol.rs":"d898543ce0d89a2496285ebb1757434777453cafd328b6e9b43d65ebfd649e31","src/macros/mod.rs":"95a77ab05ead62bdff9ef47ccde82a85c50d25751dcfe8519b57366030d90360","src/main_thread_marker.rs":"66676a96c255c1b5215860722b3e47f4c4ec8033b0237798ba261ff456b32be7","src/rc/allocated_partial_init.rs":"d8426764a264132bd7cba61ca7ad6d8e91d89eab30811fa64b6ebf02e303ce86","src/rc/autorelease.rs":"87d6a8d19e69b0eaf85c4b99ee3ee15e02c314f325e5e61520b6e35a3d2be799","src/rc/mod.rs":"dc67a96d20009a3ca9556f1a914668b0200bfb9701fc3e391a8518f25252116f","src/rc/retained.rs":"deaa939673ac1b7bf3d2c40b77a4d86fd06b805b301b1bdca10a220ed4e1b551","src/rc/retained_forwarding_impls.rs":"70c6c9e0e3089c5b5ffbb0535dfb63f355dab2932a20f87b8442cf4ff5f62518","src/rc/retained_traits.rs":"f4bb85802ce464ccd2ebe6f6a68b41537dd7444e1765862459e6ed4500721070","src/rc/test_object.rs":"0bb97ac9015559c6a1c904d3c313c7ab24facdfd7cd7ece2e3d0ded5ca7a884f","src/rc/weak.rs":"d052b7cbfbacd471a99a0ad8864c69dc12da1b39ca4b5d91dfd07b1a92c5b0e9","src/runtime/__nsstring.rs":"9e4c567499e53a4e00c1ca93973ed13df1d35745828f535a2980d17b33f708b6","src/runtime/bool.rs":"c60b4efb1a78a4ccc4b25a8dea79cdd818d4595224c096337097745d2befae98","src/runtime/define.rs":"9411701b1cf4789db48e555838a7f53deac3c2f827f5d97614d73f6fc44ec1a6","src/runtime/malloc.rs":"03eecda4e60dfb4f53a6a691ed9df0d61fc72bae84d5d74e605ba1995ded61c7","src/runtime/message_receiver.rs":"9576e377840f1707fd2457b3f5530d2da9f6279278858648f1951a78b1b16f6f","src/runtime/method_encoding_iter.rs":"f0de5d29b8b02d54a99f42eb5be57659483a3462d51bc57f0776b8871126492a","src/runtime/method_implementation.rs":"49251266843291a69f4f49278549b335bc2b3bd8e2fab355f4bc1eab4df486ab","src/runtime/mod.rs":"accb4533fec9dbfc9ca51090663def2308aac72c66ec9cbf85649c4d2729c4c6","src/runtime/nsobject.rs":"8a71f82aaee35148c403705358331c1756f010af045b23393d74a6a7dfda7c60","src/runtime/nsproxy.rs":"b7d477291c9830c0e01efe1c09e1707638f53c09755040141d643eedbe987090","src/runtime/nszone.rs":"9c6257e20aed110997483f0f7e0ce8df7b51331da36780a9a7cd32960d1c97ea","src/runtime/protocol_object.rs":"fdd6760cca194e14ea251646b5c2179a3085179913930de5d52d025c1542d5c6","src/runtime/retain_release_fast.rs":"0abc01b25d42902d7083cebed12b0fb1a72b9733679c7892fe7c3ebbeea36b6e","src/test_utils.rs":"51a8c6fe416f4a0c70edcc6e229f4879e33ec98da5d311245d4cf9775b6e9e57","src/top_level_traits.rs":"a1a2e14da886728e70afe46577a0237524dce04f0776b9e07fa67c321bbe914a","src/topics/about_generated/CHANGELOG.md":"a144fd97f913a56b6102f012fbb7aec4f7ee4fb0be11af5ae2be1210c957d758","src/topics/about_generated/README.md":"c02f1533decd37d991c2f6a4db356ea19ef3add9d638604827a886474534e18d","src/topics/about_generated/cargo_features.md":"ce950336c5dac8835c9e11144d2fc9858d324f87e480bd916d3c29e495ca4a45","src/topics/about_generated/deref.md":"16259698a4619bcec756b09268d97ff3972fa64892e4023a72f8a4d07aa3ec4e","src/topics/about_generated/list.rs":"dd8920b8979c7e846d1990adb1a53f6d6aade3246eac6b258962e3aa40ba3c8b","src/topics/about_generated/list_data.md":"a4dc51aec1b245f1a7ff7e009e47c7256feb532abdb9a646e416ea39a471bcd4","src/topics/about_generated/list_unsupported.md":"cbc4a9b00f0f4a56e8becb701f40edf3fe8956ad638abe0e3f970ed214a24e0b","src/topics/about_generated/mod.rs":"622ea407c1e50eb271cebaf3f396c97ed0b21fdb8a27b11e4f1ea2cf046630ce","src/topics/alternatives.md":"97afe7040a3fa79575318d690b7677272bf157a7e2a06d30a62e2a4d75625dc9","src/topics/crate_interop.md":"5b3903ddfa1b95303a115557cc98b92e7dda4b4cc382d8a1bb800e336e2f048c","src/topics/cross_compiling.md":"9593b68727129df6ed86a0ac00dfaa3d4e64fbda7e5b7ef040f623fcf8569d0b","src/topics/goals.md":"b91c3726bdcb53e7dedcd2f115827740a43814676a3e35307c8bf275cb20ed69","src/topics/interior_mutability.md":"b10448576894e65d76c108a2d6c3b4bf98a0671db5309bb5581b0ecf70ed5716","src/topics/kvo.md":"bb6842a3604e9a2daf01533c60122bd62a35013b9dbc08631564d4dc1d895086","src/topics/layered_safety.md":"eecb31c528430175828fc317993c70ff8c809d34aa98b9bc83910708c7e94fbf","src/topics/migrating_from_objc.md":"35f1a70af3489ca928082015cd11ba601688f35471e621fd461b8d3cba8c2358","src/topics/mod.rs":"1fce09b8caa364a938fdc0db61eeb7ee41f4b2ab8ad9f6ab7443c2fed0e50719","src/topics/mvc.md":"bbcabe6375413bf15b654df3197ccbbb5e6bad00852c838622317515be4ba85e","src/topics/run_loop.md":"b57a4d247c7f8713f9a54847202a2ae7b72f57fc4470f273a5b7b86b4823503a","src/topics/swift.md":"dc3409869719d3a750b0c3d947159661c36bbf7ede05deb1df8a1cee43173e44","src/topics/weak_property.md":"91d42bf7af831d58f7f20602de8300efd36ac3752afb8f5d542e33a125424f1c","src/verify.rs":"a178a560ffaf8c96ba315e05f2acc9677e62d4e26dc10dc9a3b0e5e772391618","tests/backtrace.rs":"327cb9bcf097cd4f18cf1c621d4c2caf5fc05991538a031bae4e6e62faa645ad","tests/define_class.rs":"c3d77b47339f94b0d153ea59ee9dd6bbf447e53caa749997bbd8a0ba9b71f131","tests/define_class_self.rs":"8cd90ad92d9310e0ae99c264d9027f827630495dd3d9083a0e4b792fe6d67a6d","tests/extern_class.rs":"51eba9a0c4f191564974dbcd2830faf1e71dedae303af4701357d7ff385442b7","tests/macros_mainthreadmarker.rs":"cc4833bfe276cdbffc15aa274d8bcd2342231eea0950be5585398268289be61c","tests/no_prelude.rs":"a3ca86cf91ffea6cdc5508ba37fbaeba8f2a8d8cee0cb3b19b907b292717e316","tests/protocol_shared_retain_count.rs":"7d736ea5ca1b077881060bf18d164ae9d97f83365d5cb5e9dc6626a35f027686","tests/track_caller.rs":"7a80738661cbf3f7f354b905b51602918e982bb05d60a2ee47ae653c05a55530","tests/use_macros.rs":"ed033ef5d7ac6f3512c69b6e8979b219630a906708abb4922aff6b6829c07f7b","translation-config.toml":"d3334f8efc03d8e4faec97bf695c56a180832a25535639b0ea773ae016678a8d"},"package":"b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05"}
+\ No newline at end of file
diff --git a/third_party/rust/objc2/CHANGELOG.md b/third_party/rust/objc2/CHANGELOG.md
@@ -0,0 +1,1779 @@
+# Changelog
+
+Notable changes to this crate will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
+
+## Unreleased - YYYY-MM-DD
+
+
+## [0.6.3] - 2025-10-04
+[0.6.3]: https://github.com/madsmtm/objc2/compare/objc2-0.6.2...objc2-0.6.3
+
+## Fixed
+* Fixed documentation on docs.rs.
+
+
+## [0.6.2] - 2025-08-14
+[0.6.2]: https://github.com/madsmtm/objc2/compare/objc2-0.6.1...objc2-0.6.2
+
+## Fixed
+* Relaxed the rules for when encodings are considered equal, to support macOS 26.
+
+
+## [0.6.1] - 2025-04-19
+[0.6.1]: https://github.com/madsmtm/objc2/compare/objc2-0.6.0...objc2-0.6.1
+
+## Added
+* Make `#[name = ...]` in `define_class!` optional. If not specified, the macro
+ will choose a suitable default that makes loading multiple versions of the
+ same library possible.
+* Added conversion trait impls from `ProtocolObject<P>` to `AnyObject`.
+* Added `extern_conformance!` to make it clearer how to correctly conform to protocols.
+
+## Changed
+* Renamed `AllocAnyThread` to `AnyThread`. The old name is kept as deprecated.
+
+## Fixed
+* Fixed undefined behaviour when calling `AnyObject::class` on invalid objects.
+* Fixed `available!` macro when running under Xcode's "Designed for iPad" setting.
+
+
+## [0.6.0] - 2025-01-22
+[0.6.0]: https://github.com/madsmtm/objc2/compare/objc2-0.5.2...objc2-0.6.0
+
+### Added
+* Added `AnyClass::is_metaclass`.
+* Added `MainThreadMarker` from `objc2-foundation`.
+ * `MainThreadMarker::new_unchecked` and `MainThreadBound::new` is now available
+ in `const`. This is useful for creating main-thread only statics.
+ * `MainThreadMarker::from` now debug-asserts that it is actually running on
+ the main thread.
+* Added `MainThreadOnly::mtm`.
+* Added `DowncastTarget`, `AnyObject::downcast_ref` and `Retained::downcast`
+ to allow safely casting between Objective-C objects.
+* Implemented more `fmt` traits on `Retained`.
+* Implemented `Extend` trait on `Retained`.
+* Implemented `AsRef` in a forwarding fashion on `Retained`.
+* Implemented `PartialEq` and `PartialOrd` on `Retained` in a slightly more
+ generic way.
+* Allow using `Into` to convert to retained objects.
+* Make `Retained::into_super` an inherent method instead of an associated
+ method. This means that you can now use it as `.into_super()`.
+* Added the `available!()` macro for determining whether code is running on
+ a given operating system.
+* Implement `Message` for `AnyClass` and `AnyProtocol`.
+* Allow `AnyClass` and `AnyProtocol` to be converted to `AnyObject` (both of
+ these can act as objects).
+* Classes created using `define_class!` now implement `Send` and `Sync` when
+ subclassing `NSObject`.
+* Added 16-fold `Encode` and `RefEncode` impls for function pointers
+ (previously only implemented for up to 12 arguments, which turned out to be
+ insufficient).
+* Added `#[unsafe(method_family = ...)]` attribute in `extern_methods!`,
+ `extern_protocol!` and `define_class!`, to allow overriding the inferred
+ method family if need be.
+* Added `PartialEq`, `Eq`, `Hash`, `PartialOrd` and `Ord` implementations for
+ `Bool`.
+* Added `"disable-encoding-assertions"` Cargo feature flag to allow completely
+ disabling encoding verification.
+
+### Changed
+* **BREAKING**: Renamed `declare_class!` to `define_class!`, and changed the
+ syntax to be more succinct:
+
+ ```rust
+ // Before
+ use objc2::mutability::InteriorMutable;
+ use objc2::runtime::{NSObject, NSObjectProtocol};
+ use objc2::{declare_class, ClassType, DeclaredClass};
+
+ struct MyIvars;
+
+ declare_class!(
+ struct MyObject;
+
+ unsafe impl ClassType for MyObject {
+ type Super = NSObject;
+ type Mutability = InteriorMutable;
+ const NAME: &'static str = "MyObject";
+ }
+
+ impl DeclaredClass for MyObject {
+ type Ivars = MyIvars;
+ }
+
+ unsafe impl MyObject {
+ #[method(myMethod)]
+ fn my_method(&self) {
+ // ...
+ }
+ }
+
+ unsafe impl NSObjectProtocol for MyObject {}
+ );
+
+ // After
+ use objc2::runtime::{NSObject, NSObjectProtocol};
+ use objc2::define_class;
+
+ struct MyIvars;
+
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[name = "MyObject"]
+ #[ivars = MyIvars]
+ struct MyObject;
+
+ impl MyObject {
+ #[unsafe(method(myMethod))]
+ fn my_method(&self) {
+ // ...
+ }
+ }
+
+ unsafe impl NSObjectProtocol for MyObject {}
+ );
+ ```
+* Whether classes are only available on the main thread is now automatically
+ inferred, and you only need to overwrite it if your class is doing something
+ different than its superclass.
+* **BREAKING**: Changed the syntax of `extern_class!` to be more succinct:
+ ```rust
+ // Before
+ use objc2::mutability::MainThreadOnly;
+ use objc2::runtime::NSObject;
+ use objc2::{extern_class, ClassType};
+
+ extern_class!(
+ #[derive(PartialEq, Eq, Hash, Debug)]
+ struct MyClass;
+
+ unsafe impl ClassType for MyClass {
+ type Super = NSObject;
+ type ThreadKind = dyn MainThreadOnly;
+ const NAME: &'static str = "MyClass";
+ }
+ );
+
+ // After
+ use objc2::runtime::NSObject;
+ use objc2::{extern_class, MainThreadOnly};
+
+ extern_class!(
+ #[unsafe(super(NSObject))]
+ #[thread_kind = MainThreadOnly]
+ #[name = "MyClass"]
+ #[derive(PartialEq, Eq, Hash, Debug)]
+ struct MyClass;
+ );
+ ```
+* **BREAKING**: Changed the syntax of `extern_protocol!` to be more succinct:
+ ```rust
+ // Before
+ extern_protocol!(
+ unsafe trait MyProtocol {
+ #[method(myMethod)]
+ fn myMethod(&self);
+ }
+
+ // The line below is now unnecessary
+ unsafe impl ProtocolType for dyn MyProtocol {}
+ );
+
+ // After
+ extern_protocol!(
+ unsafe trait MyProtocol {
+ #[unsafe(method(myMethod))]
+ fn myMethod(&self);
+ }
+ );
+ ```
+* **BREAKING**: Changed the syntax of `extern_methods!` to push the `unsafe` inside:
+ ```rust
+ // Before
+ extern_methods!(
+ unsafe impl MyObject {
+ #[method(myMethod)]
+ fn myMethod(&self);
+ }
+ );
+
+ // After
+ unsafe impl MyObject {
+ extern_methods!(
+ #[unsafe(method(myMethod))]
+ fn myMethod(&self);
+ );
+ }
+ ```
+* **BREAKING**: Moved the common `retain` and `alloc` methods from `ClassType`
+ to `Message` and `AllocAnyThread`/`MainThreadOnly`, respectively.
+
+ ```rust
+ // Before
+ use objc2::ClassType;
+ let my_obj = MyObject::init(MyObject::alloc());
+ let retained = my_obj.retain();
+
+ // After
+ use objc2::{Message, AllocAnyThread}; // Need different trait imports
+ let my_obj = MyObject::init(MyObject::alloc());
+ let retained = my_obj.retain();
+ ```
+* Print backtrace when catching exceptions with the `"catch-all"` feature.
+* Changed the return value of `ClassBuilder::add_protocol` to indicate whether
+ the protocol was already present on the class or not.
+* Merged `objc-sys` into this crate's `ffi` module.
+* **BREAKING**: Changed the signature of various `ffi` functions to no longer
+ accept nullable function pointers.
+* **BREAKING**: Changed the signature of various `ffi` functions to use the
+ proper `Bool` type instead of a typedef.
+* Made `exception::catch` safe.
+
+### Deprecated
+* Merged and deprecated the following `ffi` types:
+ - `ffi::objc_class` is merged into `runtime::AnyClass`.
+ - `ffi::objc_object` is merged into `runtime::AnyObject`.
+ - `ffi::objc_protocol` is merged into `runtime::AnyProtocol`.
+ - `ffi::IMP` is merged into `runtime::Imp`.
+ - `ffi::objc_method` is merged into `runtime::Method`.
+ - `ffi::objc_ivar` is merged into `runtime::Ivar`.
+ - `ffi::BOOL` and constants is merged into `runtime::Bool`.
+* Deprecated `ffi::id`. Use `AnyObject` instead.
+* Deprecated `NSObjectProtocol::is_kind_of`, use `isKindOfClass` or the new
+ `AnyObject::downcast_ref` method instead.
+* Deprecated `Retained::cast`, this has been renamed to `Retained::cast_unchecked`.
+* Renamed `DeclaredClass` to `DefinedClass`.
+* Merged `msg_send!` and `msg_send_id!`. The latter is now deprecated.
+* Merged `#[method(...)]` and `#[method_id(...)]` in `extern_methods!` and
+ `extern_protocol!`. `#[method_id(...)]` is now deprecated.
+* Deprecated `rc::Weak::from_id`. Use `rc::Weak::from_retained` instead.
+* Deprecated `ProtocolObject::from_id`. Use `ProtocolObject::from_retained`
+ instead.
+* Deprecated using `msg_send!` without a comma between arguments.
+
+ See the following for an example of how to upgrade:
+ ```rust
+ // Before
+ let _: NSInteger = msg_send![
+ obj,
+ addTrackingRect:rect
+ owner:obj
+ userData:ptr::null_mut::<c_void>()
+ assumeInside:Bool::NO
+ ];
+ // After
+ let _: NSInteger = msg_send![
+ obj,
+ addTrackingRect: rect, // Added comma
+ owner: obj, // Added comma
+ userData: ptr::null_mut::<c_void>(), // Added comma
+ assumeInside: false, // Added comma (optional when trailing)
+ ];
+ ```
+
+### Removed
+* **BREAKING**: Removed the `ffi::SEL` and `ffi::objc_selector` types. Use
+ `runtime::Sel` instead.
+* **BREAKING**: Removed `ffi` exception function pointer aliases.
+* **BREAKING**: Removed `mutability::HasStableHash`.
+* **BREAKING**: Removed various `_mut` methods:
+ - `Retained::as_mut_ptr`.
+ - `Retained::autorelease_mut`.
+ - `DeclaredClass::ivars_mut`.
+ - `ProtocolObject::from_mut`.
+ - `AutoreleasePool::ptr_as_mut`.
+ - `ClassType::as_super_mut`.
+* **BREAKING**: Disallow `&mut` message receivers (except in the special case
+ when the object is `AnyObject`, for better backwards compatibility with
+ `objc`).
+* **BREAKING**: Removed `AsMut`, `BorrowMut` and `DerefMut` implementations in
+ `extern_class!` and `declare_class!`.
+* **BREAKING**: Removed the `mutability` module, and everything within.
+ Classes now always use interior mutability.
+* **BREAKING**: Removed `DerefMut` implementation for `Retained<T>` when the
+ `Retained` was mutable.
+* **BREAKING**: Mark `Retained::autorelease` as `unsafe`, since we cannot
+ ensure that the given pool is actually the innermost pool.
+* **BREAKING**: Removed the deprecated `malloc` feature and `malloc_buf` dependency.
+* **BREAKING**: Removed aliases `DefaultId`, `IdFromIterator` and
+ `IdIntoIterator`, as well as their methods. Use the renamed traits instead.
+* **BREAKING**: Removed the ability to implement `ClassType` manually, to make
+ it easier to evolve the API going forwards.
+* **BREAKING**: Removed the deprecated `apple` Cargo feature flag.
+* The optimization for converting `msg_send_id![cls, alloc]` to a call to
+ the faster runtime function `objc_alloc` no longer works, use
+ `AllocAnyThread::alloc` or `MainThreadOnly::alloc` instead.
+
+### Fixed
+* Remove an incorrect assertion when adding protocols to classes in an unexpected
+ order.
+* **BREAKING**: Converted function signatures into using `extern "C-unwind"`
+ where applicable. This allows Rust and Objective-C unwinding to interoperate.
+* **BREAKING**: Use `CStr` in methods in the `runtime` module, since it's both
+ more performant, and more correct. Use the new `c"my_str"` syntax to migrate.
+
+ Specifically, this includes:
+ - `ClassBuilder::new`.
+ - `ClassBuilder::root`.
+ - `ClassBuilder::add_ivar`.
+ - `ProtocolBuilder::new`.
+ - `Sel::register`.
+ - `Sel::name`.
+ - `Ivar::name`.
+ - `Ivar::type_encoding`.
+ - `Method::return_type`.
+ - `Method::argument_type`.
+ - `AnyClass::get`.
+ - `AnyClass::name`.
+ - `AnyClass::instance_variable`.
+ - `AnyProtocol::get`.
+ - `AnyProtocol::name`.
+* Clarified that `exception::catch` does not catch Rust panics.
+* Improved the `Debug` impl when deriving via `extern_class!`.
+* Generic objects now always implement common traits `PartialEq`, `Eq` and
+ `Hash`, instead of guarding them behind `T: Message`.
+* Prevented main thread only classes created using `declare_class!` from
+ automatically implementing the auto traits `Send` and `Sync`.
+* **BREAKING**: Fixed the signature of `NSObjectProtocol::isEqual` to take a
+ nullable argument.
+* Fixed handling of methods that return NULL errors. This affected for example
+ `-[MTLBinaryArchive serializeToURL:error:]`.
+* Fixed unwinding while using writeback / error parameters.
+
+
+## [0.5.2] - 2024-05-21
+[0.5.2]: https://github.com/madsmtm/objc2/compare/objc2-0.5.1...objc2-0.5.2
+
+### Added
+* Added `Retained::autorelease_ptr`.
+* Added the feature flag `"relax-sign-encoding"`, which when enabled, allows
+ using e.g. `NSInteger` in places where you would otherwise have to use
+ `NSUInteger`.
+
+### Changed
+* Renamed `Id` to `Retained`, to better reflect what it represents.
+
+ The old name is kept as a soft-deprecated type-alias (will be fully
+ deprecated in `v0.6.0`).
+
+ The same is done for:
+ - `rc::WeakId` to `rc::Weak`.
+ - `rc::DefaultId` to `rc::DefaultRetained`.
+ - `rc::IdFromIterator` to `rc::RetainedFromIterator`.
+ - `rc::IdIntoIterator` to `rc::RetainedIntoIterator`.
+
+### Deprecated
+* Deprecated the `apple` Cargo feature flag, it is assumed by default on Apple
+ platforms.
+
+
+## [0.5.1] - 2024-04-17
+[0.5.1]: https://github.com/madsmtm/objc2/compare/objc2-0.5.0...objc2-0.5.1
+
+### Added
+* Made the following runtime methods available without the `"malloc"` feature
+ flag:
+ - `Method::return_type`.
+ - `Method::argument_type`.
+ - `AnyClass::classes`.
+ - `AnyClass::instance_methods`.
+ - `AnyClass::adopted_protocols`.
+ - `AnyClass::instance_variables`.
+ - `AnyProtocol::protocols`.
+ - `AnyProtocol::adopted_protocols`.
+* Added `Id::into_raw` as the oppositve of `Id::from_raw`.
+* Added the following missing methods on `NSObjectProtocol`:
+ - `isEqual`.
+ - `hash`.
+ - `isKindOfClass`.
+ - `isMemberOfClass`.
+ - `respondsToSelector`.
+ - `conformsToProtocol`.
+ - `description`.
+ - `debugDescription`.
+ - `isProxy`.
+ - `retainCount`.
+* Added `NSObject::init`.
+* Added `NSObject::doesNotRecognizeSelector`.
+
+### Changed
+* Moved `ClassBuilder` and `ProtocolBuilder` from the `declare` module to the
+ `runtime` module. The old locations are deprecated.
+* Enabled the `"verify"` feature flag's functionality when debug assertions are
+ enabled.
+* Renamed `Id::new` to `Id::from_raw`. The previous name is kept as a
+ deprecated alias.
+
+
+## [0.5.0] - 2023-12-03
+[0.5.0]: https://github.com/madsmtm/objc2/compare/objc2-0.4.1...objc2-0.5.0
+
+### Added
+* Added the following traits to the `mutability` module (see the documentation
+ for motivation and usage info):
+ - `HasStableHash`.
+ - `IsAllowedMutable`.
+ - `IsMainThreadOnly`.
+ - `CounterpartOrSelf`.
+* Added new `encode` traits `EncodeReturn`, `EncodeArgument` and
+ `EncodeArguments`.
+* Added methods `as_ptr` and `as_mut_ptr` to `Allocated`.
+* Added optimization for converting `msg_send_id![cls, alloc]` to a call to
+ the faster runtime function `objc_alloc`.
+* Added `DeclaredClass`, which represents classes that are declared in Rust.
+* Added `Allocated::set_ivars`, which sets the instance variables of an
+ object, and returns the new `rc::PartialInit`.
+* Added the ability for `msg_send_id!` to call `super` methods.
+* Implement `Send` and `Sync` for `ProtocolObject` if the underlying protocol
+ implements it.
+* Added ability to create `Send` and `Sync` versions of
+ `ProtocolObject<dyn NSObjectProtocol>`.
+
+### Changed
+* **BREAKING**: Changed how instance variables work in `declare_class!`.
+
+ Previously, instance variables had to implement `Encode`, and you had to
+ initialize them properly, which was difficult to ensure.
+
+ Now, you implement the new `DeclaredClass` trait instead, which helps to
+ ensure all of this for you.
+
+ ```rust
+ // Before
+ declare_class!(
+ struct MyObject {
+ object: IvarDrop<Id<NSObject>, "_object">,
+ data: IvarDrop<Option<Box<MyData>>, "_data">,
+ }
+
+ mod ivars;
+
+ unsafe impl ClassType for MyObject {
+ type Super = NSObject;
+ type Mutability = InteriorMutable;
+ const NAME: &'static str = "MyObject";
+ }
+
+ unsafe impl MyObject {
+ #[method(init)]
+ unsafe fn init(this: *mut Self) -> Option<NonNull<Self>> {
+ let this: Option<&mut Self> = msg_send![super(this), init];
+ this.map(|this| {
+ Ivar::write(&mut this.object, NSObject::new());
+ Ivar::write(&mut this.data, Box::new(MyData::new()));
+ NonNull::from(this)
+ })
+ }
+ }
+ );
+
+ extern_methods!(
+ unsafe impl MyObject {
+ #[method_id(new)]
+ pub fn new() -> Id<Self>;
+ }
+ );
+
+ fn main() {
+ let obj = MyObject::new();
+ println!("{:?}", obj.object);
+ }
+
+ // After
+ struct MyIvars {
+ object: Id<NSObject>,
+ data: Option<Box<MyData>>,
+ }
+
+ declare_class!(
+ struct MyObject;
+
+ unsafe impl ClassType for MyObject {
+ type Super = NSObject;
+ type Mutability = InteriorMutable;
+ const NAME: &'static str = "MyObject";
+ }
+
+ impl DeclaredClass for MyObject {
+ type Ivars = MyIvars;
+ }
+
+ unsafe impl MyObject {
+ #[method_id(init)]
+ pub fn init(this: Allocated<Self>) -> Option<Id<Self>> {
+ let this = this.set_ivars(MyIvars {
+ object: NSObject::new(),
+ data: MyData::new(),
+ });
+ unsafe { msg_send_id![super(this), init] }
+ }
+ }
+ );
+
+ extern_methods!(
+ unsafe impl MyObject {
+ #[method_id(new)]
+ pub fn new() -> Id<Self>;
+ }
+ );
+
+ fn main() {
+ let obj = MyObject::new();
+ println!("{:?}", obj.ivars().object);
+ }
+ ```
+* **BREAKING**: `AnyClass::verify_sel` now take more well-defined types
+ `EncodeArguments` and `EncodeReturn`.
+* **BREAKING**: Changed how the `mutability` traits work; these no longer have
+ `ClassType` as a super trait, allowing them to work for `ProtocolObject` as
+ well.
+
+ This effectively means you can now `copy` a `ProtocolObject<dyn NSCopying>`.
+* **BREAKING**: Allow implementing `DefaultId` for any type, not just those
+ who are `IsAllocableAnyThread`.
+* **BREAKING**: Moved the `MethodImplementation` trait from the `declare`
+ module to the `runtime` module.
+* **BREAKING**: Moved the `MessageReceiver` trait to the `runtime` module.
+* **BREAKING**: Make the `MessageReceiver` trait no longer implemented for
+ references to `Id`. Dereference the `Id` yourself.
+
+ Note: Passing `&Id` in `msg_send!` is still supported.
+* **BREAKING**: `MessageReceiver::send_message` and
+ `MessageReceiver::send_super_message` now take `EncodeArguments` and return
+ `EncodeReturn`, instead of internal traits.
+
+ This is done to make `MessageReceiver` more straightforward to understand,
+ although it now also has slightly less functionality than `msg_send!`.
+
+ In particular automatic conversion of `bool` is not supported in
+ `MessageReceiver`.
+* Relaxed the requirements for receivers in `MethodImplementation`; now,
+ anything that implements `MessageReceiver` can be used as the receiver of
+ a method.
+* **BREAKING**: Renamed the associated types `Ret` and `Args` on
+ `MethodImplementation` to `Return` and `Arguments`.
+* **BREAKING**: Make `rc::Allocated` allowed to be `NULL` internally, such
+ that uses of `Option<Allocated<T>>` is now simply `Allocated<T>`.
+* `AnyObject::class` now returns a `'static` reference to the class.
+* Relaxed `ProtocolType` requirement on `ProtocolObject`.
+* **BREAKING**: Updated `encode` types to those from `objc2-encode v4.0.0`.
+
+### Fixed
+* Fixed the name of the protocol that `NSObjectProtocol` references.
+* Allow cloning `Id<AnyObject>`.
+* **BREAKING**: Restrict message sending to `&mut` references to things that
+ implement `IsAllowedMutable`.
+* Disallow the ability to use non-`Self`-like types as the receiver in
+ `declare_class!`.
+* Allow adding instance variables with the same name on Apple platforms.
+* **BREAKING**: Make loading instance variables robust and sound in the face
+ of instance variables with the same name.
+
+ To read or write the instance variable for an object, you should now use the
+ `load`, `load_ptr` and `load_mut` methods on `Ivar`, instead of the `ivar`,
+ `ivar_ptr` and `ivar_mut` methods on `AnyObject`.
+
+ This _is_ more verbose, but it also ensures that the class for the instance
+ variable you're loading is the same as the one the instance variable you
+ want to access is defined on.
+
+ ```rust
+ // Before
+ let number = unsafe { *obj.ivar::<u32>("number") };
+
+ // After
+ let ivar = cls.instance_variable("number").unwrap();
+ let number = unsafe { *ivar.load::<u32>(&obj) };
+ ```
+* Implement `RefEncode` normally for `c_void`. This makes `AtomicPtr<c_void>`
+ implement `Encode`.
+
+### Removed
+* **BREAKING**: Removed `ProtocolType` implementation for `NSObject`.
+ Use the more precise `NSObjectProtocol` trait instead!
+* **BREAKING**: Removed the `MessageArguments` trait.
+* **BREAKING**: Removed the following items from the `declare` module: `Ivar`,
+ `IvarEncode`, `IvarBool`, `IvarDrop`, `IvarType` and `InnerIvarType`.
+
+ Ivar functionality is available in a different form now, see above under
+ "Changed".
+* **BREAKING**: Removed `ClassBuilder::add_static_ivar`.
+
+
+## [0.4.1] - 2023-07-31
+[0.4.1]: https://github.com/madsmtm/objc2/compare/objc2-0.4.0...objc2-0.4.1
+
+### Added
+* Allow using `MainThreadMarker` in `extern_methods!`.
+* Added the feature flag `"relax-void-encoding"`, which when enabled, allows
+ using `*mut c_void` in a few places where you would otherwise have to
+ specify the encoding precisely.
+
+### Changed
+* Renamed `runtime` types:
+ - `Object` to `AnyObject`.
+ - `Class` to `AnyClass`.
+ - `Protocol` to `AnyProtocol`.
+
+ To better fit with Swift's naming scheme. The types are still available
+ under the old names as deprecated aliases.
+
+### Fixed
+* **BREAKING**: Updated `encode` types to those from `objc2-encode v3.0.0`.
+
+ This is technically a breaking change, but it should allow this crate to be
+ compiled together with pre-release versions of it, meaning that in practice
+ strictly more code out there will compile because of this. Hence it was
+ deemed the better trade-off.
+
+
+## [0.4.0] - 2023-06-20
+[0.4.0]: https://github.com/madsmtm/objc2/compare/objc2-0.3.0-beta.5...objc2-0.4.0
+
+### Added
+* Added `objc2::rc::autoreleasepool_leaking`, and improve performance of
+ objects `Debug` impls.
+* **BREAKING**: Added associated type `ClassType::Mutability`, which replaces
+ the ownership type on `Id`, and must be specified for all class types.
+
+ An example:
+ ```rust
+ // Before
+ use objc2::runtime::NSObject;
+ use objc2::{declare_class, ClassType};
+
+ declare_class!(
+ struct MyDelegate;
+
+ unsafe impl ClassType for MyDelegate {
+ type Super = NSObject;
+ }
+
+ // ... methods
+ );
+
+ // After
+ use objc2::runtime::NSObject;
+ use objc2::mutability::InteriorMutable;
+ use objc2::{declare_class, ClassType};
+
+ declare_class!(
+ struct MyDelegate;
+
+ unsafe impl ClassType for MyDelegate {
+ type Super = NSObject;
+ type Mutability = InteriorMutable; // Added
+ }
+
+ // ... methods
+ );
+ ```
+* Added `ClassType::retain`, which is a safe way to go from a reference `&T`
+ to an `Id<T>`.
+* Added `mutability` module, containing various types that can be specified
+ for the above.
+* Preliminary support for specifying `where` bounds on methods inside
+ `extern_protocol!` and `extern_methods!`.
+* Allow arbitrary expressions in `const NAME` in `extern_class!`,
+ `extern_protocol!` and `declare_class!`.
+* Added `rc::IdIntoIterator` helper trait and forwarding `IntoIterator`
+ implementations for `rc::Id`.
+* Added `rc::IdFromIterator` helper trait for implementing `IntoIterator`
+ for `rc::Id`.
+* Added `Display` impl for `runtime::Class`, `runtime::Sel` and
+ `runtime::Protocol`.
+* Added `Debug` impl for `runtime::Method` and `runtime::Ivar`.
+* Added `Method::set_implementation`.
+* Added `Method::exchange_implementation`.
+* Added `Object::set_class`.
+
+### Changed
+* **BREAKING**: `objc2::rc::AutoreleasePool` is now a zero-sized `Copy` type
+ with a lifetime parameter, instead of the lifetime parameter being the
+ reference it was behind.
+* **BREAKING**: Made `Id::autorelease` and `Id::autorelease_return` be
+ associated functions instead of methods. This means they now have to be
+ called as `Id::autorelease(obj, pool)` instead of `obj.autorelease(pool)`.
+
+ Additionally, rename the mutable version to `Id::autorelease_mut`.
+* **BREAKING**: Moved `VerificationError`, `ProtocolObject` and
+ `ImplementedBy` into the `runtime` module.
+* Relaxed a `fmt::Debug` bound on `WeakId`'s own `fmt::Debug` impl.
+* Changed `Debug` impl for `runtime::Class`, `runtime::Sel` and
+ `runtime::Protocol` to give more information.
+* **BREAKING**: Updated `encode` module to `objc2-encode v2.0.0`.
+
+### Fixed
+* Fixed using autorelease pools on 32bit macOS and older macOS versions.
+* Fixed memory leaks in and improved performance of `exception::catch`.
+
+### Removed
+* **BREAKING**: Removed `rc::SliceId`, since it is implementable outside
+ `objc2` from the layout guarantees of `rc::Id`.
+* **BREAKING**: Removed `Ownership` type parameter from `Id`, as well as
+ `rc::Ownership`, `rc::Owned`, `rc::Shared`, `Id::from_shared` and
+ `Id::into_shared`. This functionality has been moved from being at the
+ "usage-level", to being moved to the "type-level" in the associated type
+ `ClassType::Mutability`.
+
+ While being slightly more restrictive, it should vastly help you avoid
+ making mistakes around mutability (e.g. it is usually a mistake to make a
+ mutable reference `&mut` to an Objective-C object).
+
+ An example:
+ ```rust
+ // Before
+ use objc2::rc::{Id, Shared};
+ use objc2::runtime::NSObject;
+ use objc2::msg_send_id;
+
+ let obj: Id<NSObject, Shared> = unsafe { msg_send_id![NSObject::class(), new] };
+
+ // After
+ use objc2::rc::Id;
+ use objc2::runtime::NSObject;
+ use objc2::msg_send_id;
+
+ let obj: Id<NSObject> = unsafe { msg_send_id![NSObject::class(), new] };
+ ```
+* **BREAKING**: Removed `impl<T> TryFrom<WeakId<T>> for Id<T>` impl since it
+ did not have a proper error type, making it less useful than `WeakId::load`.
+* **BREAKING**: Removed forwarding `Iterator` implementation for `Id`, since
+ it conflicts with the `IntoIterator` implementation that it now has instead.
+
+
+## [0.3.0-beta.5] - 2023-02-07
+[0.3.0-beta.5]: https://github.com/madsmtm/objc2/compare/objc2-0.3.0-beta.4...objc2-0.3.0-beta.5
+
+### Added
+* Support `#[cfg(...)]` attributes in `extern_class!` macro.
+* Added support for selectors with multiple colons like `abc::` in the `sel!`,
+ `extern_class!`, `extern_protocol!` and `declare_class!` macros.
+* Added ability to use `#[method_id(mySelector:)]` inside `declare_class!`,
+ like you would do in `extern_methods!`.
+* Added 16-fold impls for `EncodeArguments`, `MessageArguments`, and `MethodImplementation`.
+* Added `NSObjectProtocol` trait for allowing `ProtocolObject` to implement
+ `Debug`, `Hash`, `PartialEq` and `Eq`.
+* Support running `Drop` impls on `dealloc` in `declare_class!`.
+* Added `declare::IvarEncode` and `declare::IvarBool` types.
+* **BREAKING**: Moved the `objc2_encode` traits to `objc2::encode`.
+
+ This includes removing the `EncodeConvert` and `EncodeArguments` traits.
+* Added support for out-parameters like `&mut Id<_, _>` in `msg_send!`,
+ `msg_send_id!` and `extern_methods!`.
+
+### Changed
+* **BREAKING**: Using the automatic `NSError**`-to-`Result` functionality in
+ `extern_methods!` now requires a trailing underscore (so now it's
+ `#[method(myMethod:error:_)]` instead of `#[method(myMethod:error:)]`).
+* **BREAKING**: Fundamentally changed how protocols work. Instead of being
+ structs with inherent methods, they're now traits. This means that you can
+ use their methods more naturally from your Objective-C objects.
+
+ An example:
+ ```rust
+ // Before
+ extern_protocol!(
+ struct MyProtocol;
+
+ unsafe impl ProtocolType for MyProtocol {
+ #[method(myMethod)]
+ fn myMethod(&self);
+ }
+ );
+
+ let obj: &SomeObjectThatImplementsTheProtocol = ...;
+ let proto: &MyProtocol = obj.as_protocol();
+ proto.myMethod();
+
+ // After
+ extern_protocol!(
+ unsafe trait MyProtocol {
+ #[method(myMethod)]
+ fn myMethod(&self);
+ }
+
+ unsafe impl ProtocolType for dyn MyProtocol {}
+ );
+
+ let obj: &SomeObjectThatImplementsTheProtocol = ...;
+ obj.myMethod();
+ // Or
+ let proto: &ProtocolObject<dyn MyProtocol> = ProtocolObject::from_ref(obj);
+ proto.myMethod();
+ ```
+
+ The `ConformsTo` trait has similarly been removed, and the `ImplementedBy`
+ trait and `ProtocolObject` struct has been introduced instead.
+* **BREAKING**: Moved `NSObject::is_kind_of` to the new `NSObjectProtocol`.
+* **BREAKING**: Removed support for custom `dealloc` impls in
+ `declare_class!`. Implement `Drop` for the type instead.
+* **BREAKING**: Changed how the `declare_class!` macro works.
+
+ Now, you must explicitly specify the "kind" of ivar you want, as well as the
+ ivar name. Additionally, the class name must be explicitly specified.
+
+ This change is done to make it easier to see what's going on beneath the
+ hood (the name will be available in the final binary, which is important to
+ be aware of)!
+
+ An example:
+ ```rust
+ // Before
+ declare_class!(
+ struct MyClass {
+ pub ivar: u8,
+ another_ivar: bool,
+ box_ivar: IvarDrop<Box<i32>>,
+ }
+
+ unsafe impl ClassType for MyClass {
+ type Super = NSObject;
+ }
+ );
+
+ // After
+ declare_class!(
+ struct MyClass {
+ pub ivar: IvarEncode<u8, "_ivar">,
+ another_ivar: IvarBool<"_another_ivar">,
+ box_ivar: IvarDrop<Box<i32>, "_box_ivar">,
+ }
+
+ // Helper types for ivars will be put in here
+ mod ivars;
+
+ unsafe impl ClassType for MyClass {
+ type Super = NSObject;
+ const NAME: &'static str = "MyClass";
+ }
+ );
+
+ ```
+* Updated `ffi` module to `objc-sys v0.3.0`. This includes:
+ * Added `free` method (same as `libc::free`).
+ * Moved documentation from `README.md` to `docs.rs`.
+ * Added `objc_terminate`, `object_isClass`, `objc_alloc` and
+ `objc_allocWithZone` now that Rust's macOS deployment target is 10.12.
+ * Slightly improved documentation.
+ * Internal optimizations.
+ * **BREAKING**: Changed `links` key from `objc_0_2` to `objc_0_3` (so
+ `DEP_OBJC_0_2_CC_ARGS` in build scripts becomes `DEP_OBJC_0_3_CC_ARGS`).
+ * **BREAKING**: Renamed `rust_objc_sys_0_2_try_catch_exception` to
+ `try_catch`.
+ * Deprecated the unstable function `try_catch`, it is exposed in
+ `objc2-exception-helper` instead.
+* **BREAKING**: Updated `encode` module to `objc2-encode v2.0.0-pre.4`.
+
+### Fixed
+* Allow empty structs in `declare_class!` macro.
+* Allow using `extern_methods!` without the `ClassType` trait in scope.
+* Fixed a few small issues with `declare_class!`.
+* Fixed `()` being possible in argument position in `msg_send!`.
+
+
+## [0.3.0-beta.4] - 2022-12-24
+[0.3.0-beta.4]: https://github.com/madsmtm/objc2/compare/objc2-0.3.0-beta.3...objc2-0.3.0-beta.4
+
+### Added
+* Allow directly specifying class name in `extern_class!` macro.
+* Added `ClassType::alloc`.
+
+ This means you can now simplify your code as follows:
+ ```rust
+ // Before
+ let obj: Id<NSObject, Shared> = unsafe {
+ msg_send_id![msg_send_id![NSObject::class(), alloc], init]
+ };
+
+ // After
+ let obj: Id<NSObject, Shared> = unsafe {
+ msg_send_id![NSObject::alloc(), init]
+ };
+ ```
+* Added `Class::class_method`.
+* Added the ability to specify `error: _`, `somethingReturningError: _` and
+ so on at the end of `msg_send!`/`msg_send_id!`, and have it automatically
+ return a `Result<..., Id<NSError, Shared>>`.
+* Added the ability to specify an extra parameter at the end of the selector
+ in methods declared with `extern_methods!`, and let that be the `NSError**`
+ parameter.
+* Added `#[method_id(...)]` attribute to `extern_methods!`.
+* Added `"verify"` feature as a replacement for the `"verify_message"`
+ feature.
+* Added `extern_protocol!` macro and `ProtocolType` trait.
+* Added `ConformsTo` trait for marking that a type conforms to a specific
+ protocol.
+* Added `Encode` impl for `Option<Sel>`.
+* Added `objc2::runtime::NSObject` - before, this was only available under
+ `objc2::foundation::NSObject`.
+* Added `objc2::runtime::NSZone` - before, this was only available under
+ `objc2::foundation::NSZone`.
+
+### Changed
+* Allow other types than `&Class` as the receiver in `msg_send_id!` methods
+ of the `new` family.
+* **BREAKING**: Changed the `Allocated` struct to be used as `Allocated<T>`
+ instead of `Id<Allocated<T>, O>`.
+* Verify the message signature of overridden methods when declaring classes if
+ the `verify` feature is enabled.
+* Verify in `declare_class!` that protocols are implemented correctly.
+* **BREAKING**: Changed the name of the attribute macro in `extern_methods`
+ from `#[sel(...)]` to `#[method(...)]`.
+* **BREAKING**: Changed `extern_methods!` and `declare_class!` such that
+ associated functions whose first parameter is called `this`, is treated as
+ instance methods instead of class methods.
+* **BREAKING**: Message verification is now enabled by default. Your message
+ sends might panic with `debug_assertions` enabled if they are detected to
+ be invalid. Test your code to see if that is the case!
+* **BREAKING**: `declare_class!` uses `ConformsTo<...>` instead of the
+ temporary `Protocol<...>` syntax.
+* Require implementors of `Message` to support weak references.
+* **BREAKING**: Moved `objc2::foundation` into `icrate::Foundation`.
+* **BREAKING**: Moved `objc2::ns_string` into `icrate::ns_string`.
+* Updated `ffi` module to `objc-sys v0.2.0-beta.3`. This includes:
+ * Fixed minimum deployment target on macOS Aarch64.
+* **BREAKING**: Updated `encode` module `objc2-encode v2.0.0-pre.3`.
+
+### Fixed
+* Fixed duplicate selector extraction in `extern_methods!`.
+* Fixed using `deprecated` attributes in `declare_class!`.
+* Fixed `cfg` attributes on methods and implementations in `declare_class!`.
+
+### Removed
+* **BREAKING**: Removed `"verify_message"` feature. It has been mostly
+ replaced by `debug_assertions` and the `"verify"` feature.
+
+
+## [0.3.0-beta.3] - 2022-09-01
+[0.3.0-beta.3]: https://github.com/madsmtm/objc2/compare/objc2-0.3.0-beta.2...objc2-0.3.0-beta.3
+
+### Added
+* Added `Ivar::write`, `Ivar::as_ptr` and `Ivar::as_mut_ptr` for safely
+ querying and modifying instance variables inside `init` methods.
+* Added `IvarDrop<T>` to allow storing complex `Drop` values in ivars
+ (currently `rc::Id<T, O>`, `Box<T>`, `Option<rc::Id<T, O>>` or
+ `Option<Box<T>>`).
+* **BREAKING**: Added required `ClassType::NAME` constant for statically
+ determining the name of a specific class.
+* Allow directly specifying class name in `declare_class!` macro.
+
+### Removed
+* **BREAKING**: `MaybeUninit` no longer implements `IvarType` directly; use
+ `Ivar::write` instead.
+
+
+## [0.3.0-beta.2] - 2022-08-28
+[0.3.0-beta.2]: https://github.com/madsmtm/objc2/compare/objc2-0.3.0-beta.1...objc2-0.3.0-beta.2
+
+### Added
+* Added the `"unstable-static-class"` and `"unstable-static-class-inlined"`
+ feature flags to make the `class!` macro zero cost.
+* Moved the external crate `objc2_foundation` into `objc2::foundation` under
+ (default) feature flag `"foundation"`.
+* Added `declare_class!`, `extern_class!` and `ns_string!` macros from
+ `objc2-foundation`.
+* Added helper method `ClassBuilder::add_static_ivar`.
+* **BREAKING**: Added `ClassType` trait, and moved the associated `class`
+ methods that `extern_class!` and `declare_class!` generated to that. This
+ means you'll have to `use objc2::ClassType` whenever you want to use e.g.
+ `NSData::class()`.
+* Added `Id::into_super`.
+* Added `extern_methods!` macro.
+* Added ability to call `msg_send![super(obj), ...]` without explicitly
+ specifying the superclass.
+* Added automatic conversion of `bool` to/from the Objective-C `BOOL` in
+ `msg_send!`, `msg_send_id!`, `extern_methods!` and `declare_class!`.
+
+ Example:
+ ```rust
+ // Before
+ use objc2::{msg_send, msg_send_bool};
+ use objc2::rc::{Id, Shared};
+ use objc2::runtime::{Bool, Object};
+
+ let obj: Id<Object, Shared>;
+ let _: () = unsafe { msg_send![&obj, setArg: Bool::YES] };
+ let is_equal = unsafe { msg_send_bool![&obj, isEqual: &*obj] };
+
+ // After
+ use objc2::msg_send;
+ use objc2::rc::{Id, Shared};
+ use objc2::runtime::Object;
+
+ let obj: Id<Object, Shared>;
+ let _: () = unsafe { msg_send![&obj, setArg: true] };
+ let is_equal: bool = unsafe { msg_send![&obj, isEqual: &*obj] };
+ ```
+
+### Changed
+* **BREAKING**: Change syntax in `extern_class!` macro to be more Rust-like.
+* **BREAKING**: Change syntax in `declare_class!` macro to be more Rust-like.
+* **BREAKING**: Renamed `Id::from_owned` to `Id::into_shared`.
+* **BREAKING**: The return type of `msg_send_id!` is now more generic; it can
+ now either be `Option<Id<_, _>>` or `Id<_, _>` (if the latter, it'll panic
+ if the method returned `NULL`).
+
+ Example:
+ ```rust
+ // Before
+ let obj: Id<Object, Shared> = unsafe {
+ msg_send_id![msg_send_id![class!(MyObject), alloc], init].unwrap()
+ };
+
+ // After
+ let obj: Id<Object, Shared> = unsafe {
+ msg_send_id![msg_send_id![class!(MyObject), alloc], init]
+ };
+ ```
+* Updated `ffi` module to `objc-sys v0.2.0-beta.2`. This includes:
+ * Fixed `docs.rs` setup.
+* **BREAKING**: Updated `encode` module `objc2-encode v2.0.0-pre.2`.
+
+ In particular, `Encoding` no longer has a lifetime parameter:
+ ```rust
+ // Before
+ #[repr(C)]
+ pub struct NSRange {
+ pub location: usize,
+ pub length: usize,
+ }
+ unsafe impl Encode for NSRange {
+ const ENCODING: Encoding<'static> = Encoding::Struct(
+ "_NSRange", // This is how the struct is defined in C header files
+ &[usize::ENCODING, usize::ENCODING]
+ );
+ }
+ unsafe impl RefEncode for NSRange {
+ const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
+ }
+
+ // After
+ #[repr(C)]
+ pub struct NSRange {
+ pub location: usize,
+ pub length: usize,
+ }
+ unsafe impl Encode for NSRange {
+ const ENCODING: Encoding = Encoding::Struct(
+ "_NSRange", // This is how the struct is defined in C header files
+ &[usize::ENCODING, usize::ENCODING]
+ );
+ }
+ unsafe impl RefEncode for NSRange {
+ const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
+ }
+ ```
+
+### Deprecated
+* Deprecated `msg_send_bool!` in favour of new functionality on `msg_send!`
+ that allows seamlessly handling `bool`.
+
+
+## [0.3.0-beta.1] - 2022-07-19
+[0.3.0-beta.1]: https://github.com/madsmtm/objc2/compare/objc2-0.3.0-beta.0...objc2-0.3.0-beta.1
+
+### Added
+* Added `msg_send_id!` to help with following Objective-C's memory management
+ rules. **It is highly recommended that you use this instead of doing memory
+ management yourself!**
+
+ Example:
+ ```rust
+ // Before
+ let obj: Id<Object, Shared> = unsafe {
+ let obj: *mut Object = msg_send![class!(MyObject), alloc];
+ let obj: *mut Object = msg_send![obj, init];
+ Id::new(obj).unwrap()
+ };
+
+ // After
+ let obj: Id<Object, Shared> = unsafe {
+ msg_send_id![msg_send_id![class!(MyObject), alloc], init].unwrap()
+ };
+ ```
+* Added the `"unstable-static-sel"` and `"unstable-static-sel-inlined"`
+ feature flags to make the `sel!` macro (and by extension, the `msg_send!`
+ macros) faster.
+* Added `"unstable-c-unwind"` feature.
+* Added unsafe function `Id::cast` for converting between different types of
+ objects.
+* Added `Object::ivar_ptr` to allow direct access to instance variables
+ through `&Object`.
+* Added `VerificationError` as more specific return type from
+ `Class::verify_sel`.
+* Added `rc::Allocated` struct which is used within `msg_send_id!`.
+* Added `Class::responds_to`.
+* Added `exception::Exception` object to improve error messages from caught
+ exceptions.
+* Added `declare::Ivar<T>` helper struct. This is useful for building safe
+ abstractions that access instance variables.
+* Added `Id::from_owned` helper function.
+
+### Changed
+* **BREAKING**: `Sel` is now required to be non-null, which means that you
+ have to ensure that any selectors you receive from method calls are
+ non-null before using them.
+* **BREAKING**: `ClassBuilder::root` is now generic over the function pointer,
+ meaning you will have to coerce initializer functions to pointers like in
+ `ClassBuilder::add_method` before you can use it.
+* **BREAKING**: Moved `MessageReceiver::verify_message` to `Class::verify_sel`
+ and changed return type.
+* Improved debug output with `verify_message` feature enabled.
+* **BREAKING**: Changed `MessageReceiver::send_message` to panic instead of
+ returning an error.
+* **BREAKING**: Renamed `catch_all` feature to `catch-all`.
+* **BREAKING**: Made passing the function pointer argument to
+ `ClassBuilder::add_method`, `ClassBuilder::add_class_method` and similar
+ more ergonomic.
+
+ Let's say you have the following code:
+ ```rust
+ // Before
+ let init: extern "C" fn(&mut Object, Sel) -> *mut Object = init;
+ builder.add_method(sel!(init), init);
+ ```
+
+ Unfortunately, you will now encounter a very confusing error:
+ ```text
+ |
+ 2 | builder.add_method(sel!(init), init);
+ | ^^^^^^^^^^ implementation of `MethodImplementation` is not general enough
+ |
+ = note: `MethodImplementation` would have to be implemented for the type `for<'r> extern "C" fn(&'r mut Object, Sel) -> *mut Object`
+ = note: ...but `MethodImplementation` is actually implemented for the type `extern "C" fn(&'0 mut Object, Sel) -> *mut Object`, for some specific lifetime `'0`
+ ```
+
+ To fix this, let the compiler infer the argument and return types:
+ ```rust
+ // After
+ let init: extern "C" fn(_, _) -> _ = init;
+ builder.add_method(sel!(init), init);
+ ```
+* Updated `ffi` module to `objc-sys v0.2.0-beta.1`. This includes:
+ * Added `unstable-c-unwind` feature.
+ * Use `doc_auto_cfg` to improve documentation output.
+* **BREAKING**: Updated `encode` module `objc2-encode v2.0.0-pre.1`.
+
+### Fixed
+* **BREAKING**: Disallow throwing `nil` exceptions in `exception::throw`.
+
+### Removed
+* **BREAKING**: Removed the `Sel::from_ptr` method.
+* **BREAKING**: Removed `MessageError`.
+
+
+## [0.3.0-beta.0] - 2022-06-13
+[0.3.0-beta.0]: https://github.com/madsmtm/objc2/compare/objc2-0.3.0-alpha.6...objc2-0.3.0-beta.0
+
+### Added
+* Added deprecated `Object::get_ivar` and `Object::get_mut_ivar` to make
+ upgrading easier.
+* Allow using `From`/`TryFrom` to convert between `rc::Id` and `rc::WeakId`.
+* Added `Bool::as_bool` (more descriptive name than `Bool::is_true`).
+* Added convenience method `Id::as_ptr` and `Id::as_mut_ptr`.
+* The `objc2-encode` dependency is now exposed as `objc2::encode`.
+* Added `Id::retain_autoreleased` to allow following Cocoas memory management
+ rules more efficiently.
+* Consistently allow trailing commas in `msg_send!`.
+* Added `msg_send_bool!`, a less error-prone version of `msg_send!` for
+ Objective-C methods that return `BOOL`.
+* Implemented `MethodImplementation` for `unsafe` function pointers.
+
+### Changed
+* **BREAKING**: Changed signature of `Id::new` and `Id::retain` from
+ `fn(NonNull<T>) -> Id<T>` to `fn(*mut T) -> Option<Id<T>>`.
+
+ Concretely, you will have to change your code as follows.
+ ```rust
+ // Before
+ let obj: *mut Object = unsafe { msg_send![class!(NSObject), new] };
+ let obj = NonNull::new(obj).expect("failed to allocate object");
+ let obj = unsafe { Id::new(obj) };
+ // After
+ let obj: *mut Object = unsafe { msg_send![class!(NSObject), new] };
+ let obj = unsafe { Id::new(obj) }.expect("failed to allocate object");
+ ```
+* Allow specifying any receiver `T: Message` for methods added with
+ `ClassBuilder::add_method`.
+* Renamed `ClassDecl` and `ProtocolDecl` to `ClassBuilder` and
+ `ProtocolBuilder`. The old names are kept as deprecated aliases.
+* **BREAKING**: Changed how `msg_send!` works wrt. capturing its arguments.
+
+ This will require changes to your code wherever you used `Id`, for example:
+ ```rust
+ // Before
+ let obj: Id<Object, Owned> = ...;
+ let p: i32 = unsafe { msg_send![obj, parameter] };
+ let _: () = unsafe { msg_send![obj, setParameter: p + 1] };
+ // After
+ let mut obj: Id<Object, Owned> = ...;
+ let p: i32 = unsafe { msg_send![&obj, parameter] };
+ let _: () = unsafe { msg_send![&mut obj, setParameter: p + 1] };
+ ```
+
+ Notice that we now clearly pass `obj` by reference, and therein also
+ communicate the mutability of the object (in the first case, immutable, and
+ in the second, mutable).
+
+ If you previously used `*mut Object` or `&Object` as the receiver, message
+ sending should work exactly as before.
+* **BREAKING**: `Class` no longer implements `Message` (but it can still be
+ used as the receiver in `msg_send!`, so this is unlikely to break anything
+ in practice).
+* **BREAKING**: Sealed the `MethodImplementation` trait, and made its `imp`
+ method privat.
+* **BREAKING**: Updated `ffi` module to `objc-sys v0.2.0-beta.0`. This includes:
+ * **BREAKING**: Changed `links` key from `objc` to `objc_0_2` for better
+ future compatibility, until we reach 1.0 (so `DEP_OBJC_CC_ARGS` in build
+ scripts becomes `DEP_OBJC_0_2_CC_ARGS`).
+ * **BREAKING**: Apple's runtime is now always the default.
+ * **BREAKING**: Removed type aliases `Class`, `Ivar`, `Method` and `Protocol`
+ since they could be mistaken for the `objc2::runtime` structs with the same
+ name.
+ * **BREAKING**: Removed `objc_property_t`.
+ * **BREAKING**: Removed `objc_hook_getClass` and `objc_hook_lazyClassNamer`
+ type aliases (for now).
+ * **BREAKING**: Removed `DEP_OBJC_RUNTIME` build script output.
+* **BREAKING**: Updated `objc2-encode` (`Encoding`, `Encode`, `RefEncode` and
+ `EncodeArguments`) to `v2.0.0-pre.0`.
+
+### Fixed
+* Properly sealed the `MessageArguments` trait (it already had a hidden
+ method, so this is not really a breaking change).
+
+### Removed
+* **BREAKING**: `ManuallyDrop` no longer implements `Message` directly.
+* **BREAKING**: `MessageReceiver::as_raw_receiver` is no longer public.
+
+
+## [0.3.0-alpha.6] - 2022-01-03
+[0.3.0-alpha.6]: https://github.com/madsmtm/objc2/compare/objc2-0.3.0-alpha.5...objc2-0.3.0-alpha.6
+
+### Added
+* Implement `Hash` for `Sel`, `Ivar`, `Class`, `Method` and `MessageError`.
+* Implement `PartialEq` and `Eq` for `Ivar`, `Method` and `MessageError`.
+* Implement `fmt::Pointer` for `Sel` and `rc::AutoreleasePool`.
+* Implement `fmt::Debug` for `ClassDecl`, `ProtocolDecl` and `rc::AutoreleasePool`.
+
+### Changed
+* **BREAKING**: Renamed:
+ - `Object::get_ivar` -> `Object::ivar`
+ - `Object::get_mut_ivar` -> `Object::ivar_mut`
+* Vastly improved documentation.
+* **BREAKING**: Updated `ffi` module to `objc-sys v0.2.0-alpha.1`. This includes:
+ * Added `objc_exception_try_enter` and `objc_exception_try_exit` on macOS x86.
+ * **BREAKING**: Correctly `cfg`-guarded the following types and methods to not
+ be available on macOS x86:
+ - `objc_exception_matcher`
+ - `objc_exception_preprocessor`
+ - `objc_uncaught_exception_handler`
+ - `objc_exception_handler`
+ - `objc_begin_catch`
+ - `objc_end_catch`
+ - `objc_exception_rethrow`
+ - `objc_setExceptionMatcher`
+ - `objc_setExceptionPreprocessor`
+ - `objc_setUncaughtExceptionHandler`
+ - `objc_addExceptionHandler`
+ - `objc_removeExceptionHandler`
+ * **BREAKING**: Removed`objc_set_apple_compatible_objcxx_exceptions` since it
+ is only available when `libobjc2` is compiled with the correct flags.
+ * **BREAKING**: Removed `object_setInstanceVariableWithStrongDefault` since it
+ is only available since macOS 10.12.
+ * **BREAKING**: Removed `objc_setHook_getClass` since it is only available
+ since macOS 10.14.4.
+ * **BREAKING**: Removed `objc_setHook_lazyClassNamer` since it is only
+ available since macOS 11.
+ * Fixed `docs.rs` configuration.
+* **BREAKING**: Updated `objc2-encode` (`Encoding`, `Encode`, `RefEncode` and
+ `EncodeArguments`) to `v2.0.0-beta.2`.
+
+
+## [0.3.0-alpha.5] - 2021-12-22
+[0.3.0-alpha.5]: https://github.com/madsmtm/objc2/compare/objc2-0.3.0-alpha.4...objc2-0.3.0-alpha.5
+
+### Added
+* Export `objc-sys` as `ffi` module.
+* Added common trait impls on `rc::Owned` and `rc::Shared` (useful in generic
+ contexts).
+* Implement `RefEncode` for `runtime::Protocol`.
+* Added `Message` and `MessageReceiver` implementation for `ManuallyDrop<T>`
+ (where `T` is appropriately bound). This allows patterns like:
+ ```rust
+ let obj = Id::new(msg_send![class!(MyObject), alloc]);
+ let obj = ManuallyDrop::new(obj);
+ // `init` takes ownership and possibly returns a new object.
+ let obj = Id::new(msg_send![obj, init]);
+ ```
+* New cargo feature `"malloc"`, which allows cutting down on dependencies,
+ most crates don't need the introspection features that this provides.
+
+### Changed
+* Deprecated `runtime::BOOL`, `runtime::YES` and `runtime::NO`. Use the
+ newtype `Bool` instead, or low-level `ffi::BOOL`, `ffi::YES` and `ffi::NO`.
+* **BREAKING**: The following methods now require the new `"malloc"` feature
+ flag to be enabled:
+ - `MessageReceiver::verify_message` (temporarily)
+ - `Method::return_type`
+ - `Method::argument_type`
+ - `Class::classes`
+ - `Class::instance_methods`
+ - `Class::adopted_protocols`
+ - `Class::instance_variables`
+ - `Protocol::protocols`
+ - `Protocol::adopted_protocols`
+* Relaxed `Sized` bound on `rc::Id` and `rc::WeakId` to prepare for
+ `extern type` support.
+* **BREAKING**: Relaxed `Sized` bound on `rc::SliceId` and `rc::DefaultId`.
+* **BREAKING**: Updated `objc-sys` to `v0.2.0-alpha.0`. This includes:
+ * Added `NSInteger` and `NSUInteger` (type aliases of `isize`/`usize`).
+ * Added `NSIntegerMax`, `NSIntegerMin` and `NSUIntegerMax`.
+ * **BREAKING**: Changed `cfg`-guarded `class_getImageName` to only appear on
+ Apple platforms.
+ * **BREAKING**: Opaque types are now also `!UnwindSafe`.
+* Updated `objc2-encode` (`Encoding`, `Encode`, `RefEncode` and
+ `EncodeArguments`) to `v2.0.0-beta.1`.
+
+### Removed
+* **BREAKING**: Removed the raw FFI functions from the `runtime` module. These
+ are available in the new `ffi` module instead.
+
+### Fixed
+* An issue with inlining various `rc` methods.
+* Most types (e.g. `Class` and `Method`) are now `Send`, `Sync`, `UnwindSafe`
+ and `RefUnwindSafe` again.
+ Notable exception is `Object`, because that depends on the specific
+ subclass.
+
+
+## [0.3.0-alpha.4] - 2021-11-22
+[0.3.0-alpha.4]: https://github.com/madsmtm/objc2/compare/objc2-0.3.0-alpha.3...objc2-0.3.0-alpha.4
+
+Note: To use this version, specify `objc2-encode = "=2.0.0-beta.0"` in your
+`Cargo.toml` as well.
+
+### Added
+* **BREAKING**: GNUStep users must depend on, and specify the appropriate
+ feature flag on `objc-sys` for the version they're using.
+* Moved `objc_exception` crate into `exception` module (under feature flag).
+* Added support for `_Complex` types.
+* Added `rc::SliceId`, `rc::SliceIdMut` and `rc::DefaultId` helper traits for
+ extra functionality on `rc::Id`.
+
+### Changed
+* **BREAKING**: The `exception` feature now only enables the `exception`
+ module, for general use. Use the new `catch_all` feature to wrap all message
+ sends in a `@try/@catch`.
+* **BREAKING**: Updated `objc-sys` to `v0.1.0`. This includes:
+ * **BREAKING**: Use feature flags `apple`, `gnustep-X-Y` or `winobjc` to
+ specify the runtime you're using, instead of the `RUNTIME_VERSION`
+ environment variable.
+ * **BREAKING**: `DEP_OBJC_RUNTIME` now returns `gnustep` on WinObjC.
+* **BREAKING**: Updated `objc2-encode` (`Encoding`, `Encode`, `RefEncode` and
+ `EncodeArguments`) to `v2.0.0-beta.0`.
+
+
+## [0.3.0-alpha.3] - 2021-09-05
+[0.3.0-alpha.3]: https://github.com/madsmtm/objc2/compare/objc2-0.3.0-alpha.2...objc2-0.3.0-alpha.3
+
+Note: To use this version, specify `objc2-encode = "=2.0.0-alpha.1"` in your
+`Cargo.toml` as well.
+
+### Added
+* Now uses the `objc-sys` (`v0.0.1`) crate for possibly better
+ interoperability with other crates that link to `libobjc`.
+* Added newtype `runtime::Bool` to fix soundness issues with using
+ `runtime::BOOL` or `bool`.
+* Moved `objc_id` crate into `rc` module. Notable changes:
+ - Vastly improved documentation
+ - Added `Id::autorelease`
+ - Added `Id::from_shared`
+ - Added a lot of forwarding implementations on `Id` for easier use
+ - `Id` and `WeakId` are now able to use the null-pointer optimization
+ - **BREAKING**: Added `T: Message` bounds on `Id`
+ - **BREAKING**: Remove `ShareId` type alias
+ - **BREAKING**: `Id` no longer have a default `Ownership`, you must specify
+ it everywhere as either `Id<T, Shared>` or `Id<T, Owned>`
+ - **BREAKING**: Sealed the `Ownership` trait
+ - **BREAKING**: Renamed `Id::from_ptr` to `Id::retain`
+ - **BREAKING**: Renamed `Id::from_retained_ptr` to `Id::new`
+ - **BREAKING**: Changed `Id::share` to a `From` implementation (usage of
+ `obj.share()` can be changed to `obj.into()`)
+ - **BREAKING**: Fixed soundness issues with missing `Send` and `Sync` bounds
+ on `Id` and `WeakId`
+* Added sealed (for now) trait `MessageReceiver` to specify types that can
+ be used as the receiver of a message (instead of only allowing pointer
+ types).
+* Add `MessageReceiver::send_super_message` method for dynamic selectors.
+
+### Changed
+* **BREAKING**: Change types of parameters to FFI functions exported in the
+ `runtime` module.
+* **BREAKING**: Most types are now `!UnwindSafe`, to discourage trying to use
+ them after an unwind. This restriction may be lifted in the future.
+* **BREAKING**: Most types are now `!Send` and `!Sync`. This was an oversight
+ that is fixed in a later version.
+* A lot of smaller things.
+* **BREAKING**: Dynamic message sending with `Message::send_message` is moved
+ to `MessageReceiver`.
+* **BREAKING** Make `MessageArguments` a subtrait of `EncodeArguments`.
+* Allow an optional comma after each argument to `msg_send!`.
+
+### Removed
+* **BREAKING**: Removed `rc::StrongPtr`. Use `Option<rc::Id<Object, Shared>>`
+ instead (beware: This has stronger safety invariants!).
+* **BREAKING**: Removed `rc::WeakPtr`. Use `rc::WeakId<Object>` instead.
+
+### Fixed
+* **BREAKING**: Stop unsafely dereferencing `msg_send!`s first argument. The
+ `MessageReceiver` trait was introduced to avoid most breakage from this
+ change.
+
+
+## [0.3.0-alpha.2] - 2021-09-05
+[0.3.0-alpha.2]: https://github.com/madsmtm/objc2/compare/objc2-0.3.0-alpha.1...objc2-0.3.0-alpha.2
+
+### Added
+* Added `rc::AutoreleasePool` and `rc::AutoreleaseSafe` to make accessing
+ autoreleased objects safe, by binding references to it using the
+ `ptr_as_ref` and `ptr_as_mut` methods.
+
+### Changed
+* **BREAKING**: The closure in `rc::autoreleasepool` now takes an argument
+ `&rc::AutoreleasePool`. This reference can be given to functions like
+ `INSString::as_str` so that it knows which lifetime to bound the returned
+ `&str` with.
+
+ ```rust
+ // Before
+ autoreleasepool(|| {
+ // Some code that autoreleases objects
+ });
+
+ // After
+ autoreleasepool(|_pool| {
+ // Some code that autoreleases objects
+ });
+ ```
+
+### Fixed
+* The encoding of `BOOL` on `GNUStep`.
+
+
+## [0.3.0-alpha.1] - 2021-09-02
+[0.3.0-alpha.1]: https://github.com/madsmtm/objc2/compare/objc2-0.3.0-alpha.0...objc2-0.3.0-alpha.1
+
+### Added
+* More documentation of safety requirements, and in general.
+
+### Changed
+* **BREAKING**: Change `objc-encode` dependency to `objc2-encode` version
+ `2.0.0-alpha.1`, and re-export the new `RefEncode` trait from that.
+* **BREAKING**: Require that the receiver, arguments and return types of
+ messages always implement `Encode`. This helps ensuring that only types made
+ to go across the FFI boundary (`repr(C)`, ...) may. These requirements were
+ already present when the `verify_message` feature was enabled.
+
+ This is a very _disruptive change_, since libraries are now required to
+ implement `Encode` and `RefEncode` for all types intended to go across the
+ FFI-boundary to Objective-C. The change is justified because it helps
+ ensuring that users only pass valid types to `msg_send!` (for example, this
+ prevents users from accidentally passing `Drop` types to `msg_send`).
+
+ See the following examples for how to implement these traits, and otherwise
+ refer to the documentation of `objc2-encode` (`v2.0.0-alpha.1` or above).
+ ```rust
+ use objc2::{Encode, Encoding, RefEncode};
+
+ /// Example struct.
+ #[repr(C)]
+ pub struct NSRange {
+ pub location: usize,
+ pub length: usize,
+ }
+ unsafe impl Encode for NSRange {
+ const ENCODING: Encoding<'static> = Encoding::Struct(
+ "_NSRange", // This is how the struct is defined in C header files
+ &[usize::ENCODING, usize::ENCODING]
+ );
+ }
+ unsafe impl RefEncode for NSRange {
+ const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
+ }
+
+ /// Example object.
+ #[repr(C)]
+ pub struct __CFString(c_void);
+
+ pub type CFStringRef = *const __CFString;
+
+ unsafe impl RefEncode for __CFString {
+ const ENCODING_REF: Encoding<'static> = Encoding::Object;
+ }
+ ```
+* Temporarily disabled iOS tests.
+
+### Fixed
+* Statically find the correct `objc_msgSend[_X]` function to use based on the
+ `Encode` implementation of the return type. This fixes using functions that
+ return e.g. `type CGFloat = f32 / f64;`.
+* Documentation links.
+
+
+## [0.3.0-alpha.0] - 2021-08-29
+[0.3.0-alpha.0]: https://github.com/madsmtm/objc2/compare/objc2-0.2.7...objc2-0.3.0-alpha.0
+
+Note: This is the version that is, as of this writing, available on the
+`master` branch in the original `objc` project.
+
+### Added
+* Improve macro hygiene.
+ ```rust
+ // You can now do
+ use objc2::{sel, class, msg_send};
+ // Instead of
+ #[macro_use]
+ extern crate objc2;
+ ```
+* Update to Rust 2018.
+* Other internal improvements.
+
+### Changed
+* **BREAKING**: Forked the project, so the crate name is now `objc2`.
+* **BREAKING**: Updated encoding utilities to use `objc-encode`. See that for
+ how to use the updated type `Encoding` and trait `Encode`.
+
+ In short, you will likely need to change your implementations of `Encode`
+ like this:
+ ```rust
+ use objc2::{Encode, Encoding};
+
+ pub type CGFloat = ...; // Varies based on target_pointer_width
+
+ #[repr(C)]
+ pub struct NSPoint {
+ pub x: CGFloat,
+ pub y: CGFloat,
+ }
+
+ // Before
+ unsafe impl Encode for NSPoint {
+ fn encode() -> Encoding {
+ let encoding = format!(
+ "{{CGPoint={}{}}}",
+ CGFloat::encode().as_str(),
+ CGFloat::encode().as_str(),
+ );
+ unsafe { Encoding::from_str(&encoding) }
+ }
+ }
+
+ // After
+ unsafe impl Encode for NSPoint {
+ const ENCODING: Encoding<'static> = Encoding::Struct(
+ "CGPoint",
+ &[CGFloat::ENCODING, CGFloat::ENCODING]
+ );
+ }
+ ```
+* **BREAKING**: Updated public dependency `malloc_buf` to `1.0`.
+* **BREAKING**: `Method::return_type` and `Method::argument_type` now return
+ `Malloc<str>` instead of `Encoding`.
+* **BREAKING**: `Ivar::type_encoding` now return `&str` instead of `Encoding`.
+
+### Removed
+* **BREAKING**: Removed hidden `sel_impl!` macro.
+
+
+## [0.2.7] (`objc` crate) - 2019-10-19
+[0.2.7]: https://github.com/madsmtm/objc2/compare/objc2-0.2.6...objc2-0.2.7
+
+### Fixed
+* **BREAKING**: Uses of `msg_send!` will now correctly fail to compile if no
+ return type can be inferred, instead of relying on an edge case of the
+ compiler that will soon change and silently cause undefined behavior.
+
+
+## [0.2.6] (`objc` crate) - 2019-03-25
+[0.2.6]: https://github.com/madsmtm/objc2/compare/objc2-0.2.5...objc2-0.2.6
+
+### Fixed
+* Suppressed a deprecation warning in `sel!`, `msg_send!`, and `class!`.
+
+
+## [0.2.5] (`objc` crate) - 2018-07-24
+[0.2.5]: https://github.com/madsmtm/objc2/compare/objc2-0.2.4...objc2-0.2.5
+
+### Added
+* **BREAKING**: `autoreleasepool` returns the value returned by its body
+ closure.
+
+
+## [0.2.4] (`objc` crate) - 2018-07-22
+[0.2.4]: https://github.com/madsmtm/objc2/compare/objc2-0.2.3...objc2-0.2.4
+
+### Added
+* Added an `rc` module with reference counting utilities:
+ `StrongPtr`, `WeakPtr`, and `autoreleasepool`.
+* Added some reference counting ABI foreign functions to the `runtime` module.
+
+### Fixed
+* Messaging nil under GNUstep now correctly returns zeroed results for all
+ return types.
+
+
+## [0.2.3] (`objc` crate) - 2018-07-07
+[0.2.3]: https://github.com/madsmtm/objc2/compare/objc2-0.2.2...objc2-0.2.3
+
+### Added
+* Added a `class!` macro for getting statically-known classes. The result is
+ non-optional (avoiding a need to unwrap) and cached so each usage will only
+ look up the class once.
+* Added caching to the `sel!` macro so that each usage will only register the
+ selector once.
+
+### Fixed
+* Implementation of `objc2::runtime` structs so there can't be unsound
+ references to uninhabited types.
+
+
+## [0.2.2] (`objc` crate) - 2016-10-30
+[0.2.2]: https://github.com/madsmtm/objc2/compare/objc2-0.2.1...objc2-0.2.2
+
+### Added
+* Implemented `Sync` and `Send` for `Sel`.
+
+
+## [0.2.1] (`objc` crate) - 2016-04-23
+[0.2.1]: https://github.com/madsmtm/objc2/compare/objc2-0.2.0...objc2-0.2.1
+
+### Added
+* Added support for working with protocols with the `Protocol` struct.
+ The protocols a class conforms to can be examined with the new
+ `Class::adopted_protocols` and `Class::conforms_to` methods.
+* Protocols can be declared using the new `ProtocolDecl` struct.
+
+
+## [0.2.0] (`objc` crate) - 2016-03-20
+[0.2.0]: https://github.com/madsmtm/objc2/compare/objc2-0.1.8...objc2-0.2.0
+
+### Added
+* Added verification for the types used when sending messages.
+ This can be enabled for all messages with the `"verify_message"` feature,
+ or you can test before sending specific messages with the
+ `Message::verify_message` method. Verification errors are reported using the
+ new `MessageError` struct.
+* Added support for the GNUstep runtime!
+ Operating systems besides OSX and iOS will fall back to the GNUstep runtime.
+* Root classes can be declared by using the `ClassDecl::root` constructor.
+
+### Changed
+* **BREAKING**: C types are now used from `std::os::raw` rather than `libc`.
+ This means `Encode` may not be implemented for `libc` types; switch them to
+ the `std::os::raw` equivalents instead. This avoids an issue that would
+ arise from simultaneously using different versions of the libc crate.
+* **BREAKING**: Dynamic messaging was moved into the `Message` trait; instead
+ of `().send(obj, sel!(description))`, use
+ `obj.send_message(sel!(description), ())`.
+* **BREAKING**: Rearranged the parameters to `ClassDecl::new` for consistency;
+ instead of `ClassDecl::new(superclass, "MyObject")`, use
+ `ClassDecl::new("MyObject", superclass)`.
+* **BREAKING**: Overhauled the `MethodImplementation` trait. Encodings are now
+ accessed through the `MethodImplementation::Args` associated type. The
+ `imp_for` method was replaced with `imp` and no longer takes a selector or
+ returns an `UnequalArgsError`, although `ClassDecl::add_method` still
+ validates the number of arguments.
+* **BREAKING**: Updated the definition of `Imp` to not use the old dispatch
+ prototypes. To invoke an `Imp`, it must first be transmuted to the correct
+ type.
+
+### Removed
+ **BREAKING**: `objc_msgSend` functions from the `runtime` module; the
+ availability of these functions varies and they shouldn't be called without
+ trasmuting, so they are now hidden as an implementation detail of messaging.
+
+### Fixed
+* Corrected alignment of ivars in `ClassDecl`; declared classes may now have a
+ smaller size.
+* With the `"exception"` or `"verify_message"` feature enabled, panics from
+ `msg_send!` will now be triggered from the line and file where the macro is
+ used, rather than from within the implementation of messaging.
+
+## [0.1.8] (`objc` crate) - 2015-11-06
+[0.1.8]: https://github.com/madsmtm/objc2/compare/objc2-0.1.7...objc2-0.1.8
+
+### Changed
+* Updated `libc` dependency.
+
+
+## [0.1.7] (`objc` crate) - 2015-09-23
+[0.1.7]: https://github.com/madsmtm/objc2/compare/objc2-0.1.6...objc2-0.1.7
+
+### Fixed
+* `improper_ctypes` warning.
+
+
+## [0.1.6] (`objc` crate) - 2015-08-08
+[0.1.6]: https://github.com/madsmtm/objc2/compare/objc2-0.1.5...objc2-0.1.6
+
+### Added
+* Added `"exception"` feature which catches Objective-C exceptions and turns
+ them into Rust panics.
+* Added support for `ARM`, `ARM64` and `x86` architectures.
+* **BREAKING**: Added `Any` bound on message return types. In practice this
+ probably won't break anything.
+* Start testing on iOS.
+
+
+## [0.1.5] (`objc` crate) - 2015-05-02
+[0.1.5]: https://github.com/madsmtm/objc2/compare/objc2-0.1.4...objc2-0.1.5
+
+### Changed
+* **BREAKING**: Renamed `IntoMethodImp` to `MethodImplementation`.
+* **BREAKING**: Renamed `MethodImplementation::into_imp` to `::imp_for`.
+* **BREAKING**: Relaxed `Sized` bounds on `Encode` and `Message`. In practice
+ this probably won't break anything.
+
+### Removed
+* **BREAKING**: Removed `Id`, `Owned`, `Ownership`, `Shared`, `ShareId` and
+ `WeakId`. Use them from the `objc_id` crate instead.
+* **BREAKING**: Removed `Method::set_implementation` and
+ `Method::exchange_implementation`.
+
+
+## [0.1.4] (`objc` crate) - 2015-04-17
+[0.1.4]: https://github.com/madsmtm/objc2/compare/objc2-0.1.3...objc2-0.1.4
+
+### Removed
+* **BREAKING**: Removed `block` module. Use them from the `block` crate
+ instead.
+
+
+## [0.1.3] (`objc` crate) - 2015-04-11
+[0.1.3]: https://github.com/madsmtm/objc2/compare/objc2-0.1.2...objc2-0.1.3
+
+### Added
+* Implement `fmt::Pointer` for `Id`.
+
+### Fixed
+* Odd lifetime bug.
+
+
+## [0.1.2] (`objc` crate) - 2015-04-04
+[0.1.2]: https://github.com/madsmtm/objc2/compare/objc2-0.1.1...objc2-0.1.2
+
+### Fixed
+* **BREAKING**: Replace uses of `PhantomFn` with `Sized`.
+
+
+## [0.1.1] (`objc` crate) - 2015-03-27
+[0.1.1]: https://github.com/madsmtm/objc2/compare/objc2-0.1.0...objc2-0.1.1
+
+### Added
+* Implement `Error` for `UnequalArgsError`.
+
+### Removed
+* **BREAKING**: Move `objc::foundation` into new crate `objc_foundation`.
diff --git a/third_party/rust/objc2/Cargo.lock b/third_party/rust/objc2/Cargo.lock
@@ -0,0 +1,239 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "backtrace"
+version = "0.3.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
+dependencies = [
+ "addr2line",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+ "windows-targets",
+]
+
+[[package]]
+name = "cc"
+version = "1.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "core-foundation"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "gimli"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
+
+[[package]]
+name = "iai"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71a816c97c42258aa5834d07590b718b4c9a598944cd39a52dc25b351185d678"
+
+[[package]]
+name = "libc"
+version = "0.2.171"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
+dependencies = [
+ "adler2",
+]
+
+[[package]]
+name = "objc2"
+version = "0.6.3"
+dependencies = [
+ "backtrace",
+ "core-foundation",
+ "iai",
+ "libc",
+ "memoffset",
+ "objc2-encode",
+ "objc2-exception-helper",
+ "objc2-proc-macros",
+ "static_assertions",
+]
+
+[[package]]
+name = "objc2-encode"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
+
+[[package]]
+name = "objc2-exception-helper"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "objc2-proc-macros"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c42034b8cee04579dfb1fc264762ac9cbc84b03fdd00850d2ea45b356f1297e"
+
+[[package]]
+name = "object"
+version = "0.36.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/third_party/rust/objc2/Cargo.toml b/third_party/rust/objc2/Cargo.toml
@@ -0,0 +1,231 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2021"
+rust-version = "1.71"
+name = "objc2"
+version = "0.6.3"
+authors = ["Mads Marquart <mads@marquart.dk>"]
+build = "build.rs"
+autolib = false
+autobins = false
+autoexamples = false
+autotests = false
+autobenches = false
+description = "Objective-C interface and runtime bindings"
+readme = "README.md"
+keywords = [
+ "objective-c",
+ "macos",
+ "ios",
+ "objc_msgSend",
+ "objc",
+]
+categories = [
+ "external-ffi-bindings",
+ "api-bindings",
+ "development-tools::ffi",
+ "os::macos-apis",
+]
+license = "MIT"
+repository = "https://github.com/madsmtm/objc2"
+
+[package.metadata.docs.rs]
+default-target = "aarch64-apple-darwin"
+features = ["exception"]
+targets = [
+ "aarch64-apple-darwin",
+ "x86_64-apple-darwin",
+ "aarch64-apple-ios",
+ "aarch64-apple-tvos",
+ "aarch64-apple-watchos",
+ "aarch64-apple-ios-macabi",
+ "aarch64-apple-visionos",
+ "x86_64-unknown-linux-gnu",
+ "i686-unknown-linux-gnu",
+]
+
+[package.metadata.release]
+shared-version = false
+tag-prefix = "objc2"
+
+[features]
+alloc = ["objc2-encode/alloc"]
+catch-all = ["exception"]
+default = ["std"]
+disable-encoding-assertions = []
+exception = ["dep:objc2-exception-helper"]
+gnustep-1-7 = [
+ "unstable-static-class",
+ "objc2-exception-helper?/gnustep-1-7",
+]
+gnustep-1-8 = [
+ "gnustep-1-7",
+ "objc2-exception-helper?/gnustep-1-8",
+]
+gnustep-1-9 = [
+ "gnustep-1-8",
+ "objc2-exception-helper?/gnustep-1-9",
+]
+gnustep-2-0 = [
+ "gnustep-1-9",
+ "objc2-exception-helper?/gnustep-2-0",
+]
+gnustep-2-1 = [
+ "gnustep-2-0",
+ "objc2-exception-helper?/gnustep-2-1",
+]
+objc2-proc-macros = []
+relax-sign-encoding = []
+relax-void-encoding = []
+std = [
+ "alloc",
+ "objc2-encode/std",
+]
+unstable-apple-new = []
+unstable-arbitrary-self-types = []
+unstable-autoreleasesafe = []
+unstable-coerce-pointee = []
+unstable-compiler-rt = ["gnustep-1-7"]
+unstable-gnustep-strict-apple-compat = ["gnustep-1-7"]
+unstable-objfw = []
+unstable-requires-macos = []
+unstable-static-class = ["dep:objc2-proc-macros"]
+unstable-static-class-inlined = ["unstable-static-class"]
+unstable-static-sel = ["dep:objc2-proc-macros"]
+unstable-static-sel-inlined = ["unstable-static-sel"]
+unstable-winobjc = ["gnustep-1-8"]
+verify = []
+
+[lib]
+name = "objc2"
+path = "src/lib.rs"
+
+[[example]]
+name = "class_with_lifetime"
+path = "examples/class_with_lifetime.rs"
+
+[[example]]
+name = "encode_core_graphics"
+path = "examples/encode_core_graphics.rs"
+
+[[example]]
+name = "encode_nsstring"
+path = "examples/encode_nsstring.rs"
+
+[[example]]
+name = "encode_nsuinteger"
+path = "examples/encode_nsuinteger.rs"
+
+[[example]]
+name = "encode_opaque_type"
+path = "examples/encode_opaque_type.rs"
+
+[[example]]
+name = "hello_world_app"
+path = "examples/hello_world_app.rs"
+required-features = ["unstable-requires-macos"]
+
+[[example]]
+name = "introspection"
+path = "examples/introspection.rs"
+
+[[test]]
+name = "backtrace"
+path = "tests/backtrace.rs"
+
+[[test]]
+name = "define_class"
+path = "tests/define_class.rs"
+
+[[test]]
+name = "define_class_self"
+path = "tests/define_class_self.rs"
+
+[[test]]
+name = "extern_class"
+path = "tests/extern_class.rs"
+
+[[test]]
+name = "macros_mainthreadmarker"
+path = "tests/macros_mainthreadmarker.rs"
+
+[[test]]
+name = "no_prelude"
+path = "tests/no_prelude.rs"
+
+[[test]]
+name = "protocol_shared_retain_count"
+path = "tests/protocol_shared_retain_count.rs"
+
+[[test]]
+name = "track_caller"
+path = "tests/track_caller.rs"
+
+[[test]]
+name = "use_macros"
+path = "tests/use_macros.rs"
+
+[[bench]]
+name = "autorelease"
+path = "benches/autorelease.rs"
+harness = false
+
+[dependencies.objc2-encode]
+version = "4.1.0"
+default-features = false
+
+[dependencies.objc2-exception-helper]
+version = "0.1.1"
+optional = true
+default-features = false
+
+[dependencies.objc2-proc-macros]
+version = "0.2.0"
+optional = true
+
+[dev-dependencies.backtrace]
+version = "0.3.74"
+
+[dev-dependencies.iai]
+version = "0.1"
+
+[dev-dependencies.libc]
+version = "0.2.158"
+
+[dev-dependencies.memoffset]
+version = "0.9.0"
+
+[dev-dependencies.static_assertions]
+version = "1.1.0"
+
+[target.'cfg(all(target_vendor = "apple", not(target_os = "macos")))'.dev-dependencies]
+
+[target.'cfg(target_os = "macos")'.dev-dependencies]
+
+[target.'cfg(target_vendor = "apple")'.dev-dependencies.core-foundation]
+version = "0.10.0"
+
+[lints.clippy]
+ptr_as_ptr = "warn"
+redundant_feature_names = "allow"
+
+[lints.clippy.cargo]
+level = "warn"
+priority = -1
+
+[lints.rust]
+elided_lifetimes_in_paths = "warn"
+missing_copy_implementations = "warn"
+non_ascii_idents = "deny"
+unreachable_pub = "warn"
+unsafe_op_in_unsafe_fn = "deny"
diff --git a/third_party/rust/objc2/README.md b/third_party/rust/objc2/README.md
@@ -0,0 +1,17 @@
+# `objc2`
+
+[](https://crates.io/crates/objc2)
+[](../../LICENSE.md)
+[](https://docs.rs/objc2/)
+[](https://github.com/madsmtm/objc2/actions/workflows/ci.yml)
+
+Objective-C interface and runtime bindings in Rust.
+
+Most of the core libraries and frameworks that are in use on Apple systems are
+written in Objective-C; this crate enables you to interact with those.
+
+This README is kept intentionally small in an effort to consolidate the
+documentation, see [the Rust docs](https://docs.rs/objc2/) for more details.
+
+This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2),
+see that for related crates.
diff --git a/third_party/rust/objc2/benches/autorelease.rs b/third_party/rust/objc2/benches/autorelease.rs
@@ -0,0 +1,160 @@
+use core::ffi::c_void;
+use core::mem::ManuallyDrop;
+
+use objc2::rc::{autoreleasepool, Retained};
+use objc2::runtime::{AnyClass, NSObject, Sel};
+use objc2::{class, msg_send, sel};
+
+const BYTES: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+
+fn empty() {}
+
+fn pool_cleanup() {
+ autoreleasepool(|_| {});
+}
+
+fn class() -> &'static AnyClass {
+ class!(NSObject)
+}
+
+fn sel() -> Sel {
+ sel!(alloc)
+}
+
+fn send_message() -> &'static AnyClass {
+ unsafe { msg_send![class!(NSObject), class] }
+}
+
+fn alloc_nsobject() -> *mut NSObject {
+ unsafe { msg_send![class!(NSObject), alloc] }
+}
+
+fn new_nsobject() -> Retained<NSObject> {
+ let obj = alloc_nsobject();
+ let obj: *mut NSObject = unsafe { msg_send![obj, init] };
+ unsafe { Retained::from_raw(obj).unwrap_unchecked() }
+}
+
+fn new_nsdata() -> Retained<NSObject> {
+ let bytes_ptr: *const c_void = BYTES.as_ptr().cast();
+ let obj: *mut NSObject = unsafe { msg_send![class!(NSData), alloc] };
+ let obj: *mut NSObject = unsafe {
+ msg_send![
+ obj,
+ initWithBytes: bytes_ptr,
+ length: BYTES.len(),
+ ]
+ };
+ unsafe { Retained::from_raw(obj).unwrap_unchecked() }
+}
+
+fn new_leaked_nsdata() -> *const NSObject {
+ Retained::as_ptr(&*ManuallyDrop::new(new_nsdata()))
+}
+
+fn autoreleased_nsdata() -> *const NSObject {
+ // let bytes_ptr: *const c_void = BYTES.as_ptr().cast();
+ // unsafe {
+ // msg_send![
+ // class!(NSData),
+ // dataWithBytes: bytes_ptr,
+ // length: BYTES.len(),
+ // ]
+ // }
+ unsafe { msg_send![new_leaked_nsdata(), autorelease] }
+}
+
+fn new_nsstring() -> Retained<NSObject> {
+ let obj: *mut NSObject = unsafe { msg_send![class!(NSString), alloc] };
+ let obj: *mut NSObject = unsafe { msg_send![obj, init] };
+ unsafe { Retained::from_raw(obj).unwrap_unchecked() }
+}
+
+fn new_leaked_nsstring() -> *const NSObject {
+ Retained::as_ptr(&*ManuallyDrop::new(new_nsstring()))
+}
+
+fn autoreleased_nsstring() -> *const NSObject {
+ // unsafe { msg_send![class!(NSString), string] }
+ unsafe { msg_send![new_leaked_nsstring(), autorelease] }
+}
+
+fn retain_autoreleased(obj: *const NSObject) -> Retained<NSObject> {
+ unsafe { Retained::retain_autoreleased((obj as *mut NSObject).cast()).unwrap_unchecked() }
+}
+
+fn autoreleased_nsdata_pool_cleanup() -> *const NSObject {
+ autoreleasepool(|_| autoreleased_nsdata())
+}
+
+fn autoreleased_nsdata_fast_caller_cleanup() -> Retained<NSObject> {
+ retain_autoreleased(autoreleased_nsdata())
+}
+
+fn autoreleased_nsdata_fast_caller_cleanup_pool_cleanup() -> Retained<NSObject> {
+ autoreleasepool(|_| retain_autoreleased(autoreleased_nsdata()))
+}
+
+fn autoreleased_nsstring_pool_cleanup() -> *const NSObject {
+ autoreleasepool(|_| autoreleased_nsstring())
+}
+
+fn autoreleased_nsstring_fast_caller_cleanup() -> Retained<NSObject> {
+ retain_autoreleased(autoreleased_nsstring())
+}
+
+fn autoreleased_nsstring_fast_caller_cleanup_pool_cleanup() -> Retained<NSObject> {
+ autoreleasepool(|_| retain_autoreleased(autoreleased_nsstring()))
+}
+
+macro_rules! main_with_warmup {
+ ($($f:ident,)+) => {
+ mod warmup_fns {
+ $(
+ #[inline(never)]
+ pub(crate) fn $f() {
+ let _ = iai::black_box(super::$f());
+ }
+ )+
+ }
+
+ // Required to get DYLD to resolve the stubs on x86_64
+ fn warmup() {
+ $(
+ warmup_fns::$f();
+ )+
+ }
+
+ iai::main! {
+ warmup,
+ $(
+ $f,
+ )+
+ }
+ };
+}
+
+main_with_warmup! {
+ // Baseline
+ empty,
+ pool_cleanup,
+ class,
+ sel,
+ send_message,
+ alloc_nsobject,
+ new_nsobject,
+ // NSData
+ new_nsdata,
+ new_leaked_nsdata,
+ autoreleased_nsdata,
+ autoreleased_nsdata_pool_cleanup,
+ autoreleased_nsdata_fast_caller_cleanup,
+ autoreleased_nsdata_fast_caller_cleanup_pool_cleanup,
+ // NSString
+ new_nsstring,
+ new_leaked_nsstring,
+ autoreleased_nsstring,
+ autoreleased_nsstring_pool_cleanup,
+ autoreleased_nsstring_fast_caller_cleanup,
+ autoreleased_nsstring_fast_caller_cleanup_pool_cleanup,
+}
diff --git a/third_party/rust/objc2/build.rs b/third_party/rust/objc2/build.rs
@@ -0,0 +1,39 @@
+//! Helper script to work around MSRV being too low for `target_abi`.
+use std::env;
+
+fn main() {
+ // The script doesn't depend on our code
+ println!("cargo:rerun-if-changed=build.rs");
+
+ let target = env::var("TARGET").unwrap();
+ let target_abi = env::var("CARGO_CFG_TARGET_ABI");
+
+ // Used to figure out when BOOL should be i8 vs. bool
+ // Matches:
+ // aarch64-apple-ios-macabi
+ // x86_64-apple-ios-macabi
+ println!("cargo:rustc-check-cfg=cfg(target_abi_macabi)");
+ if target_abi.as_deref() == Ok("macabi")
+ // Legacy check for Rust versions < 1.78 (still within MSRV)
+ || target.ends_with("macabi")
+ {
+ println!("cargo:rustc-cfg=target_abi_macabi");
+ }
+
+ // Used to set correct image info
+ // Matches:
+ // aarch64-apple-ios-sim
+ // aarch64-apple-watchos-sim
+ // x86_64-apple-watchos-sim
+ // i386-apple-ios
+ // x86_64-apple-ios
+ println!("cargo:rustc-check-cfg=cfg(target_simulator)");
+ if target_abi.as_deref() == Ok("sim")
+ // Legacy check for Rust versions < 1.78 (still within MSRV)
+ || target.ends_with("sim")
+ || target == "i386-apple-ios"
+ || target == "x86_64-apple-ios"
+ {
+ println!("cargo:rustc-cfg=target_simulator");
+ }
+}
diff --git a/third_party/rust/objc2/examples/class_with_lifetime.rs b/third_party/rust/objc2/examples/class_with_lifetime.rs
@@ -0,0 +1,106 @@
+//! Note: We can't use the `define_class!` macro for this, it doesn't support
+//! such use-cases (yet). Instead, we'll create it manually.
+#![deny(unsafe_op_in_unsafe_fn)]
+use std::cell::Cell;
+use std::marker::PhantomData;
+use std::sync::Once;
+
+use objc2::rc::Retained;
+use objc2::runtime::{AnyClass, AnyObject, ClassBuilder, NSObject};
+use objc2::{msg_send, ClassType, Encoding, Message, RefEncode};
+
+/// The type of the instance variable that we want to store
+type Ivar<'a> = &'a Cell<u8>;
+
+/// Struct that represents our custom object.
+#[repr(C)]
+struct MyObject<'a> {
+ /// Required to give MyObject the proper layout
+ ///
+ /// Beware: `retain`-ing this field directly is dangerous, since it'd make
+ /// it possible to extend the lifetime of the object beyond lifetime `'a`.
+ inner: AnyObject,
+ /// For auto traits and variance
+ p: PhantomData<Ivar<'a>>,
+}
+
+unsafe impl RefEncode for MyObject<'_> {
+ const ENCODING_REF: Encoding = NSObject::ENCODING_REF;
+}
+
+unsafe impl Message for MyObject<'_> {}
+
+impl<'a> MyObject<'a> {
+ fn class() -> &'static AnyClass {
+ // NOTE: Use std::lazy::LazyCell if in MSRV
+ static REGISTER_CLASS: Once = Once::new();
+
+ REGISTER_CLASS.call_once(|| {
+ let superclass = NSObject::class();
+ let mut builder = ClassBuilder::new(c"MyObject", superclass).unwrap();
+
+ builder.add_ivar::<Ivar<'_>>(c"number");
+
+ let _cls = builder.register();
+ });
+ AnyClass::get(c"MyObject").unwrap()
+ }
+
+ fn new(number: &'a mut u8) -> Retained<Self> {
+ // SAFETY: The instance variable is initialized below.
+ let this: Retained<Self> = unsafe { msg_send![Self::class(), new] };
+
+ // It is generally very hard to use `mut` in Objective-C, so let's use
+ // interior mutability instead.
+ let number = Cell::from_mut(number);
+
+ let ivar = Self::class().instance_variable(c"number").unwrap();
+ // SAFETY: The ivar is added with the same type below, and the
+ // lifetime of the reference is properly bound to the class.
+ unsafe { ivar.load_ptr::<Ivar<'_>>(&this.inner).write(number) };
+ this
+ }
+
+ fn get(&self) -> u8 {
+ let ivar = Self::class().instance_variable(c"number").unwrap();
+ // SAFETY: The ivar is added with the same type below, and is initialized in `new`
+ unsafe { ivar.load::<Ivar<'_>>(&self.inner).get() }
+ }
+
+ fn set(&self, number: u8) {
+ let ivar = Self::class().instance_variable(c"number").unwrap();
+ // SAFETY: The ivar is added with the same type below, and is initialized in `new`
+ unsafe { ivar.load::<Ivar<'_>>(&self.inner).set(number) };
+ }
+}
+
+fn main() {
+ let mut number = 54;
+
+ let obj = MyObject::new(&mut number);
+ assert_eq!(obj.get(), 54);
+
+ // We can now mutate the referenced `number`
+ obj.set(7);
+ assert_eq!(obj.get(), 7);
+
+ // And we can clone the object, since we use interior mutability.
+ let clone = obj.clone();
+ clone.set(42);
+ assert_eq!(obj.get(), 42);
+ drop(clone);
+
+ // It is not possible to convert to `Retained<NSObject>`, since that would
+ // loose the lifetime information that `MyObject` stores.
+ //
+ // let obj = obj.into_super();
+ //
+ // Neither is it not possible to access `number` any more, since `obj`
+ // holds a mutable reference to it.
+ //
+ // assert_eq!(number, 42);
+
+ drop(obj);
+ // And now that we've dropped `obj`, we can access `number` again
+ assert_eq!(number, 42);
+}
diff --git a/third_party/rust/objc2/examples/encode_core_graphics.rs b/third_party/rust/objc2/examples/encode_core_graphics.rs
@@ -0,0 +1,49 @@
+use objc2::encode::{Encode, Encoding};
+
+#[cfg(target_pointer_width = "32")]
+type CGFloat = f32;
+#[cfg(target_pointer_width = "64")]
+type CGFloat = f64;
+
+#[repr(C)]
+struct CGPoint {
+ x: CGFloat,
+ y: CGFloat,
+}
+
+// SAFETY: The struct is `repr(C)`, and the encoding is correct.
+unsafe impl Encode for CGPoint {
+ const ENCODING: Encoding = Encoding::Struct("CGPoint", &[CGFloat::ENCODING, CGFloat::ENCODING]);
+}
+
+#[repr(C)]
+struct CGSize {
+ width: CGFloat,
+ height: CGFloat,
+}
+
+// SAFETY: The struct is `repr(C)`, and the encoding is correct.
+unsafe impl Encode for CGSize {
+ const ENCODING: Encoding = Encoding::Struct("CGSize", &[CGFloat::ENCODING, CGFloat::ENCODING]);
+}
+
+#[repr(C)]
+struct CGRect {
+ origin: CGPoint,
+ size: CGSize,
+}
+
+// SAFETY: The struct is `repr(C)`, and the encoding is correct.
+unsafe impl Encode for CGRect {
+ const ENCODING: Encoding = Encoding::Struct("CGRect", &[CGPoint::ENCODING, CGSize::ENCODING]);
+}
+
+fn main() {
+ let expected = if cfg!(target_pointer_width = "64") {
+ "{CGRect={CGPoint=dd}{CGSize=dd}}"
+ } else {
+ "{CGRect={CGPoint=ff}{CGSize=ff}}"
+ };
+
+ assert!(CGRect::ENCODING.equivalent_to_str(expected));
+}
diff --git a/third_party/rust/objc2/examples/encode_nsstring.rs b/third_party/rust/objc2/examples/encode_nsstring.rs
@@ -0,0 +1,26 @@
+use objc2::encode::{Encode, Encoding, RefEncode};
+use objc2::runtime::AnyObject;
+
+#[repr(transparent)]
+struct NSString {
+ // `NSString` has the same layout / works the same as `AnyObject`.
+ _priv: AnyObject,
+}
+
+// We don't know the size of NSString, so we can only hold pointers to it.
+//
+// SAFETY: The string is `repr(transparent)` over `AnyObject`.
+unsafe impl RefEncode for NSString {
+ const ENCODING_REF: Encoding = Encoding::Object;
+}
+
+fn main() {
+ // The `RefEncode` implementation provide an `Encode` implementation for
+ // pointers to the object.
+ assert_eq!(<*const NSString>::ENCODING, Encoding::Object);
+ assert_eq!(<*mut NSString>::ENCODING, Encoding::Object);
+ assert_eq!(<&NSString>::ENCODING, Encoding::Object);
+ assert_eq!(<&mut NSString>::ENCODING, Encoding::Object);
+ assert_eq!(<Option<&NSString>>::ENCODING, Encoding::Object);
+ assert_eq!(<Option<&mut NSString>>::ENCODING, Encoding::Object);
+}
diff --git a/third_party/rust/objc2/examples/encode_nsuinteger.rs b/third_party/rust/objc2/examples/encode_nsuinteger.rs
@@ -0,0 +1,30 @@
+use objc2::encode::{Encode, Encoding, RefEncode};
+
+// Note: In this case `NSUInteger` could be a type alias for `usize`, and
+// actually that's already available as `objc2::ffi::NSUInteger`.
+#[repr(transparent)]
+struct NSUInteger {
+ _inner: usize,
+}
+
+// SAFETY: `NSUInteger` has the same `repr` as `usize`.
+unsafe impl Encode for NSUInteger {
+ // Running `@encode(NSUInteger)` gives `Q` on 64-bit systems and `I` on
+ // 32-bit systems. This corresponds exactly to `usize`, which is also how
+ // we've defined our struct.
+ const ENCODING: Encoding = usize::ENCODING;
+}
+
+// SAFETY: `&NSUInteger` has the same representation as `&usize`.
+unsafe impl RefEncode for NSUInteger {
+ // Running `@encode(NSUInteger*)` gives `^Q` on 64-bit systems and `^I` on
+ // 32-bit systems. So implementing `RefEncode` as a plain pointer is
+ // correct.
+ const ENCODING_REF: Encoding = Encoding::Pointer(&NSUInteger::ENCODING);
+}
+
+fn main() {
+ assert!(NSUInteger::ENCODING.equivalent_to_str("Q"));
+ assert!(<&NSUInteger>::ENCODING.equivalent_to_str("^Q"));
+ assert!(<&NSUInteger>::ENCODING.equivalent_to_str("r^Q"));
+}
diff --git a/third_party/rust/objc2/examples/encode_opaque_type.rs b/third_party/rust/objc2/examples/encode_opaque_type.rs
@@ -0,0 +1,28 @@
+use objc2::encode::{Encoding, RefEncode};
+
+// We choose in this case to represent `NSDecimal` as an opaque struct because
+// we don't know much about the internals.
+//
+// Therefore we do not implement `Encode`.
+#[repr(C)]
+struct NSDecimal {
+ // Note: This should be an [extern type][rfc-1861] instead, when that
+ // becomes possible, for now we use this as a workaround.
+ //
+ // [rfc-1861]: https://rust-lang.github.io/rfcs/1861-extern-types.html
+ _priv: [u8; 0],
+}
+
+// SAFETY: `&NSDecimal` is a valid pointer, and the encoding is correct.
+unsafe impl RefEncode for NSDecimal {
+ const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Struct("?", &[]));
+}
+
+fn main() {
+ // Running `@encode` on `NSDecimal*` on my 64-bit system gives
+ // `^{?=cCCC[38C]}`, but empty structs are treated as equivalent to all
+ // other structs by `objc2`.
+ assert!(NSDecimal::ENCODING_REF.equivalent_to_str("^{?=cCCC[38C]}"));
+ // Does not compile:
+ // println!("{:?}", NSDecimal::ENCODING);
+}
diff --git a/third_party/rust/objc2/examples/hello_world_app.rs b/third_party/rust/objc2/examples/hello_world_app.rs
@@ -0,0 +1,131 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+use std::cell::OnceCell;
+
+use objc2::rc::Retained;
+use objc2::runtime::ProtocolObject;
+use objc2::{define_class, msg_send, DefinedClass, MainThreadOnly};
+use objc2_app_kit::{
+ NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate, NSAutoresizingMaskOptions,
+ NSBackingStoreType, NSColor, NSFont, NSTextAlignment, NSTextField, NSWindow, NSWindowDelegate,
+ NSWindowStyleMask,
+};
+use objc2_foundation::{
+ ns_string, MainThreadMarker, NSNotification, NSObject, NSObjectProtocol, NSPoint, NSRect,
+ NSSize,
+};
+
+#[derive(Debug, Default)]
+struct AppDelegateIvars {
+ window: OnceCell<Retained<NSWindow>>,
+}
+
+define_class!(
+ // SAFETY:
+ // - The superclass NSObject does not have any subclassing requirements.
+ // - `Delegate` does not implement `Drop`.
+ #[unsafe(super = NSObject)]
+ #[thread_kind = MainThreadOnly]
+ #[ivars = AppDelegateIvars]
+ struct Delegate;
+
+ // SAFETY: `NSObjectProtocol` has no safety requirements.
+ unsafe impl NSObjectProtocol for Delegate {}
+
+ // SAFETY: `NSApplicationDelegate` has no safety requirements.
+ unsafe impl NSApplicationDelegate for Delegate {
+ // SAFETY: The signature is correct.
+ #[unsafe(method(applicationDidFinishLaunching:))]
+ fn did_finish_launching(&self, notification: &NSNotification) {
+ let mtm = self.mtm();
+
+ let app = unsafe { notification.object() }
+ .unwrap()
+ .downcast::<NSApplication>()
+ .unwrap();
+
+ let text_field = unsafe {
+ let text_field = NSTextField::labelWithString(ns_string!("Hello, World!"), mtm);
+ text_field.setFrame(NSRect::new(
+ NSPoint::new(5.0, 100.0),
+ NSSize::new(290.0, 100.0),
+ ));
+ text_field.setTextColor(Some(&NSColor::colorWithSRGBRed_green_blue_alpha(
+ 0.0, 0.5, 0.0, 1.0,
+ )));
+ text_field.setAlignment(NSTextAlignment::Center);
+ text_field.setFont(Some(&NSFont::systemFontOfSize(45.0)));
+ text_field.setAutoresizingMask(
+ NSAutoresizingMaskOptions::ViewWidthSizable
+ | NSAutoresizingMaskOptions::ViewHeightSizable,
+ );
+ text_field
+ };
+
+ // SAFETY: We disable releasing when closed below.
+ let window = unsafe {
+ NSWindow::initWithContentRect_styleMask_backing_defer(
+ NSWindow::alloc(mtm),
+ NSRect::new(NSPoint::new(0.0, 0.0), NSSize::new(300.0, 300.0)),
+ NSWindowStyleMask::Titled
+ | NSWindowStyleMask::Closable
+ | NSWindowStyleMask::Miniaturizable
+ | NSWindowStyleMask::Resizable,
+ NSBackingStoreType::Buffered,
+ false,
+ )
+ };
+ // SAFETY: Disable auto-release when closing windows.
+ // This is required when creating `NSWindow` outside a window
+ // controller.
+ unsafe { window.setReleasedWhenClosed(false) };
+
+ // Set various window properties.
+ window.setTitle(ns_string!("A window"));
+ let view = window.contentView().expect("window must have content view");
+ unsafe { view.addSubview(&text_field) };
+ window.center();
+ unsafe { window.setContentMinSize(NSSize::new(300.0, 300.0)) };
+ window.setDelegate(Some(ProtocolObject::from_ref(self)));
+
+ // Show the window.
+ window.makeKeyAndOrderFront(None);
+
+ // Store the window in the delegate.
+ self.ivars().window.set(window).unwrap();
+
+ app.setActivationPolicy(NSApplicationActivationPolicy::Regular);
+
+ // Activate the application.
+ // Required when launching unbundled (as is done with Cargo).
+ #[allow(deprecated)]
+ app.activateIgnoringOtherApps(true);
+ }
+ }
+
+ // SAFETY: `NSWindowDelegate` has no safety requirements.
+ unsafe impl NSWindowDelegate for Delegate {
+ #[unsafe(method(windowWillClose:))]
+ fn window_will_close(&self, _notification: &NSNotification) {
+ // Quit the application when the window is closed.
+ unsafe { NSApplication::sharedApplication(self.mtm()).terminate(None) };
+ }
+ }
+);
+
+impl Delegate {
+ fn new(mtm: MainThreadMarker) -> Retained<Self> {
+ let this = Self::alloc(mtm).set_ivars(AppDelegateIvars::default());
+ // SAFETY: The signature of `NSObject`'s `init` method is correct.
+ unsafe { msg_send![super(this), init] }
+ }
+}
+
+fn main() {
+ let mtm = MainThreadMarker::new().unwrap();
+
+ let app = NSApplication::sharedApplication(mtm);
+ let delegate = Delegate::new(mtm);
+ app.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
+
+ app.run();
+}
diff --git a/third_party/rust/objc2/examples/introspection.rs b/third_party/rust/objc2/examples/introspection.rs
@@ -0,0 +1,52 @@
+use objc2::runtime::{AnyClass, NSObject};
+use objc2::{sel, ClassType, Encode};
+
+fn main() {
+ // Get the class representing `NSObject`
+ let cls = NSObject::class();
+
+ // Inspect various properties of the class
+ println!("NSObject superclass: {:?}", cls.superclass());
+ println!("NSObject size: {}", cls.instance_size());
+ println!(
+ "-[NSObject alloc] would work: {}",
+ cls.responds_to(sel!(alloc))
+ );
+ println!(
+ "+[NSObject alloc] would work: {}",
+ cls.metaclass().responds_to(sel!(alloc))
+ );
+
+ // Inspect an instance variable on the class
+ //
+ // Note: You should not rely on the `isa` ivar being available,
+ // this is only for demonstration.
+ let ivar = cls
+ .instance_variable(c"isa")
+ .expect("no ivar with name 'isa' found on NSObject");
+ println!(
+ "Instance variable {:?} has type encoding {:?}",
+ ivar.name(),
+ ivar.type_encoding()
+ );
+ assert!(<*const AnyClass>::ENCODING.equivalent_to_str(ivar.type_encoding().to_str().unwrap()));
+
+ // Inspect a method of the class
+ let method = cls.instance_method(sel!(hash)).unwrap();
+ println!(
+ "-[NSObject hash] takes {} parameters",
+ method.arguments_count()
+ );
+ let hash_return = method.return_type();
+ println!("-[NSObject hash] return type: {hash_return:?}");
+ assert!(usize::ENCODING.equivalent_to_str(hash_return.to_str().unwrap()));
+
+ // Create an instance
+ let obj = NSObject::new();
+
+ println!("NSObject address: {obj:p}");
+
+ // Read an ivar on the object
+ let isa: *const AnyClass = unsafe { *ivar.load(&obj) };
+ println!("NSObject isa: {isa:?}");
+}
diff --git a/third_party/rust/objc2/src/__framework_prelude.rs b/third_party/rust/objc2/src/__framework_prelude.rs
@@ -0,0 +1,15 @@
+//! Helper import prelude for framework crates.
+
+// Note: While this is not public, it is still a breaking change to remove
+// entries in here, since framework crates rely on it.
+
+pub use crate::encode::{Encode, Encoding, RefEncode};
+pub use crate::ffi::{NSInteger, NSIntegerMax, NSUInteger, NSUIntegerMax};
+pub use crate::rc::{Allocated, DefaultRetained, Retained};
+pub use crate::runtime::{
+ AnyClass, AnyObject, AnyProtocol, Bool, Imp, NSObject, NSObjectProtocol, ProtocolObject, Sel,
+};
+pub use crate::{
+ cf_objc2_type, extern_class, extern_conformance, extern_methods, extern_protocol, ClassType,
+ MainThreadMarker, MainThreadOnly, Message, ProtocolType,
+};
diff --git a/third_party/rust/objc2/src/__macro_helpers/cache.rs b/third_party/rust/objc2/src/__macro_helpers/cache.rs
@@ -0,0 +1,112 @@
+use core::ffi::{c_char, c_void, CStr};
+use core::ptr;
+use core::str;
+use core::sync::atomic::{AtomicPtr, Ordering};
+
+use crate::ffi;
+use crate::runtime::{AnyClass, Sel};
+
+/// Allows storing a [`Sel`] in a static and lazily loading it.
+#[derive(Debug)]
+pub struct CachedSel {
+ ptr: AtomicPtr<c_void>,
+}
+
+impl CachedSel {
+ /// Constructs a new [`CachedSel`].
+ #[allow(clippy::new_without_default)]
+ pub const fn new() -> Self {
+ Self {
+ ptr: AtomicPtr::new(ptr::null_mut()),
+ }
+ }
+
+ // Mark as cold since this should only ever be called once (or maybe twice
+ // if running on multiple threads).
+ #[cold]
+ unsafe fn fetch(&self, name: *const c_char) -> Sel {
+ // The panic inside `Sel::register_unchecked` is unfortunate, but
+ // strict correctness is more important than speed
+
+ // SAFETY: Input is a non-null, NUL-terminated C-string pointer.
+ //
+ // We know this, because we construct it in `sel!` ourselves
+ let sel = unsafe { Sel::register_unchecked(name) };
+ self.ptr.store(sel.as_ptr() as *mut _, Ordering::Relaxed);
+ sel
+ }
+
+ /// Returns the cached selector. If no selector is yet cached, registers
+ /// one with the given name and stores it.
+ #[inline]
+ pub unsafe fn get(&self, name: &str) -> Sel {
+ // `Relaxed` should be fine since `sel_registerName` is thread-safe.
+ let ptr = self.ptr.load(Ordering::Relaxed);
+ if let Some(sel) = unsafe { Sel::from_ptr(ptr) } {
+ sel
+ } else {
+ // SAFETY: Checked by caller
+ unsafe { self.fetch(name.as_ptr().cast()) }
+ }
+ }
+}
+
+/// Allows storing a [`AnyClass`] reference in a static and lazily loading it.
+#[derive(Debug)]
+pub struct CachedClass {
+ ptr: AtomicPtr<AnyClass>,
+}
+
+impl CachedClass {
+ /// Constructs a new [`CachedClass`].
+ #[allow(clippy::new_without_default)]
+ pub const fn new() -> CachedClass {
+ CachedClass {
+ ptr: AtomicPtr::new(ptr::null_mut()),
+ }
+ }
+
+ // Mark as cold since this should only ever be called once (or maybe twice
+ // if running on multiple threads).
+ #[cold]
+ #[track_caller]
+ unsafe fn fetch(&self, name: *const c_char) -> &'static AnyClass {
+ let ptr: *const AnyClass = unsafe { ffi::objc_getClass(name) }.cast();
+ self.ptr.store(ptr as *mut AnyClass, Ordering::Relaxed);
+ if let Some(cls) = unsafe { ptr.as_ref() } {
+ cls
+ } else {
+ // Recover the name from the pointer. We do it like this so that
+ // we don't have to pass the length of the class to this method,
+ // improving binary size.
+ let name = unsafe { CStr::from_ptr(name) };
+ let name = str::from_utf8(name.to_bytes()).unwrap();
+ panic!("class {name} could not be found")
+ }
+ }
+
+ /// Returns the cached class. If no class is yet cached, gets one with
+ /// the given name and stores it.
+ #[inline]
+ #[track_caller]
+ pub unsafe fn get(&self, name: &str) -> &'static AnyClass {
+ // `Relaxed` should be fine since `objc_getClass` is thread-safe.
+ let ptr = self.ptr.load(Ordering::Relaxed);
+ if let Some(cls) = unsafe { ptr.as_ref() } {
+ cls
+ } else {
+ // SAFETY: Checked by caller
+ unsafe { self.fetch(name.as_ptr().cast()) }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ #[should_panic = "class NonExistentClass could not be found"]
+ #[cfg(not(feature = "unstable-static-class"))]
+ fn test_not_found() {
+ let _ = crate::class!(NonExistentClass);
+ }
+}
diff --git a/third_party/rust/objc2/src/__macro_helpers/class.rs b/third_party/rust/objc2/src/__macro_helpers/class.rs
@@ -0,0 +1,112 @@
+use crate::{AnyThread, ClassType, MainThreadOnly, ThreadKind};
+
+/// Helper for ensuring that `ClassType::ThreadKind`, if specified, is set
+/// correctly.
+pub trait ValidThreadKind<Requested: ?Sized + ThreadKind>
+where
+ Self: ClassType<ThreadKind = Requested>,
+ // Ensure the user did not attempt to declare or define a root class.
+ Self::Super: ClassType,
+{
+ // Required to reference the trait.
+ fn check() {}
+}
+
+/// Always allow setting `MainThreadOnly`.
+impl<'a, Cls> ValidThreadKind<dyn MainThreadOnly + 'a> for Cls
+where
+ Self: ClassType<ThreadKind = dyn MainThreadOnly + 'a>,
+ Self::Super: ClassType,
+{
+}
+
+/// But restrict `AnyThread` to only if the superclass also sets it.
+impl<'a, 'b, Cls> ValidThreadKind<dyn AnyThread + 'a> for Cls
+where
+ Self: ClassType<ThreadKind = dyn AnyThread + 'a>,
+ Self::Super: ClassType<ThreadKind = dyn AnyThread + 'b>,
+{
+}
+
+/// Check that `MainThreadOnly` types do not implement `Send` and `Sync`.
+///
+/// Check implemented using type inference:
+/// let _ = <MyType as MainThreadOnlyDoesNotImplSendSync<_>>::check
+pub trait MainThreadOnlyDoesNotImplSendSync<Inferred> {
+ // Required to reference the trait.
+ fn check() {}
+}
+
+// Type inference will find this blanket impl...
+impl<Cls: ?Sized> MainThreadOnlyDoesNotImplSendSync<()> for Cls {}
+
+// ... unless one of these impls also apply, then type inference fails.
+struct ImplsSend;
+impl<Cls: ?Sized + MainThreadOnly + Send> MainThreadOnlyDoesNotImplSendSync<ImplsSend> for Cls {}
+
+struct ImplsSync;
+impl<Cls: ?Sized + MainThreadOnly + Sync> MainThreadOnlyDoesNotImplSendSync<ImplsSync> for Cls {}
+
+/// Check that class does not implement `Drop`.
+///
+/// This is not needed for soundness, it's just a nice footgun to avoid (since
+/// it wouldn't ever get called).
+///
+/// Check implemented using type inference:
+/// let _ = <MyType as DoesNotImplDrop<_>>::check
+pub trait DoesNotImplDrop<Inferred> {
+ // Required to reference the trait.
+ fn check() {}
+}
+
+// Type inference will find this blanket impl...
+impl<Cls: ?Sized> DoesNotImplDrop<()> for Cls {}
+
+// ... unless this impl also applies, then type inference fails.
+struct ImplsDrop;
+#[allow(drop_bounds)] // We're intentionally using `Drop` as a bound.
+impl<Cls: ?Sized + Drop> DoesNotImplDrop<ImplsDrop> for Cls {}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::extern_class;
+ use crate::runtime::NSObject;
+
+ extern_class!(
+ #[unsafe(super(NSObject))]
+ #[thread_kind = AnyThread]
+ #[name = "NSObject"]
+ struct SetAnyThread;
+ );
+
+ extern_class!(
+ #[unsafe(super(NSObject))]
+ #[thread_kind = AnyThread]
+ #[name = "NSObject"]
+ struct SendSync;
+ );
+
+ unsafe impl Send for SendSync {}
+ unsafe impl Sync for SendSync {}
+
+ extern_class!(
+ #[unsafe(super(NSObject))]
+ #[thread_kind = MainThreadOnly]
+ #[name = "NSObject"]
+ struct OnlyMain;
+ );
+
+ extern_class!(
+ #[unsafe(super(OnlyMain))]
+ #[name = "NSObject"]
+ struct OnlyMainSubDefault;
+ );
+
+ extern_class!(
+ #[unsafe(super(OnlyMain))]
+ #[thread_kind = MainThreadOnly]
+ #[name = "NSObject"]
+ struct OnlyMainSubExplicit;
+ );
+}
diff --git a/third_party/rust/objc2/src/__macro_helpers/common_selectors.rs b/third_party/rust/objc2/src/__macro_helpers/common_selectors.rs
@@ -0,0 +1,117 @@
+//! Common selectors.
+//!
+//! These are put here to deduplicate the cached selector, and when using
+//! `unstable-static-sel`, the statics.
+//!
+//! Note that our assembly tests of `unstable-static-sel-inlined` output a GOT
+//! entry for such accesses, but that is just a limitation of our tests - the
+//! actual assembly is as one would expect.
+use crate::__sel_inner;
+use crate::runtime::Sel;
+
+#[inline]
+pub fn alloc_sel() -> Sel {
+ __sel_inner!("alloc\0", "alloc")
+}
+
+#[inline]
+pub fn init_sel() -> Sel {
+ __sel_inner!("init\0", "init")
+}
+
+#[inline]
+pub fn new_sel() -> Sel {
+ __sel_inner!("new\0", "new")
+}
+
+#[inline]
+pub fn dealloc_sel() -> Sel {
+ __sel_inner!("dealloc\0", "dealloc")
+}
+
+/// An undocumented selector called by the Objective-C runtime when
+/// initializing instance variables.
+#[inline]
+#[allow(dead_code)] // May be useful in the future
+fn cxx_construct_sel() -> Sel {
+ __sel_inner!(".cxx_construct\0", ".cxx_construct")
+}
+
+/// Objective-C runtimes call `.cxx_destruct` as part of the final `dealloc`
+/// call inside `NSObject` (and has done so since macOS 10.4).
+///
+/// While [GCC does document it somewhat][gcc-docs], this is still severely
+/// undocumented in clang - but since the selector is emitted into the final
+/// binary, it is fine to rely on it being used.
+///
+/// Unfortunately though, this only works if the class has been defined
+/// statically, since in that case a flag is set to inform the runtime that it
+/// needs to run destructors. So unfortunately we can't use this on
+/// dynamically defined classes.
+///
+///
+/// # ABI
+///
+/// The ABI of `.cxx_destruct` in Apple's runtime is actually that it does NOT
+/// take a selector, unlike every other Objective-C method, see:
+/// <https://github.com/apple-oss-distributions/objc4/blob/objc4-906/runtime/objc-class.mm#L457>
+///
+/// So the signature is `extern "C-unwind" fn(*mut AnyObject)`.
+///
+/// This is likely because it's not a real Objective-C method that can be
+/// called from userspace / objc_msgSend, and it's more efficient to not pass
+/// the selector.
+///
+/// Note that even if Apple decides to suddenly add the selector one day,
+/// ignoring it will still be sound, since the function uses the C calling
+/// convention, where such an ignored parameter would be allowed on all
+/// relevant architectures.
+///
+/// [gcc-docs]: https://gcc.gnu.org/onlinedocs/gcc/Objective-C-and-Objective-C_002b_002b-Dialect-Options.html#index-fobjc-call-cxx-cdtors
+#[inline]
+#[allow(dead_code)] // May be useful in the future
+fn cxx_destruct_sel() -> Sel {
+ __sel_inner!(".cxx_destruct\0", ".cxx_destruct")
+}
+
+#[cfg(test)]
+mod tests {
+ use alloc::ffi::CString;
+ use core::sync::atomic::{AtomicBool, Ordering};
+
+ use crate::rc::Retained;
+ use crate::runtime::ClassBuilder;
+ use crate::runtime::NSObject;
+ use crate::{msg_send, ClassType};
+
+ use super::*;
+
+ /// Test the unfortunate fact that we can't use .cxx_destruct on dynamic classes.
+ #[test]
+ fn test_destruct_dynamic() {
+ static HAS_RUN: AtomicBool = AtomicBool::new(false);
+
+ let name = CString::new("TestCxxDestruct").unwrap();
+ let mut builder = ClassBuilder::new(&name, NSObject::class()).unwrap();
+
+ unsafe extern "C" fn destruct(_: *mut NSObject, _: Sel) {
+ HAS_RUN.store(true, Ordering::Relaxed);
+ }
+
+ // Note: The ABI is not upheld here, but its fine for this test
+ unsafe { builder.add_method(cxx_destruct_sel(), destruct as unsafe extern "C" fn(_, _)) };
+
+ let cls = builder.register();
+
+ let obj: Retained<NSObject> = unsafe { msg_send![cls, new] };
+ drop(obj);
+ let has_run_destruct = HAS_RUN.load(Ordering::Relaxed);
+
+ // This does work on GNUStep, but unfortunately not in Apple's objc4
+ if cfg!(feature = "gnustep-1-7") {
+ assert!(has_run_destruct);
+ } else {
+ assert!(!has_run_destruct);
+ }
+ }
+}
diff --git a/third_party/rust/objc2/src/__macro_helpers/convert.rs b/third_party/rust/objc2/src/__macro_helpers/convert.rs
@@ -0,0 +1,355 @@
+use crate::encode::{EncodeArgument, EncodeArguments, EncodeReturn};
+use crate::rc::{Allocated, Retained};
+use crate::runtime::{AnyObject, Bool, Sel};
+use crate::Message;
+
+mod argument_private {
+ pub trait Sealed {}
+}
+
+/// Represents types that can be converted to/from an [`EncodeArgument`] type.
+///
+/// This is implemented specially for [`bool`] to allow using that as
+/// Objective-C `BOOL`, where it would otherwise not be allowed (since they
+/// are not ABI compatible).
+///
+/// This is also done specially for `&mut Retained<_>`-like arguments, to
+/// allow using those as "out" / pass-by-writeback parameters.
+pub trait ConvertArgument: argument_private::Sealed {
+ /// The inner type that this can be converted to and from.
+ #[doc(hidden)]
+ type __Inner: EncodeArgument;
+
+ /// A helper type for out parameters.
+ ///
+ /// When dropped, this will process any necessary change to the
+ /// parameters.
+ #[doc(hidden)]
+ type __WritebackOnDrop: Sized;
+
+ #[doc(hidden)]
+ fn __from_defined_param(inner: Self::__Inner) -> Self;
+
+ /// # Safety
+ ///
+ /// The `__WritebackOnDrop` return type must not be leaked, and the
+ /// `__Inner` pointer must not be used after the `__WritebackOnDrop` has
+ /// dropped.
+ ///
+ /// NOTE: The standard way to ensure such a thing is with closures, but
+ /// using those would interact poorly with backtraces of the message send,
+ /// so we're forced to ensure this out of band.
+ #[doc(hidden)]
+ unsafe fn __into_argument(self) -> (Self::__Inner, Self::__WritebackOnDrop);
+}
+
+// Implemented in writeback.rs
+impl<T: Message> argument_private::Sealed for &mut Retained<T> {}
+impl<T: Message> argument_private::Sealed for Option<&mut Retained<T>> {}
+impl<T: Message> argument_private::Sealed for &mut Option<Retained<T>> {}
+impl<T: Message> argument_private::Sealed for Option<&mut Option<Retained<T>>> {}
+
+impl<T: EncodeArgument> argument_private::Sealed for T {}
+impl<T: EncodeArgument> ConvertArgument for T {
+ type __Inner = Self;
+
+ type __WritebackOnDrop = ();
+
+ #[inline]
+ fn __from_defined_param(inner: Self::__Inner) -> Self {
+ inner
+ }
+
+ #[inline]
+ unsafe fn __into_argument(self) -> (Self::__Inner, Self::__WritebackOnDrop) {
+ (self, ())
+ }
+}
+
+impl argument_private::Sealed for bool {}
+impl ConvertArgument for bool {
+ type __Inner = Bool;
+
+ type __WritebackOnDrop = ();
+
+ #[inline]
+ fn __from_defined_param(inner: Self::__Inner) -> Self {
+ inner.as_bool()
+ }
+
+ #[inline]
+ unsafe fn __into_argument(self) -> (Self::__Inner, Self::__WritebackOnDrop) {
+ (Bool::new(self), ())
+ }
+}
+
+mod return_private {
+ pub trait Sealed {}
+}
+
+/// Same as [`ConvertArgument`], but for return types.
+///
+/// See `RetainSemantics` for more details.
+pub trait ConvertReturn<MethodFamily>: return_private::Sealed {
+ type Inner: EncodeReturn;
+
+ #[track_caller]
+ unsafe fn convert_message_return(
+ inner: Self::Inner,
+ receiver_ptr: *mut AnyObject,
+ sel: Sel,
+ ) -> Self;
+
+ fn convert_defined_return(self) -> Self::Inner;
+}
+
+impl<T: EncodeReturn> return_private::Sealed for T {}
+impl<T: EncodeReturn, MethodFamily> ConvertReturn<MethodFamily> for T {
+ type Inner = Self;
+
+ #[inline]
+ unsafe fn convert_message_return(
+ inner: Self::Inner,
+ _receiver_ptr: *mut AnyObject,
+ _sel: Sel,
+ ) -> Self {
+ inner
+ }
+
+ #[inline]
+ fn convert_defined_return(self) -> Self::Inner {
+ self
+ }
+}
+
+impl return_private::Sealed for bool {}
+impl<MethodFamily> ConvertReturn<MethodFamily> for bool {
+ type Inner = Bool;
+
+ #[inline]
+ unsafe fn convert_message_return(
+ inner: Self::Inner,
+ _receiver_ptr: *mut AnyObject,
+ _sel: Sel,
+ ) -> Self {
+ inner.as_bool()
+ }
+
+ #[inline]
+ fn convert_defined_return(self) -> Self::Inner {
+ Bool::new(self)
+ }
+}
+
+// Implemented in retain_semantics.rs
+impl<T: ?Sized + Message> return_private::Sealed for Retained<T> {}
+impl<T: ?Sized + Message> return_private::Sealed for Option<Retained<T>> {}
+impl<T: ?Sized + Message> return_private::Sealed for Allocated<T> {}
+
+pub trait ConvertArguments {
+ #[doc(hidden)]
+ type __Inner: EncodeArguments;
+
+ #[doc(hidden)]
+ type __WritebackOnDrop: Sized;
+
+ #[doc(hidden)]
+ unsafe fn __into_arguments(self) -> (Self::__Inner, Self::__WritebackOnDrop);
+}
+
+pub trait TupleExtender<T> {
+ #[doc(hidden)]
+ type PlusOneArgument;
+ #[doc(hidden)]
+ fn add_argument(self, arg: T) -> Self::PlusOneArgument;
+}
+
+macro_rules! args_impl {
+ ($($a:ident: $t:ident),*) => (
+ impl<$($t: ConvertArgument),*> ConvertArguments for ($($t,)*) {
+ type __Inner = ($($t::__Inner,)*);
+
+ type __WritebackOnDrop = ($($t::__WritebackOnDrop,)*);
+
+ #[inline]
+ unsafe fn __into_arguments(self) -> (Self::__Inner, Self::__WritebackOnDrop) {
+ let ($($a,)*) = self;
+ // SAFETY: Upheld by caller
+ $(let $a = unsafe { ConvertArgument::__into_argument($a) };)*
+
+ (($($a.0,)*), ($($a.1,)*))
+ }
+ }
+
+ impl<$($t,)* T> TupleExtender<T> for ($($t,)*) {
+ type PlusOneArgument = ($($t,)* T,);
+
+ #[inline]
+ fn add_argument(self, arg: T) -> Self::PlusOneArgument {
+ let ($($a,)*) = self;
+ ($($a,)* arg,)
+ }
+ }
+ );
+}
+
+args_impl!();
+args_impl!(a: A);
+args_impl!(a: A, b: B);
+args_impl!(a: A, b: B, c: C);
+args_impl!(a: A, b: B, c: C, d: D);
+args_impl!(a: A, b: B, c: C, d: D, e: E);
+args_impl!(a: A, b: B, c: C, d: D, e: E, f: F);
+args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);
+args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
+args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
+args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
+args_impl!(
+ a: A,
+ b: B,
+ c: C,
+ d: D,
+ e: E,
+ f: F,
+ g: G,
+ h: H,
+ i: I,
+ j: J,
+ k: K
+);
+args_impl!(
+ a: A,
+ b: B,
+ c: C,
+ d: D,
+ e: E,
+ f: F,
+ g: G,
+ h: H,
+ i: I,
+ j: J,
+ k: K,
+ l: L
+);
+args_impl!(
+ a: A,
+ b: B,
+ c: C,
+ d: D,
+ e: E,
+ f: F,
+ g: G,
+ h: H,
+ i: I,
+ j: J,
+ k: K,
+ l: L,
+ m: M
+);
+args_impl!(
+ a: A,
+ b: B,
+ c: C,
+ d: D,
+ e: E,
+ f: F,
+ g: G,
+ h: H,
+ i: I,
+ j: J,
+ k: K,
+ l: L,
+ m: M,
+ n: N
+);
+args_impl!(
+ a: A,
+ b: B,
+ c: C,
+ d: D,
+ e: E,
+ f: F,
+ g: G,
+ h: H,
+ i: I,
+ j: J,
+ k: K,
+ l: L,
+ m: M,
+ n: N,
+ o: O
+);
+args_impl!(
+ a: A,
+ b: B,
+ c: C,
+ d: D,
+ e: E,
+ f: F,
+ g: G,
+ h: H,
+ i: I,
+ j: J,
+ k: K,
+ l: L,
+ m: M,
+ n: N,
+ o: O,
+ p: P
+);
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use core::any::TypeId;
+ use core::ptr;
+
+ use crate::sel;
+
+ #[test]
+ fn convert_normally_noop() {
+ assert_eq!(
+ TypeId::of::<<i32 as ConvertArgument>::__Inner>(),
+ TypeId::of::<i32>()
+ );
+ assert_eq!(<i32 as ConvertArgument>::__from_defined_param(42), 42);
+ assert_eq!(unsafe { ConvertArgument::__into_argument(42i32).0 }, 42);
+ }
+
+ #[test]
+ fn convert_i8() {
+ assert_eq!(
+ TypeId::of::<<i8 as ConvertArgument>::__Inner>(),
+ TypeId::of::<i8>()
+ );
+ assert_eq!(<i8 as ConvertArgument>::__from_defined_param(-3), -3);
+ assert_eq!(unsafe { ConvertArgument::__into_argument(-3i32).0 }, -3);
+ }
+
+ #[test]
+ fn convert_bool() {
+ let receiver_ptr = ptr::null_mut::<AnyObject>();
+ let sel = sel!(foo);
+
+ assert!(!<bool as ConvertArgument>::__from_defined_param(Bool::NO));
+ assert!(<bool as ConvertArgument>::__from_defined_param(Bool::YES));
+ assert!(!unsafe {
+ <bool as ConvertReturn<()>>::convert_message_return(Bool::NO, receiver_ptr, sel)
+ });
+ assert!(unsafe {
+ <bool as ConvertReturn<()>>::convert_message_return(Bool::YES, receiver_ptr, sel)
+ });
+
+ assert!(!unsafe { ConvertArgument::__into_argument(false).0 }.as_bool());
+ assert!(unsafe { ConvertArgument::__into_argument(true).0 }.as_bool());
+ assert!(!ConvertReturn::<()>::convert_defined_return(false).as_bool());
+ assert!(ConvertReturn::<()>::convert_defined_return(true).as_bool());
+
+ #[cfg(all(target_vendor = "apple", target_os = "macos", target_arch = "x86_64"))]
+ assert_eq!(
+ <bool as ConvertArgument>::__Inner::ENCODING_ARGUMENT,
+ crate::encode::Encoding::Char,
+ );
+ }
+}
diff --git a/third_party/rust/objc2/src/__macro_helpers/define_class.rs b/third_party/rust/objc2/src/__macro_helpers/define_class.rs
@@ -0,0 +1,422 @@
+use alloc::ffi::CString;
+#[cfg(debug_assertions)]
+use alloc::vec::Vec;
+use core::marker::PhantomData;
+use core::panic::{RefUnwindSafe, UnwindSafe};
+#[cfg(debug_assertions)]
+use std::collections::HashSet;
+
+use crate::encode::{Encode, Encoding};
+use crate::rc::{Allocated, Retained};
+use crate::runtime::{
+ AnyClass, AnyObject, ClassBuilder, MessageReceiver, MethodImplementation, Sel,
+};
+#[cfg(debug_assertions)]
+use crate::runtime::{AnyProtocol, MethodDescription};
+use crate::{AnyThread, ClassType, DefinedClass, Message, ProtocolType};
+
+use super::defined_ivars::{register_with_ivars, setup_dealloc};
+use super::{CopyFamily, InitFamily, MutableCopyFamily, NewFamily, NoneFamily};
+
+/// Helper for determining auto traits of defined classes.
+///
+/// This will contain either `dyn AnyThread` or `dyn MainThreadOnly`, so it
+/// will have no auto traits by default.
+#[derive(Debug)]
+pub struct ThreadKindAutoTraits<T: ?Sized>(T);
+
+// SAFETY: `AnyThread` does not place restrictions on thread safety.
+unsafe impl Send for ThreadKindAutoTraits<dyn AnyThread> {}
+// SAFETY: Same as above.
+unsafe impl Sync for ThreadKindAutoTraits<dyn AnyThread> {}
+
+// NOTE: A similar implementation for `dyn MainThreadOnly` is explicitly not
+// allowed, as that would enable users to pass something that is tied to the
+// main thread to other threads. Remember that we can view `MainThreadOnly`
+// classes as containing a `MainThreadMarker` (which is always accessible
+// using `MainThreadOnly::mtm`).
+//
+// impl !Send for ThreadKindAutoTraits<dyn MainThreadOnly> {}
+// impl !Sync for ThreadKindAutoTraits<dyn MainThreadOnly> {}
+
+// Thread kind does not affect pinning or unwind safety
+impl<T: ?Sized> Unpin for ThreadKindAutoTraits<T> {}
+impl<T: ?Sized> UnwindSafe for ThreadKindAutoTraits<T> {}
+impl<T: ?Sized> RefUnwindSafe for ThreadKindAutoTraits<T> {}
+
+// Thread kind does not affect autorelease safety.
+#[cfg(feature = "unstable-autoreleasesafe")]
+unsafe impl<T: ?Sized> crate::rc::AutoreleaseSafe for ThreadKindAutoTraits<T> {}
+
+/// Helper type for implementing `MethodImplementation` with a receiver of
+/// `Allocated<T>`, without exposing that implementation to users.
+//
+// Must be private, soundness of MethodImplementation relies on this.
+#[doc(hidden)]
+#[repr(transparent)]
+#[derive(Debug)]
+#[allow(dead_code)]
+pub struct RetainedReturnValue(pub(crate) *mut AnyObject);
+
+// SAFETY: `RetainedReturnValue` is `#[repr(transparent)]`
+unsafe impl Encode for RetainedReturnValue {
+ const ENCODING: Encoding = <*mut AnyObject>::ENCODING;
+}
+
+// One could imagine a different design where we had a method like
+// `fn convert_receiver()`, but that won't work in `define_class!` since we
+// can't actually modify the `self` argument (e.g. `let self = foo(self)` is
+// not allowed).
+//
+// See `MsgSendRetained` and `MethodFamily` for details on the retain
+// semantics we're following here.
+pub trait MessageReceiveRetained<Receiver, Ret> {
+ fn into_return(obj: Ret) -> RetainedReturnValue;
+}
+
+// Receiver and return type have no correlation
+impl<Receiver, Ret> MessageReceiveRetained<Receiver, Ret> for NewFamily
+where
+ Receiver: MessageReceiver,
+ Ret: MaybeOptionRetained,
+{
+ #[inline]
+ fn into_return(obj: Ret) -> RetainedReturnValue {
+ obj.consumed_return()
+ }
+}
+
+// Explicitly left unimplemented for now!
+// impl MessageReceiveRetained<impl MessageReceiver, Allocated<T>> for Alloc {}
+
+// Note: `MethodImplementation` allows for `Allocated` as the receiver, so we
+// restrict it here to only be when the selector is `init`.
+//
+// Additionally, the receiver and return type must have the same generic
+// parameter `T`.
+impl<Ret, T> MessageReceiveRetained<Allocated<T>, Ret> for InitFamily
+where
+ T: Message,
+ Ret: MaybeOptionRetained<Inner = T>,
+{
+ #[inline]
+ fn into_return(obj: Ret) -> RetainedReturnValue {
+ obj.consumed_return()
+ }
+}
+
+// Receiver and return type have no correlation
+impl<Receiver, Ret> MessageReceiveRetained<Receiver, Ret> for CopyFamily
+where
+ Receiver: MessageReceiver,
+ Ret: MaybeOptionRetained,
+{
+ #[inline]
+ fn into_return(obj: Ret) -> RetainedReturnValue {
+ obj.consumed_return()
+ }
+}
+
+// Receiver and return type have no correlation
+impl<Receiver, Ret> MessageReceiveRetained<Receiver, Ret> for MutableCopyFamily
+where
+ Receiver: MessageReceiver,
+ Ret: MaybeOptionRetained,
+{
+ #[inline]
+ fn into_return(obj: Ret) -> RetainedReturnValue {
+ obj.consumed_return()
+ }
+}
+
+// Receiver and return type have no correlation
+impl<Receiver, Ret> MessageReceiveRetained<Receiver, Ret> for NoneFamily
+where
+ Receiver: MessageReceiver,
+ Ret: MaybeOptionRetained,
+{
+ #[inline]
+ fn into_return(obj: Ret) -> RetainedReturnValue {
+ obj.autorelease_return()
+ }
+}
+
+/// Helper trait for specifying an `Retained<T>` or an `Option<Retained<T>>`.
+///
+/// (Both of those are valid return types from define_class!
+/// `#[unsafe(method_id)]`).
+pub trait MaybeOptionRetained {
+ type Inner;
+
+ fn consumed_return(self) -> RetainedReturnValue;
+ fn autorelease_return(self) -> RetainedReturnValue;
+}
+
+impl<T: Message> MaybeOptionRetained for Retained<T> {
+ type Inner = T;
+
+ #[inline]
+ fn consumed_return(self) -> RetainedReturnValue {
+ let ptr: *mut T = Retained::into_raw(self);
+ RetainedReturnValue(ptr.cast())
+ }
+
+ #[inline]
+ fn autorelease_return(self) -> RetainedReturnValue {
+ let ptr: *mut T = Retained::autorelease_return(self);
+ RetainedReturnValue(ptr.cast())
+ }
+}
+
+impl<T: Message> MaybeOptionRetained for Option<Retained<T>> {
+ type Inner = T;
+
+ #[inline]
+ fn consumed_return(self) -> RetainedReturnValue {
+ let ptr: *mut T = Retained::consume_as_ptr_option(self);
+ RetainedReturnValue(ptr.cast())
+ }
+
+ #[inline]
+ fn autorelease_return(self) -> RetainedReturnValue {
+ let ptr: *mut T = Retained::autorelease_return_option(self);
+ RetainedReturnValue(ptr.cast())
+ }
+}
+
+#[derive(Debug)]
+pub struct ClassBuilderHelper<T: ?Sized> {
+ builder: ClassBuilder,
+ p: PhantomData<T>,
+}
+
+// Outlined for code size
+#[track_caller]
+fn create_builder(name: &str, superclass: &AnyClass) -> ClassBuilder {
+ let c_name = CString::new(name).expect("class name must be UTF-8");
+ match ClassBuilder::new(&c_name, superclass) {
+ Some(builder) => builder,
+ None => panic!(
+ "could not create new class {name}. Perhaps a class with that name already exists?"
+ ),
+ }
+}
+
+impl<T: DefinedClass> ClassBuilderHelper<T> {
+ #[inline]
+ #[track_caller]
+ #[allow(clippy::new_without_default)]
+ pub fn new() -> Self
+ where
+ T::Super: ClassType,
+ {
+ let mut builder = create_builder(T::NAME, <T::Super as ClassType>::class());
+
+ setup_dealloc::<T>(&mut builder);
+
+ Self {
+ builder,
+ p: PhantomData,
+ }
+ }
+
+ #[inline]
+ pub fn add_protocol_methods<P>(&mut self) -> ClassProtocolMethodsBuilder<'_, T>
+ where
+ P: ?Sized + ProtocolType,
+ {
+ let protocol = P::protocol();
+
+ if let Some(protocol) = protocol {
+ // Ignore the return value; whether the protocol is added is
+ // inherently dependent on the order of the protocols.
+ self.builder.add_protocol(protocol);
+ }
+
+ #[cfg(debug_assertions)]
+ {
+ ClassProtocolMethodsBuilder {
+ builder: self,
+ protocol,
+ required_instance_methods: protocol
+ .map(|p| p.method_descriptions(true))
+ .unwrap_or_default(),
+ optional_instance_methods: protocol
+ .map(|p| p.method_descriptions(false))
+ .unwrap_or_default(),
+ registered_instance_methods: HashSet::new(),
+ required_class_methods: protocol
+ .map(|p| p.class_method_descriptions(true))
+ .unwrap_or_default(),
+ optional_class_methods: protocol
+ .map(|p| p.class_method_descriptions(false))
+ .unwrap_or_default(),
+ registered_class_methods: HashSet::new(),
+ }
+ }
+
+ #[cfg(not(debug_assertions))]
+ {
+ ClassProtocolMethodsBuilder { builder: self }
+ }
+ }
+
+ // Addition: This restricts to callee `T`
+ #[inline]
+ pub unsafe fn add_method<F>(&mut self, sel: Sel, func: F)
+ where
+ F: MethodImplementation<Callee = T>,
+ {
+ // SAFETY: Checked by caller
+ unsafe { self.builder.add_method(sel, func) }
+ }
+
+ #[inline]
+ pub unsafe fn add_class_method<F>(&mut self, sel: Sel, func: F)
+ where
+ F: MethodImplementation<Callee = AnyClass>,
+ {
+ // SAFETY: Checked by caller
+ unsafe { self.builder.add_class_method(sel, func) }
+ }
+
+ #[inline]
+ pub fn register(self) -> (&'static AnyClass, isize, isize) {
+ register_with_ivars::<T>(self.builder)
+ }
+}
+
+/// Helper for ensuring that:
+/// - Only methods on the protocol are overridden.
+/// - TODO: The methods have the correct signature.
+/// - All required methods are overridden.
+#[derive(Debug)]
+pub struct ClassProtocolMethodsBuilder<'a, T: ?Sized> {
+ builder: &'a mut ClassBuilderHelper<T>,
+ #[cfg(debug_assertions)]
+ protocol: Option<&'static AnyProtocol>,
+ #[cfg(debug_assertions)]
+ required_instance_methods: Vec<MethodDescription>,
+ #[cfg(debug_assertions)]
+ optional_instance_methods: Vec<MethodDescription>,
+ #[cfg(debug_assertions)]
+ registered_instance_methods: HashSet<Sel>,
+ #[cfg(debug_assertions)]
+ required_class_methods: Vec<MethodDescription>,
+ #[cfg(debug_assertions)]
+ optional_class_methods: Vec<MethodDescription>,
+ #[cfg(debug_assertions)]
+ registered_class_methods: HashSet<Sel>,
+}
+
+impl<T: DefinedClass> ClassProtocolMethodsBuilder<'_, T> {
+ // Addition: This restricts to callee `T`
+ #[inline]
+ pub unsafe fn add_method<F>(&mut self, sel: Sel, func: F)
+ where
+ F: MethodImplementation<Callee = T>,
+ {
+ #[cfg(debug_assertions)]
+ if let Some(protocol) = self.protocol {
+ let _types = self
+ .required_instance_methods
+ .iter()
+ .chain(&self.optional_instance_methods)
+ .find(|desc| desc.sel == sel)
+ .map(|desc| desc.types)
+ .unwrap_or_else(|| {
+ panic!(
+ "failed overriding protocol method -[{protocol} {sel}]: method not found"
+ )
+ });
+ }
+
+ // SAFETY: Checked by caller
+ unsafe { self.builder.add_method(sel, func) };
+
+ #[cfg(debug_assertions)]
+ if !self.registered_instance_methods.insert(sel) {
+ unreachable!("already added")
+ }
+ }
+
+ #[inline]
+ pub unsafe fn add_class_method<F>(&mut self, sel: Sel, func: F)
+ where
+ F: MethodImplementation<Callee = AnyClass>,
+ {
+ #[cfg(debug_assertions)]
+ if let Some(protocol) = self.protocol {
+ let _types = self
+ .required_class_methods
+ .iter()
+ .chain(&self.optional_class_methods)
+ .find(|desc| desc.sel == sel)
+ .map(|desc| desc.types)
+ .unwrap_or_else(|| {
+ panic!(
+ "failed overriding protocol method +[{protocol} {sel}]: method not found"
+ )
+ });
+ }
+
+ // SAFETY: Checked by caller
+ unsafe { self.builder.add_class_method(sel, func) };
+
+ #[cfg(debug_assertions)]
+ if !self.registered_class_methods.insert(sel) {
+ unreachable!("already added")
+ }
+ }
+
+ #[cfg(debug_assertions)]
+ pub fn finish(self) {
+ let superclass = self.builder.builder.superclass();
+
+ if let Some(protocol) = self.protocol {
+ for desc in &self.required_instance_methods {
+ if self.registered_instance_methods.contains(&desc.sel) {
+ continue;
+ }
+
+ // TODO: Don't do this when `NS_PROTOCOL_REQUIRES_EXPLICIT_IMPLEMENTATION`
+ if superclass
+ .and_then(|superclass| superclass.instance_method(desc.sel))
+ .is_some()
+ {
+ continue;
+ }
+
+ panic!(
+ "must implement required protocol method -[{protocol} {}]",
+ desc.sel
+ )
+ }
+ }
+
+ if let Some(protocol) = self.protocol {
+ for desc in &self.required_class_methods {
+ if self.registered_class_methods.contains(&desc.sel) {
+ continue;
+ }
+
+ // TODO: Don't do this when `NS_PROTOCOL_REQUIRES_EXPLICIT_IMPLEMENTATION`
+ if superclass
+ .and_then(|superclass| superclass.class_method(desc.sel))
+ .is_some()
+ {
+ continue;
+ }
+
+ panic!(
+ "must implement required protocol method +[{protocol} {}]",
+ desc.sel
+ );
+ }
+ }
+ }
+
+ #[inline]
+ #[cfg(not(debug_assertions))]
+ pub fn finish(self) {}
+}
diff --git a/third_party/rust/objc2/src/__macro_helpers/defined_ivars.rs b/third_party/rust/objc2/src/__macro_helpers/defined_ivars.rs
@@ -0,0 +1,906 @@
+//! # Supporting code for instance variables on defined classes.
+//!
+//! Adding instance variables to Objective-C classes is fairly simple, it can
+//! be done using `ClassBuilder::add_ivar`.
+//!
+//! However, things become more complicated once we have to handle `Drop`,
+//! deallocation and unwind safety; remember, `dealloc` may be called even on
+//! newly, non-initialized instances.
+//!
+//! Note that Swift [doesn't handle this][swift-deinit-unsound], but that
+//! doesn't mean we can simply stick our heads in the sand.
+//!
+//! Basically, instead of storing the ivars directly, we store it as the
+//! following tagged enum:
+//! ```
+//! #[repr(u8)]
+//! enum ActualIvar<T: objc2::DefinedClass> {
+//! Allocated = 0,
+//! PartialInit(T::Ivars),
+//! Finalized(T::Ivars),
+//! }
+//! ```
+//!
+//! For performance reasons, we unfortunately can't write it that cleanly: we
+//! want the data and the drop flag as two separate ivars instead of combining
+//! them into one, since that will give the layout algorithm in the
+//! Objective-C runtime more information to work with, and it allows us to
+//! selectively omit the drop flag or the data storage when either is not
+//! needed.
+//!
+//! Ideally, we'd be able to somehow statically detect when the ivars have a
+//! zero niche, which would allow us to know if the type is safe to drop when
+//! zero-initialized:
+//! ```ignore
+//! None::<T::Ivars>.is_all_zeroes_bitpattern()
+//! ```
+//!
+//! However, detecting if the `None` is all zeroes requires reading the bytes,
+//! which is [unsound for types that may have padding][unsound-read-padding],
+//! since that padding is uninitialized.
+//!
+//! So this is an optimization that we don't yet do, but that may be possible
+//! in the future using something like `bytemuck::ZeroableInOption`.
+//!
+//! [swift-deinit-unsound]: https://github.com/apple/swift/issues/68734
+//! [unsound-read-padding]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ea068e8d9e55801aa9520ea914eb2822
+
+use alloc::borrow::Cow;
+use alloc::ffi::CString;
+use alloc::format;
+use core::ffi::CStr;
+use core::mem;
+use core::ptr::{self, NonNull};
+
+use crate::encode::{Encode, Encoding};
+use crate::runtime::{AnyClass, AnyObject, ClassBuilder, MessageReceiver, Sel};
+use crate::{sel, ClassType, DefinedClass};
+
+/// A type representing the drop flags that may be set for a type.
+#[repr(u8)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub(crate) enum DropFlag {
+ /// Set to zero to ensure that this is the default when created by the
+ /// Objective-C runtime.
+ ///
+ /// Ivars are [documented][obj-init-zeroed] to be zero-initialized after
+ /// allocation, and that has been true since at least [the Objective-C
+ /// version shipped with Mac OS X 10.0][objc4-208-init].
+ ///
+ /// [obj-init-zeroed]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithObjects/WorkingwithObjects.html#//apple_ref/doc/uid/TP40011210-CH4-SW7
+ /// [objc4-208-init]: https://github.com/apple-oss-distributions/objc4/blob/objc4-208/runtime/objc-class.m#L367
+ #[allow(dead_code)]
+ Allocated = 0x00,
+ /// Used when `mem::needs_drop::<T::Ivars>()`, or with debug assertions enabled.
+ InitializedIvars = 0x0f,
+ /// Used when `mem::needs_drop::<T>()`, or with debug assertions enabled.
+ Finalized = 0xff,
+}
+
+// SAFETY: The DropFlag is #[repr(u8)]
+unsafe impl Encode for DropFlag {
+ const ENCODING: Encoding = u8::ENCODING;
+}
+
+pub trait DefinedIvarsHelper {
+ const HAS_IVARS: bool;
+ const HAS_DROP_FLAG: bool;
+}
+
+impl<T: DefinedClass> DefinedIvarsHelper for T {
+ /// Only add ivar if we need the runtime to allocate memory for it.
+ ///
+ /// We can avoid doing so if the type is a zero-sized type (ZST), and the
+ /// required alignment is less than the alignment of a pointer (objects
+ /// are guaranteed to have at least that alignment themselves).
+ const HAS_IVARS: bool = {
+ mem::size_of::<T::Ivars>() > 0
+ || mem::align_of::<T::Ivars>() > mem::align_of::<*mut AnyObject>()
+ };
+ /// Only add drop flag if the type or the ivars need it.
+ ///
+ /// `needs_drop::<T>` can reliably detect a direct implementation of
+ /// `Drop`, since the type only includes `ManuallyDrop` or `PhantomData`
+ /// fields.
+ const HAS_DROP_FLAG: bool = mem::needs_drop::<T>() || mem::needs_drop::<T::Ivars>();
+}
+
+/// Helper function for getting a pointer to the instance variable.
+///
+/// # Safety
+///
+/// The pointer must be valid, and the instance variable offset (if it has
+/// any) must have been initialized.
+#[inline]
+unsafe fn ptr_to_ivar<T: ?Sized + DefinedClass>(ptr: NonNull<T>) -> NonNull<T::Ivars> {
+ // This is called even when there is no ivars, but that's fine, since in
+ // that case the ivar is zero-sized, and the offset will be zero, so we
+ // can still compute a valid pointer to the ivar.
+ //
+ // debug_assert!(T::HAS_IVARS);
+
+ // SAFETY: That an instance variable with the given type exists at the
+ // specified offset is ensured by `DefinedClass` trait implementor.
+ unsafe { AnyObject::ivar_at_offset::<T::Ivars>(ptr.cast(), T::__ivars_offset()) }
+}
+
+/// Helper function for getting a pointer to the drop flag.
+///
+/// # Safety
+///
+/// The pointer must be valid and have an initialized drop flag.
+#[inline]
+unsafe fn ptr_to_drop_flag<T: DefinedClass>(ptr: NonNull<T>) -> *mut DropFlag {
+ debug_assert!(T::HAS_DROP_FLAG, "type did not have drop flag");
+ // SAFETY: That a drop flag exists at the specified offset is ensured
+ // by caller.
+ unsafe { AnyObject::ivar_at_offset::<DropFlag>(ptr.cast(), T::__drop_flag_offset()).as_ptr() }
+}
+
+pub(crate) fn setup_dealloc<T: DefinedClass>(builder: &mut ClassBuilder)
+where
+ T::Super: ClassType,
+{
+ // Add dealloc if the class or the ivars need dropping.
+ if mem::needs_drop::<T>() || mem::needs_drop::<T::Ivars>() {
+ let func: unsafe extern "C-unwind" fn(_, _) = dealloc::<T>;
+ // SAFETY: The function signature is correct, and method contract is
+ // upheld inside `dealloc`.
+ unsafe { builder.add_method(sel!(dealloc), func) };
+ } else {
+ // Users should not rely on this omission, it is only an optimization.
+ }
+}
+
+/// The `dealloc` Objective-C method.
+///
+/// See the following links for more details about `dealloc`:
+/// - <https://clang.llvm.org/docs/AutomaticReferenceCounting.html#dealloc>
+/// - <https://developer.apple.com/documentation/objectivec/nsobject/1571947-dealloc>
+/// - <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-SW2>
+unsafe extern "C-unwind" fn dealloc<T: DefinedClass>(this: NonNull<T>, cmd: Sel)
+where
+ T::Super: ClassType,
+{
+ /// Helper function for marking the cold path when branching.
+ #[inline]
+ #[cold]
+ fn cold_path() {}
+
+ // SAFETY: `dealloc` is only registered when there is a need for dropping,
+ // and hence a need for a drop flag.
+ let drop_flag = unsafe { *ptr_to_drop_flag(this) };
+
+ if mem::needs_drop::<T>() {
+ match drop_flag {
+ // Don't deallocate the current instance if it has not been fully
+ // initialized.
+ //
+ // Note that we still run the superclass deinitializer below.
+ DropFlag::Allocated | DropFlag::InitializedIvars => cold_path(),
+ // SAFETY: This is the `dealloc` method, so we know that the type
+ // never needs to be deallocated again.
+ //
+ // Additionally, we know that the type was fully initialized, since
+ // that's what the drop flag says.
+ //
+ // TODO: This can unwind, is it correct to just let that
+ // propagate?
+ DropFlag::Finalized => unsafe { ptr::drop_in_place(this.as_ptr()) },
+ }
+ }
+
+ // Note: This should be done inside `.cxx_destruct`, since if a superclass
+ // calls an overwritten method in its `dealloc`, it can access
+ // deinitialized instance variables; but we can't do that without
+ // generating statics, so we have to do it in `dealloc` for now.
+ //
+ // It is very important that we do this after the `Drop` of the class
+ // itself above, though.
+ //
+ // Another possibility would be to read the contents of the ivars onto the
+ // stack here, and only deinitialize after the superclass' `dealloc`, but
+ // that would break the pinning guarantee that ivars otherwise have.
+ if mem::needs_drop::<T::Ivars>() {
+ match drop_flag {
+ // Do nothing if the ivars have not been initialized.
+ DropFlag::Allocated => cold_path(),
+ DropFlag::InitializedIvars | DropFlag::Finalized => {
+ // SAFETY: The instance variable is initialized, so it is
+ // valid to drop here.
+ //
+ // TODO: This can unwind, is it correct to just let that
+ // propagate?
+ unsafe { ptr::drop_in_place(ptr_to_ivar(this).as_ptr()) };
+ }
+ }
+ }
+
+ // The superclass' "marker" that this stores is wrapped in `ManuallyDrop`,
+ // we drop it by calling the superclass' `dealloc` method instead.
+ //
+ // Note: ARC does this automatically, which means most Objective-C code in
+ // the wild don't contain this call; but we _are_ ARC, so we must do this.
+ //
+ // SAFETY: The argument and return types are correct, and we make sure to
+ // only call this once.
+ unsafe {
+ MessageReceiver::send_super_message(
+ this,
+ <T as ClassType>::Super::class(),
+ cmd, // Reuse the selector
+ (), // No arguments
+ )
+ }
+}
+
+/// Register the class, and get the ivar offsets.
+#[inline]
+pub(crate) fn register_with_ivars<T: DefinedClass>(
+ mut builder: ClassBuilder,
+) -> (&'static AnyClass, isize, isize) {
+ let (ivar_name, drop_flag_name): (Cow<'static, CStr>, Cow<'static, CStr>) = {
+ if cfg!(feature = "gnustep-1-7") {
+ // GNUStep does not support a subclass having an ivar with the
+ // same name as a superclass, so let's use the class name as the
+ // ivar name to ensure uniqueness.
+ (
+ CString::new(format!("{}_ivars", T::NAME)).unwrap().into(),
+ CString::new(format!("{}_drop_flag", T::NAME))
+ .unwrap()
+ .into(),
+ )
+ } else {
+ // SAFETY: The byte slices are NUL-terminated, and do not contain
+ // interior NUL bytes.
+ // TODO: Use `c"my_str"` syntax once in MSRV
+ unsafe {
+ (
+ CStr::from_bytes_with_nul_unchecked(b"ivars\0").into(),
+ CStr::from_bytes_with_nul_unchecked(b"drop_flag\0").into(),
+ )
+ }
+ }
+ };
+
+ if T::HAS_IVARS {
+ // TODO: Consider not adding a encoding - Swift doesn't do it.
+ let ivar_encoding = Encoding::Array(
+ mem::size_of::<T::Ivars>() as u64,
+ match mem::align_of::<T::Ivars>() {
+ 1 => &u8::ENCODING,
+ 2 => &u16::ENCODING,
+ 4 => &u32::ENCODING,
+ // The alignment of `u64` may not be 8 on all architectures
+ 8 if mem::align_of::<u64>() == 8 => &u64::ENCODING,
+ alignment => panic!("unsupported alignment {alignment} for `{}::Ivars`", T::NAME),
+ },
+ );
+ unsafe { builder.add_ivar_inner::<T::Ivars>(&ivar_name, &ivar_encoding) };
+ }
+
+ if T::HAS_DROP_FLAG {
+ // TODO: Maybe we can reuse the drop flag when subclassing an already
+ // defined class?
+ builder.add_ivar::<DropFlag>(&drop_flag_name);
+ }
+
+ let cls = builder.register();
+
+ let ivars_offset = if T::HAS_IVARS {
+ // Monomorphized error handling
+ // Intentionally not #[track_caller], we expect this error to never occur
+ fn get_ivar_failed() -> ! {
+ unreachable!("failed retrieving instance variable on newly defined class")
+ }
+
+ cls.instance_variable(&ivar_name)
+ .unwrap_or_else(|| get_ivar_failed())
+ .offset()
+ } else {
+ // Fallback to an offset of zero.
+ //
+ // This is fine, since any reads here will only be via zero-sized
+ // ivars, where the actual pointer doesn't matter.
+ 0
+ };
+
+ let drop_flag_offset = if T::HAS_DROP_FLAG {
+ // Monomorphized error handling
+ // Intentionally not #[track_caller], we expect this error to never occur
+ fn get_drop_flag_failed() -> ! {
+ unreachable!("failed retrieving drop flag instance variable on newly defined class")
+ }
+
+ cls.instance_variable(&drop_flag_name)
+ .unwrap_or_else(|| get_drop_flag_failed())
+ .offset()
+ } else {
+ // Fall back to an offset of zero.
+ //
+ // This is fine, since the drop flag is never actually used in the
+ // cases where it was not added.
+ 0
+ };
+
+ (cls, ivars_offset, drop_flag_offset)
+}
+
+/// # Safety
+///
+/// The pointer must be a valid, newly allocated instance.
+#[inline]
+#[track_caller]
+pub(crate) unsafe fn initialize_ivars<T: DefinedClass>(ptr: NonNull<T>, val: T::Ivars) {
+ // Debug assert the state of the drop flag
+ if T::HAS_DROP_FLAG && cfg!(debug_assertions) {
+ // SAFETY: Just checked that the drop flag is available.
+ match unsafe { *ptr_to_drop_flag(ptr) } {
+ DropFlag::Allocated => {
+ // Allow initialization after allocation
+ }
+ DropFlag::InitializedIvars => {
+ panic!("tried to initialize ivars after they were already initialized")
+ }
+ DropFlag::Finalized => {
+ panic!("tried to initialize ivars on an already initialized object")
+ }
+ }
+ }
+
+ // SAFETY:
+ // - Caller ensures the pointer is valid.
+ // - The location is properly aligned by `ClassBuilder::add_ivar`.
+ // - This write is done as part of initialization, so we know that the
+ // pointer is not shared elsewhere.
+ unsafe { ptr_to_ivar(ptr).as_ptr().write(val) };
+
+ // Write to drop flag that we've initialized the instance variables.
+ //
+ // Note: We intentionally only do this _after_ writing to the ivars,
+ // for better unwind safety.
+ if T::HAS_DROP_FLAG && (mem::needs_drop::<T::Ivars>() || cfg!(debug_assertions)) {
+ // SAFETY: Just checked that the drop flag is available.
+ unsafe { ptr_to_drop_flag(ptr).write(DropFlag::InitializedIvars) }
+ }
+}
+
+/// # Safety
+///
+/// The pointer must be valid and finalized (i.e. all super initializers must
+/// have been run).
+#[inline]
+#[track_caller]
+pub(crate) unsafe fn set_finalized<T: DefinedClass>(ptr: NonNull<T>) {
+ // Debug assert the state of the drop flag
+ if T::HAS_DROP_FLAG && cfg!(debug_assertions) {
+ // SAFETY: Just checked that the drop flag is available.
+ match unsafe { *ptr_to_drop_flag(ptr) } {
+ DropFlag::Allocated => {
+ panic!("tried to finalize an object that was not yet fully initialized")
+ }
+ DropFlag::InitializedIvars => {
+ // Allow finalizing after initialization
+ }
+ DropFlag::Finalized => {
+ panic!("tried to finalize an already finalized object")
+ }
+ }
+ }
+
+ // Write to drop flag that we've fully initialized the class.
+ if T::HAS_DROP_FLAG && (mem::needs_drop::<T>() || cfg!(debug_assertions)) {
+ // SAFETY: Just checked that the drop flag is available.
+ unsafe { ptr_to_drop_flag(ptr).write(DropFlag::Finalized) }
+ }
+}
+
+/// # Safety
+///
+/// The pointer must be valid and the instance variables must be initialized.
+#[inline]
+#[track_caller]
+pub(crate) unsafe fn get_initialized_ivar_ptr<T: DefinedClass>(
+ ptr: NonNull<T>,
+) -> NonNull<T::Ivars> {
+ // Debug assert the state of the drop flag
+ if T::HAS_DROP_FLAG && cfg!(debug_assertions) {
+ // SAFETY: Just checked that the drop flag is available.
+ match unsafe { *ptr_to_drop_flag(ptr) } {
+ DropFlag::Allocated => {
+ panic!("tried to access uninitialized instance variable")
+ }
+ DropFlag::InitializedIvars => {
+ // Allow accessing even if not finalized, since we only set
+ // that state _after_ it actually happens, while accesses may
+ // be done by the superclass initializer in e.g. an
+ // overwritten method.
+ }
+ DropFlag::Finalized => {
+ // Allow accessing if finalized
+ }
+ }
+ }
+
+ // SAFETY: That the pointer is valid is ensured by caller.
+ unsafe { ptr_to_ivar(ptr) }
+}
+
+#[cfg(test)]
+mod tests {
+ use alloc::vec::Vec;
+ use core::cell::Cell;
+ use std::sync::OnceLock;
+
+ use super::*;
+ use crate::rc::{Allocated, PartialInit, RcTestObject, Retained, ThreadTestData};
+ use crate::runtime::NSObject;
+ use crate::{define_class, msg_send, AnyThread, Message};
+
+ /// Initialize superclasses, but not own class.
+ unsafe fn init_only_superclasses<T: DefinedClass>(obj: Allocated<T>) -> Retained<T>
+ where
+ T::Super: ClassType,
+ {
+ unsafe { Retained::from_raw(msg_send![super(Allocated::into_ptr(obj)), init]) }.unwrap()
+ }
+
+ /// Initialize, but fail to finalize (which is done internally by
+ /// `msg_send!` when returning `Retained`).
+ unsafe fn init_no_finalize<T: DefinedClass>(obj: Allocated<T>) -> Retained<T>
+ where
+ T::Super: ClassType,
+ T::Ivars: Default,
+ {
+ let obj = obj.set_ivars(Default::default());
+ unsafe { Retained::from_raw(msg_send![super(PartialInit::into_ptr(obj)), init]) }.unwrap()
+ }
+
+ /// Initialize properly.
+ unsafe fn init<T: DefinedClass>(obj: Allocated<T>) -> Retained<T> {
+ unsafe { msg_send![obj, init] }
+ }
+
+ #[test]
+ fn assert_size() {
+ assert_eq!(mem::size_of::<DropFlag>(), 1);
+ }
+
+ #[test]
+ #[cfg(feature = "std")]
+ fn test_dealloc_and_dealloc_subclasses() {
+ use std::sync::Mutex;
+
+ #[derive(Debug, PartialEq)]
+ enum Operation {
+ DropIvar,
+ DropClass,
+ }
+
+ static OPERATIONS: Mutex<Vec<Operation>> = Mutex::new(Vec::new());
+
+ #[derive(Default)]
+ struct IvarThatImplsDrop;
+
+ impl Drop for IvarThatImplsDrop {
+ fn drop(&mut self) {
+ OPERATIONS.lock().unwrap().push(Operation::DropIvar);
+ }
+ }
+
+ #[track_caller]
+ fn check<const N: usize>(expected: [Operation; N]) {
+ let mut operations = OPERATIONS.lock().unwrap();
+ assert_eq!(&**operations, expected);
+ operations.clear();
+ }
+
+ // First class
+
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[ivars = ()]
+ struct ImplsDrop;
+
+ impl ImplsDrop {
+ #[unsafe(method_id(init))]
+ fn init(this: Allocated<Self>) -> Option<Retained<Self>> {
+ unsafe { msg_send![super(this.set_ivars(())), init] }
+ }
+ }
+ );
+
+ impl Drop for ImplsDrop {
+ fn drop(&mut self) {
+ OPERATIONS.lock().unwrap().push(Operation::DropClass);
+ }
+ }
+
+ let _ = ImplsDrop::alloc();
+ check([]);
+
+ let _ = unsafe { init_only_superclasses(ImplsDrop::alloc()) };
+ check([]);
+
+ let _ = unsafe { init_no_finalize(ImplsDrop::alloc()) };
+ check([]);
+
+ let _ = unsafe { init(ImplsDrop::alloc()) };
+ check([Operation::DropClass]);
+
+ // Subclass
+
+ define_class!(
+ #[unsafe(super(ImplsDrop))]
+ #[ivars = IvarThatImplsDrop]
+ struct IvarsImplDrop;
+
+ impl IvarsImplDrop {
+ #[unsafe(method_id(init))]
+ fn init(this: Allocated<Self>) -> Option<Retained<Self>> {
+ unsafe { msg_send![super(this.set_ivars(IvarThatImplsDrop)), init] }
+ }
+ }
+ );
+
+ let _ = IvarsImplDrop::alloc();
+ check([]);
+
+ let _ = unsafe { init_only_superclasses(IvarsImplDrop::alloc()) };
+ check([Operation::DropClass]);
+
+ let _ = unsafe { init_no_finalize(IvarsImplDrop::alloc()) };
+ check([Operation::DropIvar, Operation::DropClass]);
+
+ let _ = unsafe { init(IvarsImplDrop::alloc()) };
+ check([Operation::DropIvar, Operation::DropClass]);
+
+ // Further subclass
+
+ define_class!(
+ #[unsafe(super(IvarsImplDrop))]
+ #[ivars = IvarThatImplsDrop]
+ struct BothIvarsAndTypeImplsDrop;
+
+ impl BothIvarsAndTypeImplsDrop {
+ #[unsafe(method_id(init))]
+ fn init(this: Allocated<Self>) -> Option<Retained<Self>> {
+ unsafe { msg_send![super(this.set_ivars(IvarThatImplsDrop)), init] }
+ }
+ }
+ );
+
+ impl Drop for BothIvarsAndTypeImplsDrop {
+ fn drop(&mut self) {
+ OPERATIONS.lock().unwrap().push(Operation::DropClass);
+ }
+ }
+
+ let _ = BothIvarsAndTypeImplsDrop::alloc();
+ check([]);
+
+ let _ = unsafe { init_only_superclasses(BothIvarsAndTypeImplsDrop::alloc()) };
+ check([Operation::DropIvar, Operation::DropClass]);
+
+ let _ = unsafe { init_no_finalize(BothIvarsAndTypeImplsDrop::alloc()) };
+ check([
+ Operation::DropIvar,
+ Operation::DropIvar,
+ Operation::DropClass,
+ ]);
+
+ let _ = unsafe { init(BothIvarsAndTypeImplsDrop::alloc()) };
+ check([
+ Operation::DropClass,
+ Operation::DropIvar,
+ Operation::DropIvar,
+ Operation::DropClass,
+ ]);
+ }
+
+ #[test]
+ fn test_no_generated_dealloc_if_not_needed() {
+ #[allow(unused)]
+ struct Ivar {
+ field1: u8,
+ field2: bool,
+ }
+
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[ivars = Ivar]
+ struct IvarsNoDrop;
+ );
+
+ assert!(!mem::needs_drop::<IvarsNoDrop>());
+ assert!(!mem::needs_drop::<Ivar>());
+ assert_eq!(
+ IvarsNoDrop::class().instance_method(sel!(dealloc)),
+ NSObject::class().instance_method(sel!(dealloc)),
+ );
+ }
+
+ #[test]
+ fn zst_ivar() {
+ #[derive(Default, Debug, Clone, Copy)]
+ struct Ivar;
+
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[ivars = Cell<Ivar>]
+ struct IvarZst;
+
+ impl IvarZst {
+ #[unsafe(method_id(init))]
+ fn init(this: Allocated<Self>) -> Option<Retained<Self>> {
+ unsafe { msg_send![super(this.set_ivars(Cell::new(Ivar))), init] }
+ }
+ }
+ );
+
+ assert_eq!(
+ IvarZst::class().instance_size(),
+ NSObject::class().instance_size(),
+ );
+ let ivar_name = if cfg!(feature = "gnustep-1-7") {
+ "IvarZst_ivars"
+ } else {
+ "ivars"
+ };
+ let ivar_name = CString::new(ivar_name).unwrap();
+ assert!(IvarZst::class().instance_variable(&ivar_name).is_none());
+
+ let obj = unsafe { init(IvarZst::alloc()) };
+ #[cfg(feature = "std")]
+ std::println!("{:?}", obj.ivars().get());
+ obj.ivars().set(Ivar);
+ }
+
+ #[test]
+ #[should_panic = "unsupported alignment 16 for `HasIvarWithHighAlignment::Ivars`"]
+ fn test_generate_ivar_high_alignment() {
+ #[repr(align(16))]
+ struct HighAlignment;
+
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[name = "HasIvarWithHighAlignment"]
+ #[ivars = HighAlignment]
+ struct HasIvarWithHighAlignment;
+ );
+
+ // Have to allocate up to the desired alignment, but no need to go
+ // further, since the object is zero-sized.
+ assert_eq!(HasIvarWithHighAlignment::class().instance_size(), 16);
+
+ let ivar_name = if cfg!(feature = "gnustep-1-7") {
+ "IvarZst_ivars"
+ } else {
+ "ivars"
+ };
+ let ivar_name = CString::new(ivar_name).unwrap();
+ let ivar = HasIvarWithHighAlignment::class()
+ .instance_variable(&ivar_name)
+ .unwrap();
+ assert_eq!(ivar.offset(), 16);
+ }
+
+ #[test]
+ fn test_ivar_access() {
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[ivars = Cell<Option<Retained<RcTestObject>>>]
+ struct RcIvar;
+
+ impl RcIvar {
+ #[unsafe(method_id(init))]
+ fn init(this: Allocated<Self>) -> Option<Retained<Self>> {
+ let this = this.set_ivars(Cell::new(Some(RcTestObject::new())));
+ unsafe { msg_send![super(this), init] }
+ }
+ }
+ );
+
+ let mut expected = ThreadTestData::current();
+
+ let _ = RcIvar::alloc();
+ expected.assert_current();
+
+ let _ = unsafe { init_only_superclasses(RcIvar::alloc()) };
+ expected.assert_current();
+
+ // Ivar access is valid even if the class is not finalized.
+ let obj = unsafe { init_no_finalize(RcIvar::alloc()) };
+ expected.assert_current();
+
+ obj.ivars().set(Some(RcTestObject::new()));
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.assert_current();
+
+ drop(obj);
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+
+ let obj = unsafe { init(RcIvar::alloc()) };
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.assert_current();
+
+ // SAFETY: Cloned immediately after, so not accessed while borrowed
+ let ivar = unsafe { &*obj.ivars().as_ptr() }.clone();
+ obj.ivars().set(ivar);
+ expected.retain += 1;
+ expected.release += 1;
+ expected.assert_current();
+
+ drop(obj);
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+
+ #[derive(Default)]
+ struct RcIvarSubclassIvars {
+ int: Cell<i32>,
+ obj: Cell<Retained<RcTestObject>>,
+ }
+
+ define_class!(
+ #[unsafe(super(RcIvar))]
+ #[ivars = RcIvarSubclassIvars]
+ struct RcIvarSubclass;
+
+ impl RcIvarSubclass {
+ #[unsafe(method_id(init))]
+ fn init(this: Allocated<Self>) -> Option<Retained<Self>> {
+ let this = this.set_ivars(RcIvarSubclassIvars {
+ int: Cell::new(42),
+ obj: Cell::new(RcTestObject::new()),
+ });
+ unsafe { msg_send![super(this), init] }
+ }
+ }
+ );
+
+ let obj = unsafe { init(RcIvarSubclass::alloc()) };
+ expected.alloc += 2;
+ expected.init += 2;
+ expected.assert_current();
+ assert_eq!(obj.ivars().int.get(), 42);
+
+ obj.ivars().int.set(obj.ivars().int.get() + 1);
+ assert_eq!(obj.ivars().int.get(), 43);
+
+ // SAFETY: Cloned immediately after, so not accessed while borrowed
+ let ivar = unsafe { &*(**obj).ivars().as_ptr() }.clone().unwrap();
+ obj.ivars().obj.set(ivar);
+ expected.retain += 1;
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+
+ // Change super ivars
+ (**obj).ivars().set(None);
+ expected.release += 1;
+ expected.assert_current();
+
+ drop(obj);
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+
+ let obj = unsafe { init_only_superclasses(RcIvarSubclass::alloc()) };
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.assert_current();
+
+ // Accessing superclass ivars is valid
+ // SAFETY: Cell not accessed while ivar is borrowed
+ #[cfg(feature = "std")]
+ std::println!("{:?}", unsafe { &*(**obj).ivars().as_ptr() });
+
+ drop(obj);
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+ }
+
+ #[test]
+ #[cfg_attr(not(debug_assertions), ignore = "only panics with debug assertions")]
+ #[should_panic = "tried to access uninitialized instance variable"]
+ fn access_invalid() {
+ define_class!(
+ #[unsafe(super(NSObject))]
+ // Type has to have a drop flag to detect invalid access
+ #[ivars = Retained<NSObject>]
+ struct InvalidAccess;
+ );
+
+ let obj = unsafe { init_only_superclasses(InvalidAccess::alloc()) };
+ #[cfg(feature = "std")]
+ std::println!("{:?}", obj.ivars());
+ }
+
+ #[test]
+ #[should_panic = "panic in drop"]
+ #[ignore = "panicking in Drop requires that we actually implement `dealloc` as `C-unwind`"]
+ fn test_panic_in_drop() {
+ define_class!(
+ #[unsafe(super(NSObject))]
+ struct DropPanics;
+ );
+
+ impl Drop for DropPanics {
+ fn drop(&mut self) {
+ panic!("panic in drop");
+ }
+ }
+
+ let obj = DropPanics::alloc().set_ivars(());
+ let obj: Retained<DropPanics> = unsafe { msg_send![super(obj), init] };
+ drop(obj);
+ }
+
+ #[test]
+ #[should_panic = "panic in ivar drop"]
+ #[ignore = "panicking in Drop requires that we actually implement `dealloc` as `C-unwind`"]
+ fn test_panic_in_ivar_drop() {
+ struct DropPanics;
+
+ impl Drop for DropPanics {
+ fn drop(&mut self) {
+ panic!("panic in ivar drop");
+ }
+ }
+
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[ivars = DropPanics]
+ struct IvarDropPanics;
+ );
+
+ let obj = IvarDropPanics::alloc().set_ivars(DropPanics);
+ let obj: Retained<IvarDropPanics> = unsafe { msg_send![super(obj), init] };
+ drop(obj);
+ }
+
+ // We cannot really guard against this, so dealloc must be unsafe!
+ //
+ // At least not by checking `retainCount`, since that gets set to `0` when
+ // dropping. I guess we _could_ override `retain` and check `retainCount`
+ // before we do anything, but that seems brittle, and it would hurt
+ // performance.
+ #[test]
+ fn test_retain_leak_in_drop() {
+ define_class!(
+ // SAFETY: Intentionally broken!
+ #[unsafe(super(NSObject))]
+ #[derive(Debug)]
+ struct DropRetainsAndLeaksSelf;
+ );
+
+ unsafe impl Send for DropRetainsAndLeaksSelf {}
+ unsafe impl Sync for DropRetainsAndLeaksSelf {}
+
+ static OBJ: OnceLock<Retained<DropRetainsAndLeaksSelf>> = OnceLock::new();
+
+ impl Drop for DropRetainsAndLeaksSelf {
+ fn drop(&mut self) {
+ fn inner(this: &DropRetainsAndLeaksSelf) {
+ // Smuggle a reference out of this context.
+ OBJ.set(this.retain()).unwrap();
+ }
+
+ inner(self)
+ }
+ }
+
+ let obj = DropRetainsAndLeaksSelf::alloc().set_ivars(());
+ let obj: Retained<DropRetainsAndLeaksSelf> = unsafe { msg_send![super(obj), init] };
+ drop(obj);
+
+ // Suddenly, the object is alive again!
+ let _ = OBJ.get().unwrap();
+ }
+}
diff --git a/third_party/rust/objc2/src/__macro_helpers/image_info.rs b/third_party/rust/objc2/src/__macro_helpers/image_info.rs
@@ -0,0 +1,46 @@
+#[repr(C)]
+#[doc(hidden)]
+#[derive(Debug, Clone, Copy)]
+pub struct ImageInfo {
+ // These are not actually `unsigned int`, even though the docs say so
+ /// The version of the image info struct.
+ version: u32,
+ flags: u32,
+}
+
+#[allow(unused)]
+impl ImageInfo {
+ /// Unused
+ const FIX_AND_CONTINUE: u32 = 1 << 0;
+ const SUPPORTS_GARBAGE_COLLECTED: u32 = 1 << 1;
+ const REQUIRES_GARBAGE_COLLECTION: u32 = 1 << 2;
+ const OPTIMIZED_BY_DYLD: u32 = 1 << 3; // TODO
+ /// Unused
+ const CORRECTED_SYNTHESIZE: u32 = 1 << 4;
+ /// Whether we're compiling this to run on a simulator.
+ const IMAGE_IS_SIMULATED: u32 = 1 << 5;
+ /// Whether we are generating class properties.
+ const CLASS_PROPERTIES: u32 = 1 << 6;
+ const DYLD_PREOPTIMIZED: u32 = 1 << 7;
+
+ const SWIFT_ABI_VERSION_SHIFT: u32 = 8;
+ const SWIFT_ABI_VERSION_MASK: u32 = 0xff << Self::SWIFT_ABI_VERSION_SHIFT;
+ const SWIFT_MINOR_VERSION_SHIFT: u32 = 16;
+ const SWIFT_MINOR_VERSION_MASK: u32 = 0xff << Self::SWIFT_MINOR_VERSION_SHIFT;
+ const SWIFT_MAJOR_VERSION_SHIFT: u32 = 24;
+ const SWIFT_MAJOR_VERSION_MASK: u32 = 0xff << Self::SWIFT_MAJOR_VERSION_SHIFT;
+
+ /// Fetches the image info for the current runtime + target combination
+ #[inline]
+ pub const fn system() -> Self {
+ // We don't currently do anything relating to class properties, but
+ // let's mimic what Clang does!
+ let mut flags = Self::CLASS_PROPERTIES;
+
+ if cfg!(target_simulator) {
+ flags |= Self::IMAGE_IS_SIMULATED;
+ }
+
+ Self { version: 0, flags }
+ }
+}
diff --git a/third_party/rust/objc2/src/__macro_helpers/method_family.rs b/third_party/rust/objc2/src/__macro_helpers/method_family.rs
@@ -0,0 +1,342 @@
+/// Helper for specifying the method family for a given selector.
+///
+/// Note that we can't actually check if a method is in a method family; only
+/// whether the _selector_ is in a _selector_ family.
+///
+/// The slight difference here is:
+/// - The method may be annotated with the `objc_method_family` attribute,
+/// which would cause it to be in a different family. That this is not the
+/// case is part of the `unsafe` contract of `msg_send!`.
+/// - The method may not obey the added restrictions of the method family.
+/// The added restrictions are:
+/// - `new`, `alloc`, `copy` and `mutableCopy`: The method must return a
+/// retainable object pointer type - we ensure this by making
+/// `message_send` return `Retained`.
+/// - `init`: The method must be an instance method and must return an
+/// Objective-C pointer type - We ensure this by taking `Allocated<T>`,
+/// which means it can't be a class method!
+///
+/// <https://clang.llvm.org/docs/AutomaticReferenceCounting.html#retainable-object-pointers-as-operands-and-arguments>
+// TODO: Use an enum instead of u8 here when possible in `const`.
+#[derive(Debug)]
+pub struct MethodFamily<const INNER: u8> {}
+
+/// The `new` family.
+pub type NewFamily = MethodFamily<1>;
+/// The `alloc` family.
+pub type AllocFamily = MethodFamily<2>;
+/// The `init` family.
+pub type InitFamily = MethodFamily<3>;
+/// The `copy` family.
+pub type CopyFamily = MethodFamily<4>;
+/// The `mutableCopy` family.
+pub type MutableCopyFamily = MethodFamily<5>;
+/// No family.
+pub type NoneFamily = MethodFamily<6>;
+
+// These are used to avoid trying to do retain-semantics for these special
+// selectors that would otherwise fall under `NoneFamily`.
+
+/// The `retain` selector itself.
+pub type RetainSelector = MethodFamily<8>;
+/// The `release` selector itself.
+pub type ReleaseSelector = MethodFamily<9>;
+/// The `autorelease` selector itself.
+pub type AutoreleaseSelector = MethodFamily<10>;
+/// The `dealloc` selector itself.
+pub type DeallocSelector = MethodFamily<11>;
+
+/// Helper module where `#[unsafe(method_family = $family:ident)]` will import
+/// its value from.
+#[allow(non_camel_case_types)]
+pub mod method_family_import {
+ // Rename to match Clang's `__attribute__((objc_method_family(family)))`.
+ pub use super::{
+ AllocFamily as alloc, CopyFamily as copy, InitFamily as init,
+ MutableCopyFamily as mutableCopy, NewFamily as new, NoneFamily as none,
+ };
+}
+
+/// Determine the constant to specify in `MethodFamily` to get the method
+/// family type.
+///
+/// This is only called with the first part of the selector, as that's enough
+/// to determine the family, and that way we can emit less code for rustc to
+/// parse.
+///
+/// Examples:
+/// - `init` in `init`, returns `3`.
+/// - `allocWithZone` in `allocWithZone:`, returns `2`.
+/// - `copyItemAtURL` in `copyItemAtURL:toURL:error:`, returns `4`.
+/// - `convertRect` in `convertRect:fromView:`, returns `6`.
+pub const fn method_family(first_selector_part: &str) -> u8 {
+ let first_selector_part = first_selector_part.as_bytes();
+ match (
+ in_selector_family(first_selector_part, b"new"),
+ in_selector_family(first_selector_part, b"alloc"),
+ in_selector_family(first_selector_part, b"init"),
+ in_selector_family(first_selector_part, b"copy"),
+ in_selector_family(first_selector_part, b"mutableCopy"),
+ ) {
+ (true, false, false, false, false) => 1,
+ (false, true, false, false, false) => 2,
+ (false, false, true, false, false) => 3,
+ (false, false, false, true, false) => 4,
+ (false, false, false, false, true) => 5,
+ (false, false, false, false, false) => 6,
+ _ => unreachable!(),
+ }
+}
+
+/// Get the method family from an explicit family name, if specified,
+/// otherwise infer it from the given selector.
+///
+/// No validation of the selector is done here, that must be done elsewhere.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __method_family {
+ // Explicit method family provided.
+ //
+ // This branch is placed first for compile-time performance.
+ (
+ ($($method_family:tt)+)
+ ($($sel:tt)*)
+ ) => {
+ $crate::__macro_helpers::method_family_import::$($method_family)+
+ };
+
+ // Often called, avoid generating logic for figuring it out from selector.
+ (
+ ()
+ (alloc)
+ ) => {
+ $crate::__macro_helpers::AllocFamily
+ };
+ (
+ ()
+ (new)
+ ) => {
+ $crate::__macro_helpers::NewFamily
+ };
+ (
+ ()
+ (init)
+ ) => {
+ $crate::__macro_helpers::InitFamily
+ };
+
+ // To prevent automatic memory management when using these.
+ (
+ ()
+ (dealloc)
+ ) => {
+ $crate::__macro_helpers::DeallocSelector
+ };
+ (
+ ()
+ (retain)
+ ) => {
+ $crate::__macro_helpers::RetainSelector
+ };
+ (
+ ()
+ (release)
+ ) => {
+ $crate::__macro_helpers::ReleaseSelector
+ };
+ (
+ ()
+ (autorelease)
+ ) => {
+ $crate::__macro_helpers::AutoreleaseSelector
+ };
+
+ // Figure out from selector.
+ (
+ ()
+ ($sel_first:tt $($sel_rest:tt)*)
+ ) => {
+ $crate::__macro_helpers::MethodFamily<{
+ // Method families can be determined from just the first part of
+ // the selector, so for compile-time performance we only stringify
+ // and pass that part.
+ $crate::__macro_helpers::method_family($crate::__macro_helpers::stringify!($sel_first))
+ }>
+ };
+
+ // Missing selector, allow for better UI.
+ (
+ ()
+ ()
+ ) => {
+ $crate::__macro_helpers::MethodFamily<{
+ $crate::__macro_helpers::compile_error!("missing selector");
+ $crate::__macro_helpers::method_family("")
+ }>
+ };
+}
+
+/// Checks whether a given selector is said to be in a given selector family.
+///
+/// <https://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-method-families>
+const fn in_selector_family(mut selector: &[u8], mut family: &[u8]) -> bool {
+ // Skip leading underscores from selector
+ loop {
+ selector = match selector {
+ [b'_', rest @ ..] => rest,
+ _ => break,
+ }
+ }
+
+ // Compare each character
+ loop {
+ (selector, family) = match (selector, family) {
+ // Remaining items
+ ([s, selector @ ..], [f, family @ ..]) => {
+ if *s == *f {
+ // Next iteration
+ (selector, family)
+ } else {
+ // Family does not begin with selector
+ return false;
+ }
+ }
+ // Equal
+ ([], []) => {
+ return true;
+ }
+ // Selector can't be part of family if smaller than it
+ ([], _) => {
+ return false;
+ }
+ // Remaining items in selector
+ // -> ensure next character is not lowercase
+ ([s, ..], []) => {
+ return !s.is_ascii_lowercase();
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use alloc::string::ToString;
+
+ use super::*;
+
+ #[test]
+ fn test_in_selector_family() {
+ #[track_caller]
+ fn assert_in_family(selector: &str, family: &str) {
+ assert!(in_selector_family(selector.as_bytes(), family.as_bytes()));
+ let selector = selector.to_string() + "\0";
+ assert!(in_selector_family(selector.as_bytes(), family.as_bytes()));
+ }
+
+ #[track_caller]
+ fn assert_not_in_family(selector: &str, family: &str) {
+ assert!(!in_selector_family(selector.as_bytes(), family.as_bytes()));
+ let selector = selector.to_string() + "\0";
+ assert!(!in_selector_family(selector.as_bytes(), family.as_bytes()));
+ }
+
+ // Common cases
+
+ assert_in_family("alloc", "alloc");
+ assert_in_family("allocWithZone:", "alloc");
+ assert_not_in_family("dealloc", "alloc");
+ assert_not_in_family("initialize", "init");
+ assert_not_in_family("decimalNumberWithDecimal:", "init");
+ assert_in_family("initWithCapacity:", "init");
+ assert_in_family("_initButPrivate:withParam:", "init");
+ assert_not_in_family("description", "init");
+ assert_not_in_family("inIT", "init");
+
+ assert_not_in_family("init", "copy");
+ assert_not_in_family("copyingStuff:", "copy");
+ assert_in_family("copyWithZone:", "copy");
+ assert_not_in_family("initWithArray:copyItems:", "copy");
+ assert_in_family("copyItemAtURL:toURL:error:", "copy");
+
+ assert_not_in_family("mutableCopying", "mutableCopy");
+ assert_in_family("mutableCopyWithZone:", "mutableCopy");
+ assert_in_family("mutableCopyWithZone:", "mutableCopy");
+
+ assert_in_family(
+ "newScriptingObjectOfClass:forValueForKey:withContentsValue:properties:",
+ "new",
+ );
+ assert_in_family(
+ "newScriptingObjectOfClass:forValueForKey:withContentsValue:properties:",
+ "new",
+ );
+ assert_not_in_family("newsstandAssetDownload", "new");
+
+ // Trying to weed out edge-cases:
+
+ assert_in_family("__abcDef", "abc");
+ assert_in_family("_abcDef", "abc");
+ assert_in_family("abcDef", "abc");
+ assert_in_family("___a", "a");
+ assert_in_family("__a", "a");
+ assert_in_family("_a", "a");
+ assert_in_family("a", "a");
+
+ assert_not_in_family("_abcdef", "abc");
+ assert_not_in_family("_abcdef", "def");
+ assert_not_in_family("_bcdef", "abc");
+ assert_not_in_family("a_bc", "abc");
+ assert_not_in_family("abcdef", "abc");
+ assert_not_in_family("abcdef", "def");
+ assert_not_in_family("abcdef", "abb");
+ assert_not_in_family("___", "a");
+ assert_not_in_family("_", "a");
+ assert_not_in_family("", "a");
+
+ assert_in_family("copy", "copy");
+ assert_in_family("copy:", "copy");
+ assert_in_family("copyMe", "copy");
+ assert_in_family("_copy", "copy");
+ assert_in_family("_copy:", "copy");
+ assert_in_family("_copyMe", "copy");
+ assert_not_in_family("copying", "copy");
+ assert_not_in_family("copying:", "copy");
+ assert_not_in_family("_copying", "copy");
+ assert_not_in_family("Copy", "copy");
+ assert_not_in_family("COPY", "copy");
+
+ // Empty family (not supported)
+ assert_in_family("___", "");
+ assert_in_family("__", "");
+ assert_in_family("_", "");
+ assert_in_family("", "");
+ assert_not_in_family("_a", "");
+ assert_not_in_family("a", "");
+ assert_in_family("_A", "");
+ assert_in_family("A", "");
+
+ // Double-colon selectors
+ assert_in_family("abc::abc::", "abc");
+ assert_in_family("abc:::", "abc");
+ assert_in_family("abcDef::xyz:", "abc");
+ // Invalid selector (probably)
+ assert_not_in_family("::abc:", "abc");
+ }
+
+ #[test]
+ fn test_method_family() {
+ #[track_caller]
+ fn assert_types_eq<T: 'static, U: 'static>() {
+ assert_eq!(std::any::TypeId::of::<T>(), std::any::TypeId::of::<U>());
+ }
+
+ assert_types_eq::<AllocFamily, __method_family!(()(alloc))>();
+ assert_types_eq::<AllocFamily, __method_family!(()(allocWithZone:))>();
+ assert_types_eq::<CopyFamily, __method_family!(()(copyItemAtURL:toURL:error:))>();
+ assert_types_eq::<NewFamily, __method_family!(()(new))>();
+ assert_types_eq::<InitFamily, __method_family!(()(initWithArray:))>();
+ assert_types_eq::<NoneFamily, __method_family!(()(somethingElse:))>();
+
+ assert_types_eq::<CopyFamily, __method_family!((copy)(initWithArray:))>();
+ }
+}
diff --git a/third_party/rust/objc2/src/__macro_helpers/mod.rs b/third_party/rust/objc2/src/__macro_helpers/mod.rs
@@ -0,0 +1,108 @@
+pub use core::borrow::Borrow;
+pub use core::cell::UnsafeCell;
+pub use core::cmp::{Eq, PartialEq};
+pub use core::convert::AsRef;
+pub use core::default::Default;
+pub use core::fmt;
+pub use core::hash::{Hash, Hasher};
+pub use core::marker::{PhantomData, Sized};
+pub use core::mem::{size_of, transmute, ManuallyDrop, MaybeUninit};
+pub use core::ops::Deref;
+pub use core::option::Option::{self, None, Some};
+pub use core::primitive::{bool, isize, str, u8};
+pub use core::{compile_error, concat, env, module_path, panic, stringify};
+// TODO: Use `core::cell::LazyCell`
+pub use std::sync::Once;
+
+mod cache;
+mod class;
+mod common_selectors;
+mod convert;
+mod define_class;
+pub(crate) mod defined_ivars;
+mod image_info;
+mod method_family;
+mod module_info;
+mod msg_send_retained;
+mod null_error;
+mod os_version;
+mod retain_semantics;
+mod sync_unsafe_cell;
+mod writeback;
+
+pub use self::cache::{CachedClass, CachedSel};
+pub use self::class::{DoesNotImplDrop, MainThreadOnlyDoesNotImplSendSync, ValidThreadKind};
+pub use self::common_selectors::{alloc_sel, dealloc_sel, init_sel, new_sel};
+pub use self::convert::{ConvertArgument, ConvertArguments, ConvertReturn, TupleExtender};
+pub use self::define_class::{
+ ClassBuilderHelper, ClassProtocolMethodsBuilder, MaybeOptionRetained, MessageReceiveRetained,
+ RetainedReturnValue, ThreadKindAutoTraits,
+};
+pub use self::defined_ivars::DefinedIvarsHelper;
+pub use self::image_info::ImageInfo;
+pub use self::method_family::{
+ method_family, method_family_import, AllocFamily, AutoreleaseSelector, CopyFamily,
+ DeallocSelector, InitFamily, MethodFamily, MutableCopyFamily, NewFamily, NoneFamily,
+ ReleaseSelector, RetainSelector,
+};
+pub use self::module_info::ModuleInfo;
+pub use self::msg_send_retained::{MsgSend, MsgSendError, MsgSendSuper, MsgSendSuperError};
+pub use self::os_version::{is_available, AvailableVersion, OSVersion};
+pub use self::retain_semantics::{
+ KindDefined, KindSendMessage, KindSendMessageSuper, RetainSemantics,
+};
+pub use self::sync_unsafe_cell::SyncUnsafeCell;
+
+/// Disallow using this passed in value in const and statics for forwards
+/// compatibility (this function is not a `const` function).
+#[inline]
+pub fn disallow_in_static<T>(item: &'static T) -> &'static T {
+ item
+}
+
+#[deprecated = "having the `impl` inside `extern_methods!` is deprecated, move it outside instead"]
+pub const fn extern_methods_unsafe_impl() {}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[cfg(any(feature = "unstable-static-sel", feature = "unstable-static-class"))]
+ use crate::__hash_idents;
+
+ #[test]
+ #[cfg(any(feature = "unstable-static-sel", feature = "unstable-static-class"))]
+ fn hash_idents_different() {
+ assert_ne!(__hash_idents!(abc), __hash_idents!(def));
+ }
+
+ #[test]
+ #[cfg(any(feature = "unstable-static-sel", feature = "unstable-static-class"))]
+ fn hash_idents_same_no_equal() {
+ assert_ne!(__hash_idents!(abc), __hash_idents!(abc));
+ assert_ne!(__hash_idents!(abc def ghi), __hash_idents!(abc def ghi));
+ }
+
+ #[test]
+ #[cfg(any(feature = "unstable-static-sel", feature = "unstable-static-class"))]
+ fn hash_idents_exact_same_ident() {
+ macro_rules! x {
+ ($x:ident) => {
+ (__hash_idents!($x), __hash_idents!($x))
+ };
+ }
+ let (ident1, ident2) = x!(abc);
+ // This is a limitation of `__hash_idents`, ideally we'd like these
+ // to be different!
+ assert_eq!(ident1, ident2);
+ }
+
+ #[test]
+ #[cfg_attr(
+ not(all(target_vendor = "apple", target_os = "macos", target_arch = "x86")),
+ ignore = "Only relevant on macOS 32-bit"
+ )]
+ fn ensure_size_of_module_info() {
+ assert_eq!(core::mem::size_of::<ModuleInfo>(), 16);
+ }
+}
diff --git a/third_party/rust/objc2/src/__macro_helpers/module_info.rs b/third_party/rust/objc2/src/__macro_helpers/module_info.rs
@@ -0,0 +1,29 @@
+/// Helper struct for emitting the module info that macOS 32-bit requires.
+///
+/// <https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/CodeGen/CGObjCMac.cpp#L5211-L5234>
+#[repr(C)]
+#[derive(Debug)]
+pub struct ModuleInfo {
+ version: usize,
+ size: usize,
+ name: *const u8,
+ symtab: *const (),
+}
+
+// SAFETY: ModuleInfo is immutable.
+unsafe impl Sync for ModuleInfo {}
+
+impl ModuleInfo {
+ /// This is hardcoded in clang as 7.
+ const VERSION: usize = 7;
+
+ pub const fn new(name: *const u8) -> Self {
+ Self {
+ version: Self::VERSION,
+ size: core::mem::size_of::<Self>(),
+ name,
+ // We don't expose any symbols
+ symtab: core::ptr::null(),
+ }
+ }
+}
diff --git a/third_party/rust/objc2/src/__macro_helpers/msg_send_retained.rs b/third_party/rust/objc2/src/__macro_helpers/msg_send_retained.rs
@@ -0,0 +1,1058 @@
+use core::ptr;
+
+use crate::encode::{Encode, RefEncode};
+use crate::rc::Retained;
+use crate::runtime::{AnyClass, MessageReceiver, Sel};
+use crate::ClassType;
+
+use super::null_error::encountered_error;
+use super::{
+ ConvertArguments, KindSendMessage, KindSendMessageSuper, RetainSemantics, TupleExtender,
+};
+
+//
+// MsgSend
+//
+
+pub trait MsgSend<Receiver, Return> {
+ #[track_caller]
+ unsafe fn send_message<A: ConvertArguments>(receiver: Receiver, sel: Sel, args: A) -> Return;
+}
+
+impl<Receiver, Return, MethodFamily> MsgSend<Receiver, Return> for MethodFamily
+where
+ MethodFamily: RetainSemantics<Receiver, Return, KindSendMessage>,
+{
+ #[inline]
+ unsafe fn send_message<A: ConvertArguments>(receiver: Receiver, sel: Sel, args: A) -> Return {
+ let ptr = Self::prepare_message_send(receiver).__as_raw_receiver();
+
+ // SAFETY: The writeback helper is not leaked (it is dropped at the
+ // end of this scope).
+ let (args, _helper) = unsafe { A::__into_arguments(args) };
+
+ // SAFETY: Upheld by caller.
+ let ret = unsafe { MessageReceiver::send_message(ptr, sel, args) };
+
+ // SAFETY: The pointers are valid (or, in the case of the receiver
+ // pointer, at least valid when the message send is not `init`).
+ unsafe { Self::convert_message_return(ret, ptr, sel) }
+ }
+}
+
+//
+// MsgSendSuper
+//
+
+pub trait MsgSendSuper<Receiver, Return> {
+ type Inner: ?Sized + RefEncode;
+
+ #[track_caller]
+ unsafe fn send_super_message<A: ConvertArguments>(
+ receiver: Receiver,
+ superclass: &AnyClass,
+ sel: Sel,
+ args: A,
+ ) -> Return;
+
+ #[inline]
+ #[track_caller]
+ unsafe fn send_super_message_static<A: ConvertArguments>(
+ receiver: Receiver,
+ sel: Sel,
+ args: A,
+ ) -> Return
+ where
+ Self::Inner: ClassType,
+ <Self::Inner as ClassType>::Super: ClassType,
+ {
+ unsafe {
+ Self::send_super_message(
+ receiver,
+ <Self::Inner as ClassType>::Super::class(),
+ sel,
+ args,
+ )
+ }
+ }
+}
+
+impl<Receiver, Return, MethodFamily> MsgSendSuper<Receiver, Return> for MethodFamily
+where
+ MethodFamily: RetainSemantics<Receiver, Return, KindSendMessageSuper>,
+{
+ type Inner = <<MethodFamily as RetainSemantics<Receiver, Return, KindSendMessageSuper>>::ReceiverInner as MessageReceiver>::__Inner;
+
+ #[inline]
+ unsafe fn send_super_message<A: ConvertArguments>(
+ receiver: Receiver,
+ superclass: &AnyClass,
+ sel: Sel,
+ args: A,
+ ) -> Return {
+ let ptr = Self::prepare_message_send(receiver).__as_raw_receiver();
+
+ // SAFETY: The writeback helper is not leaked (it is dropped at the
+ // end of this scope).
+ let (args, _helper) = unsafe { A::__into_arguments(args) };
+
+ // SAFETY: Upheld by caller.
+ let ret = unsafe { MessageReceiver::send_super_message(ptr, superclass, sel, args) };
+
+ // SAFETY: The pointers are valid (or, in the case of the receiver
+ // pointer, at least valid when the message send is not `init`).
+ unsafe { Self::convert_message_return(ret, ptr, sel) }
+ }
+}
+
+//
+// MsgSendError
+//
+
+pub trait MsgSendError<Receiver, Return> {
+ /// Add an extra error argument to the argument list, call `send_message`
+ /// with that, and return an error if one occurred.
+ #[track_caller]
+ unsafe fn send_message_error<A, E>(
+ receiver: Receiver,
+ sel: Sel,
+ args: A,
+ ) -> Result<Return, Retained<E>>
+ where
+ *mut *mut E: Encode,
+ A: TupleExtender<*mut *mut E>,
+ <A as TupleExtender<*mut *mut E>>::PlusOneArgument: ConvertArguments,
+ E: ClassType;
+}
+
+// `Option<Retained<T>>` return.
+impl<Receiver, Return, MethodFamily> MsgSendError<Receiver, Retained<Return>> for MethodFamily
+where
+ MethodFamily: MsgSend<Receiver, Option<Retained<Return>>>,
+{
+ #[inline]
+ unsafe fn send_message_error<A, E>(
+ receiver: Receiver,
+ sel: Sel,
+ args: A,
+ ) -> Result<Retained<Return>, Retained<E>>
+ where
+ *mut *mut E: Encode,
+ A: TupleExtender<*mut *mut E>,
+ <A as TupleExtender<*mut *mut E>>::PlusOneArgument: ConvertArguments,
+ E: ClassType,
+ {
+ let mut err: *mut E = ptr::null_mut();
+ let args = args.add_argument(&mut err);
+ let ret = unsafe { Self::send_message(receiver, sel, args) };
+ // As per the Cocoa documentation:
+ // > Success or failure is indicated by the return value of the
+ // > method. Although Cocoa methods that indirectly return error
+ // > objects in the Cocoa error domain are guaranteed to return such
+ // > objects if the method indicates failure by directly returning
+ // > `nil` or `NO`, you should always check that the return value is
+ // > `nil` or `NO` before attempting to do anything with the `NSError`
+ // > object.
+ if let Some(ret) = ret {
+ // In this case, the error is likely not created. If it is, it is
+ // autoreleased anyhow, so it would be a waste to retain and
+ // release it here.
+ Ok(ret)
+ } else {
+ // In this case, the error has very likely been created, but has
+ // been autoreleased (as is common for "out parameters", see
+ // `src/__macro_helpers/writeback.rs`). Hence we need to retain it
+ // if we want it to live across autorelease pools.
+ //
+ // SAFETY: The message send is guaranteed to populate the error
+ // object, or leave it as NULL. The error is shared, and all
+ // holders of the error know this, so is safe to retain.
+ Err(unsafe { encountered_error(err) })
+ }
+ }
+}
+
+// Bool return.
+impl<Receiver, MethodFamily> MsgSendError<Receiver, ()> for MethodFamily
+where
+ MethodFamily: MsgSend<Receiver, bool>,
+{
+ #[inline]
+ unsafe fn send_message_error<A, E>(
+ receiver: Receiver,
+ sel: Sel,
+ args: A,
+ ) -> Result<(), Retained<E>>
+ where
+ *mut *mut E: Encode,
+ A: TupleExtender<*mut *mut E>,
+ <A as TupleExtender<*mut *mut E>>::PlusOneArgument: ConvertArguments,
+ E: ClassType,
+ {
+ let mut err: *mut E = ptr::null_mut();
+ let args = args.add_argument(&mut err);
+ let ret = unsafe { Self::send_message(receiver, sel, args) };
+ if ret {
+ Ok(())
+ } else {
+ Err(unsafe { encountered_error(err) })
+ }
+ }
+}
+
+//
+// MsgSendSuperError
+//
+
+pub trait MsgSendSuperError<Receiver, Return> {
+ type Inner: ?Sized + RefEncode;
+
+ #[track_caller]
+ unsafe fn send_super_message_error<A, E>(
+ receiver: Receiver,
+ superclass: &AnyClass,
+ sel: Sel,
+ args: A,
+ ) -> Result<Return, Retained<E>>
+ where
+ *mut *mut E: Encode,
+ A: TupleExtender<*mut *mut E>,
+ <A as TupleExtender<*mut *mut E>>::PlusOneArgument: ConvertArguments,
+ E: ClassType;
+
+ #[track_caller]
+ #[inline]
+ unsafe fn send_super_message_static_error<A, E>(
+ receiver: Receiver,
+ sel: Sel,
+ args: A,
+ ) -> Result<Return, Retained<E>>
+ where
+ Self::Inner: ClassType,
+ <Self::Inner as ClassType>::Super: ClassType,
+ *mut *mut E: Encode,
+ A: TupleExtender<*mut *mut E>,
+ <A as TupleExtender<*mut *mut E>>::PlusOneArgument: ConvertArguments,
+ E: ClassType,
+ {
+ unsafe {
+ Self::send_super_message_error(
+ receiver,
+ <Self::Inner as ClassType>::Super::class(),
+ sel,
+ args,
+ )
+ }
+ }
+}
+
+// `Option<Retained<T>>` return.
+impl<Receiver, Return, MethodFamily> MsgSendSuperError<Receiver, Retained<Return>> for MethodFamily
+where
+ MethodFamily: MsgSendSuper<Receiver, Option<Retained<Return>>>,
+{
+ type Inner = <MethodFamily as MsgSendSuper<Receiver, Option<Retained<Return>>>>::Inner;
+
+ #[inline]
+ unsafe fn send_super_message_error<A, E>(
+ receiver: Receiver,
+ superclass: &AnyClass,
+ sel: Sel,
+ args: A,
+ ) -> Result<Retained<Return>, Retained<E>>
+ where
+ *mut *mut E: Encode,
+ A: TupleExtender<*mut *mut E>,
+ <A as TupleExtender<*mut *mut E>>::PlusOneArgument: ConvertArguments,
+ E: ClassType,
+ {
+ let mut err: *mut E = ptr::null_mut();
+ let args = args.add_argument(&mut err);
+ // SAFETY: See `send_message_error`
+ let ret = unsafe { Self::send_super_message(receiver, superclass, sel, args) };
+ if let Some(ret) = ret {
+ Ok(ret)
+ } else {
+ // SAFETY: See `send_message_error`
+ Err(unsafe { encountered_error(err) })
+ }
+ }
+}
+
+// Bool return.
+impl<Receiver, MethodFamily> MsgSendSuperError<Receiver, ()> for MethodFamily
+where
+ MethodFamily: MsgSendSuper<Receiver, bool>,
+{
+ type Inner = <MethodFamily as MsgSendSuper<Receiver, bool>>::Inner;
+
+ #[inline]
+ unsafe fn send_super_message_error<A, E>(
+ receiver: Receiver,
+ superclass: &AnyClass,
+ sel: Sel,
+ args: A,
+ ) -> Result<(), Retained<E>>
+ where
+ *mut *mut E: Encode,
+ A: TupleExtender<*mut *mut E>,
+ <A as TupleExtender<*mut *mut E>>::PlusOneArgument: ConvertArguments,
+ E: ClassType,
+ {
+ let mut err: *mut E = ptr::null_mut();
+ let args = args.add_argument(&mut err);
+ // SAFETY: See `send_message_error`
+ let ret = unsafe { Self::send_super_message(receiver, superclass, sel, args) };
+ if ret {
+ Ok(())
+ } else {
+ // SAFETY: See `send_message_error`
+ Err(unsafe { encountered_error(err) })
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::mem::ManuallyDrop;
+
+ use super::*;
+
+ use crate::rc::{autoreleasepool, Allocated, PartialInit, RcTestObject, ThreadTestData};
+ use crate::runtime::{AnyObject, NSObject, NSObjectProtocol, NSZone};
+ use crate::{class, define_class, extern_methods, msg_send, test_utils, AnyThread};
+
+ #[test]
+ fn test_send_message_manuallydrop() {
+ let obj = ManuallyDrop::new(test_utils::custom_object());
+ unsafe {
+ let _: () = msg_send![obj, release];
+ };
+ // `obj` is consumed, can't use here
+ }
+
+ macro_rules! test_error_bool {
+ ($expected:expr, $($obj:tt)*) => {
+ // Succeeds
+ let res: Result<(), Retained<NSObject>> = unsafe {
+ msg_send![$($obj)*, boolAndShouldError: false, error: _]
+ };
+ assert_eq!(res, Ok(()));
+ $expected.assert_current();
+
+ // Errors
+ let res = autoreleasepool(|_pool| {
+ let res: Result<(), Retained<NSObject>> = unsafe {
+ msg_send![$($obj)*, boolAndShouldError: true, error: _]
+ };
+ let res = res.expect_err("not err");
+ $expected.alloc += 1;
+ $expected.init += 1;
+ $expected.autorelease += 1;
+ $expected.retain += 1;
+ $expected.assert_current();
+ res
+ });
+ $expected.release += 1;
+ $expected.assert_current();
+
+ drop(res);
+ $expected.release += 1;
+ $expected.drop += 1;
+ $expected.assert_current();
+ }
+ }
+
+ define_class!(
+ #[unsafe(super(RcTestObject, NSObject))]
+ #[derive(Debug, PartialEq, Eq)]
+ struct RcTestObjectSubclass;
+ );
+
+ #[cfg_attr(not(test), allow(unused))]
+ impl RcTestObjectSubclass {
+ fn new() -> Retained<Self> {
+ unsafe { msg_send![Self::class(), new] }
+ }
+ }
+
+ #[test]
+ fn test_error_bool() {
+ let mut expected = ThreadTestData::current();
+
+ let cls = RcTestObject::class();
+ test_error_bool!(expected, cls);
+
+ let obj = RcTestObject::new();
+ expected.alloc += 1;
+ expected.init += 1;
+ test_error_bool!(expected, &obj);
+
+ let obj = RcTestObjectSubclass::new();
+ expected.alloc += 1;
+ expected.init += 1;
+ test_error_bool!(expected, &obj);
+ test_error_bool!(expected, super(&obj));
+ test_error_bool!(expected, super(&obj, RcTestObjectSubclass::class()));
+ test_error_bool!(expected, super(&obj, RcTestObject::class()));
+ }
+
+ mod test_trait_disambugated {
+ use super::*;
+
+ #[allow(dead_code)]
+ trait Abc {
+ fn send_message(&self) {}
+ }
+
+ impl<T> Abc for T {}
+
+ #[test]
+ fn test_macro_still_works() {
+ let _: Retained<NSObject> = unsafe { msg_send![NSObject::class(), new] };
+ }
+ }
+
+ // `new` family
+
+ #[test]
+ fn test_new() {
+ let mut expected = ThreadTestData::current();
+ let cls = RcTestObject::class();
+
+ let _obj: Retained<AnyObject> = unsafe { msg_send![cls, new] };
+ let _obj: Option<Retained<AnyObject>> = unsafe { msg_send![cls, new] };
+ // This is just a roundabout way of calling `[__RcTestObject new]`.
+ let _obj: Retained<AnyObject> = unsafe { msg_send![super(cls, cls.metaclass()), new] };
+ let _obj: Option<Retained<AnyObject>> =
+ unsafe { msg_send![super(cls, cls.metaclass()), new] };
+
+ // `__RcTestObject` does not override `new`, so this just ends up
+ // calling `[[__RcTestObject alloc] init]` as usual.
+ let _obj: Retained<RcTestObject> =
+ unsafe { msg_send![super(cls, NSObject::class().metaclass()), new] };
+
+ expected.alloc += 5;
+ expected.init += 5;
+ expected.assert_current();
+ }
+
+ #[test]
+ fn test_new_not_on_class() {
+ let mut expected = ThreadTestData::current();
+ let obj = RcTestObject::new();
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.assert_current();
+
+ let _obj: Retained<AnyObject> = unsafe { msg_send![&obj, newMethodOnInstance] };
+ let _obj: Option<Retained<AnyObject>> = unsafe { msg_send![&obj, newMethodOnInstance] };
+ let _obj: Retained<AnyObject> =
+ unsafe { msg_send![super(&obj, RcTestObject::class()), newMethodOnInstance] };
+ let _obj: Option<Retained<AnyObject>> =
+ unsafe { msg_send![super(&obj, RcTestObject::class()), newMethodOnInstance] };
+ expected.alloc += 4;
+ expected.init += 4;
+ expected.assert_current();
+ }
+
+ #[test]
+ // newScriptingObjectOfClass only available on macOS
+ #[cfg_attr(not(all(target_vendor = "apple", target_os = "macos")), ignore)]
+ fn test_new_with_args() {
+ let mut expected = ThreadTestData::current();
+
+ let object_class = RcTestObject::class();
+ let key: Retained<AnyObject> = unsafe { msg_send![class!(NSString), new] };
+ let contents_value: *const AnyObject = ptr::null();
+ let properties: Retained<AnyObject> = unsafe { msg_send![class!(NSDictionary), new] };
+
+ let _obj: Option<Retained<AnyObject>> = unsafe {
+ msg_send![
+ NSObject::class(),
+ newScriptingObjectOfClass: object_class,
+ forValueForKey: &*key,
+ withContentsValue: contents_value,
+ properties: &*properties,
+ ]
+ };
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.assert_current();
+ }
+
+ #[test]
+ #[should_panic = "failed creating new instance of NSValue"]
+ // GNUStep instead returns an invalid instance that panics on accesses
+ #[cfg_attr(feature = "gnustep-1-7", ignore)]
+ fn new_nsvalue_fails() {
+ let _val: Retained<AnyObject> = unsafe { msg_send![class!(NSValue), new] };
+ }
+
+ #[test]
+ #[should_panic = "failed creating new instance using +[__RcTestObject newReturningNull]"]
+ fn test_new_with_null() {
+ let _obj: Retained<RcTestObject> =
+ unsafe { msg_send![RcTestObject::class(), newReturningNull] };
+ }
+
+ #[test]
+ #[should_panic = "failed creating new instance using +[__RcTestObject newReturningNull]"]
+ fn test_super_new_with_null() {
+ let _: Retained<RcTestObject> = unsafe {
+ msg_send![
+ super(RcTestObject::class(), RcTestObject::class().metaclass()),
+ newReturningNull
+ ]
+ };
+ }
+
+ #[test]
+ #[should_panic = "unexpected NULL returned from -[__RcTestObject newMethodOnInstanceNull]"]
+ fn test_new_any_with_null() {
+ let obj = RcTestObject::new();
+ let _obj: Retained<AnyObject> = unsafe { msg_send![&obj, newMethodOnInstanceNull] };
+ }
+
+ #[test]
+ #[should_panic = "unexpected NULL returned from -[__RcTestObject newMethodOnInstanceNull]"]
+ fn test_super_new_any_with_null() {
+ let obj = RcTestObject::new();
+ let _obj: Retained<AnyObject> =
+ unsafe { msg_send![super(&obj, RcTestObject::class()), newMethodOnInstanceNull] };
+ }
+
+ #[test]
+ #[cfg_attr(
+ debug_assertions,
+ should_panic = "messsaging newMethodOnInstance to nil"
+ )]
+ #[cfg_attr(
+ not(debug_assertions),
+ ignore = "unexpected NULL newMethodOnInstance; receiver was NULL"
+ )]
+ fn test_new_any_with_null_receiver() {
+ let obj: *const NSObject = ptr::null();
+ let _obj: Retained<AnyObject> = unsafe { msg_send![obj, newMethodOnInstance] };
+ }
+
+ #[test]
+ #[cfg_attr(
+ debug_assertions,
+ should_panic = "messsaging newMethodOnInstance to nil"
+ )]
+ #[cfg_attr(
+ not(debug_assertions),
+ ignore = "unexpected NULL newMethodOnInstance; receiver was NULL"
+ )]
+ fn test_super_new_any_with_null_receiver() {
+ let obj: *const RcTestObject = ptr::null();
+ let _obj: Retained<AnyObject> = unsafe { msg_send![super(obj), newMethodOnInstance] };
+ }
+
+ // `alloc` family
+
+ #[test]
+ fn test_alloc() {
+ let mut expected = ThreadTestData::current();
+ let cls = RcTestObject::class();
+
+ let obj: Allocated<RcTestObject> = unsafe { msg_send![cls, alloc] };
+ expected.alloc += 1;
+ expected.assert_current();
+
+ drop(obj);
+ expected.release += 1;
+ // Drop flag ensures uninitialized do not Drop
+ // expected.drop += 1;
+ expected.assert_current();
+
+ // `+[NSObject alloc]` forwards to `allocWithZone:`, so this still
+ // allocates a `__RcTestObject`.
+ let _: Allocated<NSObject> =
+ unsafe { msg_send![super(cls, NSObject::class().metaclass()), alloc] };
+ expected.alloc += 1;
+ expected.release += 1;
+ // Drop flag ensures uninitialized do not Drop
+ // expected.drop += 1;
+ expected.assert_current();
+ }
+
+ #[test]
+ fn test_alloc_with_zone() {
+ let mut expected = ThreadTestData::current();
+ let cls = RcTestObject::class();
+ let zone: *const NSZone = ptr::null();
+
+ let _obj: Allocated<RcTestObject> = unsafe { msg_send![cls, allocWithZone: zone] };
+ expected.alloc += 1;
+ expected.assert_current();
+
+ let _obj: Allocated<RcTestObject> =
+ unsafe { msg_send![super(cls, cls.metaclass()), allocWithZone: zone] };
+ expected.alloc += 1;
+ expected.assert_current();
+
+ let _obj: Allocated<NSObject> =
+ unsafe { msg_send![super(cls, NSObject::class().metaclass()), allocWithZone: zone] };
+ expected.assert_current();
+ }
+
+ #[test]
+ fn test_alloc_with_null() {
+ let obj: Allocated<RcTestObject> =
+ unsafe { msg_send![RcTestObject::class(), allocReturningNull] };
+ assert!(Allocated::as_ptr(&obj).is_null());
+ }
+
+ // `init` family
+
+ #[test]
+ fn test_init() {
+ let mut expected = ThreadTestData::current();
+
+ let _: Retained<RcTestObject> = unsafe { msg_send![RcTestObject::alloc(), init] };
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+
+ let obj = RcTestObject::alloc().set_ivars(());
+ let _: Retained<RcTestObject> = unsafe { msg_send![super(obj), init] };
+ expected.alloc += 1;
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+
+ // Check allocation error before init
+ let obj = RcTestObject::alloc();
+ expected.alloc += 1;
+ assert!(!Allocated::as_ptr(&obj).is_null());
+ let _: Retained<RcTestObject> = unsafe { msg_send![obj, init] };
+ expected.init += 1;
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+ }
+
+ #[test]
+ #[should_panic = "failed initializing object with -initReturningNull"]
+ fn test_init_with_null() {
+ let obj: Allocated<RcTestObject> = unsafe { msg_send![RcTestObject::class(), alloc] };
+ let _obj: Retained<RcTestObject> = unsafe { msg_send![obj, initReturningNull] };
+ }
+
+ #[test]
+ #[cfg_attr(debug_assertions, should_panic = "messsaging init to nil")]
+ #[cfg_attr(not(debug_assertions), ignore = "failed allocating object")]
+ fn test_init_with_null_receiver() {
+ let obj: Allocated<RcTestObject> =
+ unsafe { msg_send![RcTestObject::class(), allocReturningNull] };
+ let _obj: Retained<RcTestObject> = unsafe { msg_send![obj, init] };
+ }
+
+ #[test]
+ #[should_panic = "tried to initialize ivars after they were already initialized"]
+ #[cfg_attr(not(debug_assertions), ignore = "only checked with debug assertions")]
+ fn test_super_init_not_initialized() {
+ let obj = RcTestObject::alloc().set_ivars(());
+ let _: Retained<RcTestObject> =
+ unsafe { msg_send![super(obj, RcTestObject::class()), init] };
+ }
+
+ #[test]
+ #[should_panic = "tried to finalize an already finalized object"]
+ #[cfg_attr(not(debug_assertions), ignore = "only checked with debug assertions")]
+ fn test_super_init_not_finalized() {
+ let obj = unsafe { PartialInit::new(Allocated::into_ptr(RcTestObject::alloc())) };
+ let _: Retained<RcTestObject> =
+ unsafe { msg_send![super(obj, RcTestObject::class()), init] };
+ }
+
+ // `copy` family
+
+ #[test]
+ fn test_copy() {
+ let obj = RcTestObject::new();
+ let mut expected = ThreadTestData::current();
+
+ let _: Retained<RcTestObject> = unsafe { msg_send![&obj, copy] };
+ expected.copy += 1;
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+
+ // `+[NSObject copy]` forwards to `copyWithZone:`, so this still
+ // creates a `__RcTestObject`.
+ let _: Retained<NSObject> = unsafe { msg_send![super(&obj), copy] };
+ expected.copy += 1;
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+ }
+
+ #[test]
+ #[should_panic = "failed copying object"]
+ fn test_copy_with_null() {
+ let obj = RcTestObject::new();
+ let _obj: Retained<RcTestObject> = unsafe { msg_send![&obj, copyReturningNull] };
+ }
+
+ #[test]
+ #[should_panic = "failed copying object"]
+ fn test_super_copy_with_null() {
+ let obj = RcTestObject::new();
+ let _obj: Retained<RcTestObject> =
+ unsafe { msg_send![super(&obj, RcTestObject::class()), copyReturningNull] };
+ }
+
+ // `mutableCopy` family
+
+ #[test]
+ fn test_mutable_copy() {
+ let obj = RcTestObject::new();
+ let mut expected = ThreadTestData::current();
+
+ let _: Retained<RcTestObject> = unsafe { msg_send![&obj, mutableCopy] };
+ expected.mutable_copy += 1;
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+
+ // `+[NSObject mutableCopy]` forwards to `mutableCopyWithZone:`, so
+ // this still creates a `__RcTestObject`.
+ let _: Retained<NSObject> = unsafe { msg_send![super(&obj), mutableCopy] };
+ expected.mutable_copy += 1;
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+ }
+
+ // No method family
+
+ #[test]
+ fn test_normal() {
+ let obj = RcTestObject::new();
+ let mut expected = ThreadTestData::current();
+
+ let _: Retained<RcTestObject> = unsafe { msg_send![&obj, self] };
+ expected.retain += 1;
+ expected.release += 1;
+ expected.assert_current();
+
+ let _: Retained<RcTestObject> = unsafe { msg_send![super(&obj), self] };
+ expected.retain += 1;
+ expected.release += 1;
+ expected.assert_current();
+
+ let _: Option<Retained<RcTestObject>> = unsafe { msg_send![&obj, description] };
+ expected.assert_current();
+
+ let _: Option<Retained<RcTestObject>> = unsafe { msg_send![super(&obj), description] };
+ expected.assert_current();
+ }
+
+ #[test]
+ #[should_panic = "unexpected NULL returned from -[__RcTestObject methodReturningNull]"]
+ fn test_normal_with_null() {
+ let obj = RcTestObject::new();
+ let _obj: Retained<RcTestObject> = unsafe { msg_send![&obj, methodReturningNull] };
+ }
+
+ #[test]
+ #[should_panic = "unexpected NULL returned from -[__RcTestObject aMethod:]"]
+ fn test_normal_with_param_and_null() {
+ let obj = RcTestObject::new();
+ let _obj: Retained<RcTestObject> = unsafe { msg_send![&obj, aMethod: false] };
+ }
+
+ #[test]
+ #[cfg_attr(debug_assertions, should_panic = "messsaging description to nil")]
+ #[cfg_attr(
+ not(debug_assertions),
+ ignore = "unexpected NULL description; receiver was NULL"
+ )]
+ fn test_normal_with_null_receiver() {
+ let obj: *const NSObject = ptr::null();
+ let _obj: Retained<AnyObject> = unsafe { msg_send![obj, description] };
+ }
+
+ /// This is imperfect, but will do for now.
+ const fn autorelease_skipped(self_declared: bool) -> bool {
+ if cfg!(feature = "gnustep-1-7") {
+ // GNUStep does the optimization a different way, so it isn't
+ // optimization-dependent.
+ true
+ } else if cfg!(all(target_arch = "arm", panic = "unwind")) {
+ // 32-bit ARM unwinding sometimes interferes with the optimization
+ false
+ } else if self_declared {
+ // FIXME: Autorelease_return is not currently tail-called, so the
+ // optimization doesn't work on define_class! functions.
+ false
+ } else if cfg!(feature = "catch-all") {
+ // FIXME: `catch-all` is inserted before we get a chance to retain.
+ false
+ } else if cfg!(debug_assertions) {
+ // `debug_assertions` ~proxy for if optimizations are off.
+ false
+ } else {
+ true
+ }
+ }
+
+ macro_rules! test_error_retained {
+ ($expected:expr, $if_autorelease_not_skipped:expr, $sel:ident, $($obj:tt)*) => {
+ // Succeeds
+ let res = autoreleasepool(|_pool| {
+ let res: Result<Retained<RcTestObject>, Retained<NSObject>> = unsafe {
+ msg_send![$($obj)*, $sel: false, error: _]
+ };
+ let res = res.expect("not ok");
+ $expected.alloc += 1;
+ $expected.init += 1;
+ $expected.autorelease += $if_autorelease_not_skipped;
+ $expected.retain += $if_autorelease_not_skipped;
+ $expected.assert_current();
+ res
+ });
+ $expected.release += $if_autorelease_not_skipped;
+ $expected.assert_current();
+
+ drop(res);
+ $expected.release += 1;
+ $expected.drop += 1;
+ $expected.assert_current();
+
+ // Errors
+ let res = autoreleasepool(|_pool| {
+ let res: Result<Retained<RcTestObject>, Retained<NSObject>> = unsafe {
+ msg_send![$($obj)*, $sel: true, error: _]
+ };
+ $expected.alloc += 1;
+ $expected.init += 1;
+ $expected.autorelease += 1;
+ $expected.retain += 1;
+ $expected.assert_current();
+ res.expect_err("not err")
+ });
+ $expected.release += 1;
+ $expected.assert_current();
+
+ drop(res);
+ $expected.release += 1;
+ $expected.drop += 1;
+ $expected.assert_current();
+ }
+ }
+
+ #[test]
+ fn test_error_retained() {
+ let mut expected = ThreadTestData::current();
+
+ let cls = RcTestObject::class();
+ test_error_retained!(
+ expected,
+ if autorelease_skipped(true) { 0 } else { 1 },
+ idAndShouldError,
+ cls
+ );
+ test_error_retained!(expected, 0, newAndShouldError, cls);
+
+ let obj = RcTestObject::new();
+ expected.alloc += 1;
+ expected.init += 1;
+ test_error_retained!(
+ expected,
+ if autorelease_skipped(true) { 0 } else { 1 },
+ idAndShouldError,
+ &obj
+ );
+
+ expected.alloc -= 1;
+ expected.release -= 1;
+ test_error_retained!(expected, 0, initAndShouldError, {
+ expected.alloc += 1;
+ expected.release += 1;
+ // Drop flag ensures newly allocated objects do not drop
+ // expected.drop += 1;
+ RcTestObject::alloc()
+ });
+ }
+
+ #[test]
+ fn test_method_with_param() {
+ let mut expected = ThreadTestData::current();
+
+ let obj = RcTestObject::new();
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.assert_current();
+
+ let res: Option<Retained<RcTestObject>> = unsafe { msg_send![&obj, aMethod: false] };
+ assert!(res.is_none());
+ expected.assert_current();
+
+ let _res = autoreleasepool(|_pool| {
+ let res: Option<Retained<RcTestObject>> = unsafe { msg_send![&obj, aMethod: true] };
+ assert!(res.is_some());
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.autorelease += if autorelease_skipped(true) { 0 } else { 1 };
+ expected.retain += if autorelease_skipped(true) { 0 } else { 1 };
+ expected.assert_current();
+ res
+ });
+ expected.release += if autorelease_skipped(true) { 0 } else { 1 };
+ expected.assert_current();
+ }
+
+ fn create_obj() -> Retained<NSObject> {
+ let obj = ManuallyDrop::new(NSObject::new());
+ unsafe {
+ let obj: *mut NSObject = msg_send![&*obj, autorelease];
+ // All code between the `msg_send!` and the `retain_autoreleased`
+ // must be able to be optimized away for this to work.
+ Retained::retain_autoreleased(obj).unwrap()
+ }
+ }
+
+ #[test]
+ fn test_retain_autoreleased() {
+ autoreleasepool(|_| {
+ // Run once to allow DYLD to resolve the symbol stubs.
+ // Required for making `retain_autoreleased` work on x86_64.
+ let _data = create_obj();
+
+ // When compiled in release mode / with optimizations enabled,
+ // subsequent usage of `retain_autoreleased` will succeed in
+ // retaining the autoreleased value!
+ let expected = if autorelease_skipped(false) { 1 } else { 2 };
+
+ let data = create_obj();
+ assert_eq!(data.retainCount(), expected);
+
+ let data = create_obj();
+ assert_eq!(data.retainCount(), expected);
+
+ // Here we manually clean up the autorelease, so it will always be 1.
+ let data = autoreleasepool(|_| create_obj());
+ assert_eq!(data.retainCount(), 1);
+ });
+ }
+
+ #[test]
+ fn msg_send_class() {
+ let cls = NSObject::class();
+
+ let retained: Retained<AnyClass> = unsafe { msg_send![cls, self] };
+ assert_eq!(&*retained, cls);
+
+ let retained: Option<Retained<AnyClass>> = unsafe { msg_send![cls, self] };
+ let retained = retained.unwrap();
+ assert_eq!(&*retained, cls);
+ }
+
+ impl RcTestObject {
+ extern_methods!(
+ #[unsafe(method(copy))]
+ #[unsafe(method_family = new)]
+ fn copy_new(&self) -> Retained<Self>;
+
+ #[unsafe(method(copy))]
+ #[unsafe(method_family = init)]
+ fn copy_init(this: Allocated<Self>) -> Retained<Self>;
+
+ #[unsafe(method(copy))]
+ #[unsafe(method_family = copy)]
+ fn copy_copy(&self) -> Retained<Self>;
+
+ #[unsafe(method(copy))]
+ #[unsafe(method_family = mutableCopy)]
+ fn copy_mutable_copy(&self) -> Retained<Self>;
+
+ #[unsafe(method(copy))]
+ #[unsafe(method_family = none)]
+ fn copy_none(&self) -> Retained<Self>;
+ );
+ }
+
+ #[test]
+ fn test_method_family() {
+ // Test a few combinations of (incorrect) method families.
+ let obj = RcTestObject::new();
+ let mut expected = ThreadTestData::current();
+
+ let copy = obj.copy_new();
+ expected.copy += 1;
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.assert_current();
+ drop(copy);
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+
+ let alloc = RcTestObject::alloc();
+ let ptr = Allocated::as_ptr(&alloc);
+ expected.alloc += 1;
+ expected.assert_current();
+ let copy = RcTestObject::copy_init(alloc);
+ expected.copy += 1;
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.assert_current();
+ drop(copy);
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+ drop(unsafe { Allocated::new(ptr.cast_mut()) });
+ expected.release += 1;
+ expected.assert_current();
+
+ let copy = obj.copy_copy();
+ expected.copy += 1;
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.assert_current();
+ drop(copy);
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+
+ let copy = obj.copy_mutable_copy();
+ expected.copy += 1;
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.assert_current();
+ drop(copy);
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+
+ let copy = obj.copy_none();
+ expected.copy += 1;
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.retain += 1;
+ expected.assert_current();
+ // SAFETY: Wrong method family specified, so we have +1 retain count
+ // in excess.
+ drop(unsafe { Retained::from_raw(Retained::as_ptr(©).cast_mut()) });
+ expected.release += 1;
+ expected.assert_current();
+ drop(copy);
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+ }
+}
diff --git a/third_party/rust/objc2/src/__macro_helpers/null_error.rs b/third_party/rust/objc2/src/__macro_helpers/null_error.rs
@@ -0,0 +1,137 @@
+use core::ffi::CStr;
+use std::sync::OnceLock;
+
+use crate::ffi::NSInteger;
+use crate::rc::{autoreleasepool, Retained};
+use crate::runtime::{AnyClass, NSObject};
+use crate::{msg_send, ClassType};
+
+// Marked `#[cold]` to tell the optimizer that errors are comparatively rare.
+//
+// And intentionally not `#[inline]`, we'll let the optimizer figure out if it
+// wants to do that or not.
+#[cold]
+pub(crate) unsafe fn encountered_error<E: ClassType>(err: *mut E) -> Retained<E> {
+ // SAFETY: Caller ensures that the pointer is valid.
+ unsafe { Retained::retain(err) }.unwrap_or_else(|| {
+ let err = null_error();
+ assert!(E::IS_NSERROR_COMPATIBLE);
+ // SAFETY: Just checked (via `const` assertion) that the `E` type is
+ // either `NSError` or `NSObject`, and hence it is valid to cast the
+ // `NSObject` that we have here to that.
+ unsafe { Retained::cast_unchecked(err) }
+ })
+}
+
+/// Poor mans string equality in `const`. Implements `a == b`.
+const fn is_eq(a: &str, b: &str) -> bool {
+ let a = a.as_bytes();
+ let b = b.as_bytes();
+
+ if a.len() != b.len() {
+ return false;
+ }
+
+ let mut i = 0;
+ while i < a.len() {
+ if a[i] != b[i] {
+ return false;
+ }
+ i += 1;
+ }
+
+ true
+}
+
+// TODO: Use inline `const` once in MSRV (or add proper trait bounds).
+trait IsNSError {
+ const IS_NSERROR_COMPATIBLE: bool;
+}
+
+impl<T: ClassType> IsNSError for T {
+ const IS_NSERROR_COMPATIBLE: bool = {
+ if is_eq(T::NAME, "NSError") || is_eq(T::NAME, "NSObject") {
+ true
+ } else {
+ // The post monomorphization error here is not nice, but it's
+ // better than UB because the user used a type that cannot be
+ // converted to NSError.
+ //
+ // TODO: Add a trait bound or similar instead.
+ panic!("error parameter must be either `NSError` or `NSObject`")
+ }
+ };
+}
+
+#[cold] // Mark the NULL error branch as cold
+fn null_error() -> Retained<NSObject> {
+ static CACHED_NULL_ERROR: OnceLock<NSErrorWrapper> = OnceLock::new();
+
+ // We use a OnceLock here, since performance doesn't really matter, and
+ // using an AtomicPtr would leak under (very) high initialization
+ // contention.
+ CACHED_NULL_ERROR.get_or_init(create_null_error).0.clone()
+}
+
+struct NSErrorWrapper(Retained<NSObject>);
+
+// SAFETY: NSError is immutable and thread safe.
+unsafe impl Send for NSErrorWrapper {}
+unsafe impl Sync for NSErrorWrapper {}
+
+#[cold] // Mark the error creation branch as cold
+fn create_null_error() -> NSErrorWrapper {
+ // Wrap creation in an autoreleasepool, since we don't know anything about
+ // the outside world, and we don't want to appear to leak.
+ autoreleasepool(|_| {
+ // TODO: Replace with c string literals once in MSRV.
+
+ // SAFETY: The string is NUL terminated.
+ let cls = unsafe { CStr::from_bytes_with_nul_unchecked(b"NSString\0") };
+ // Intentional dynamic lookup, we don't know if Foundation is linked.
+ let cls = AnyClass::get(cls).unwrap_or_else(foundation_not_linked);
+
+ // SAFETY: The string is NUL terminated.
+ let domain = unsafe { CStr::from_bytes_with_nul_unchecked(b"__objc2.missingError\0") };
+ // SAFETY: The signate is correct, and the string is UTF-8 encoded and
+ // NUL terminated.
+ let domain: Retained<NSObject> =
+ unsafe { msg_send![cls, stringWithUTF8String: domain.as_ptr()] };
+
+ // SAFETY: The string is valid.
+ let cls = unsafe { CStr::from_bytes_with_nul_unchecked(b"NSError\0") };
+ // Intentional dynamic lookup, we don't know if Foundation is linked.
+ let cls = AnyClass::get(cls).unwrap_or_else(foundation_not_linked);
+
+ let domain: &NSObject = &domain;
+ let code: NSInteger = 0;
+ let user_info: Option<&NSObject> = None;
+ // SAFETY: The signate is correct.
+ let err: Retained<NSObject> =
+ unsafe { msg_send![cls, errorWithDomain: domain, code: code, userInfo: user_info] };
+ NSErrorWrapper(err)
+ })
+}
+
+fn foundation_not_linked() -> &'static AnyClass {
+ panic!("Foundation must be linked to get a proper error message on NULL errors")
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_is_eq() {
+ assert!(is_eq("NSError", "NSError"));
+ assert!(!is_eq("nserror", "NSError"));
+ assert!(!is_eq("CFError", "NSError"));
+ assert!(!is_eq("NSErr", "NSError"));
+ assert!(!is_eq("NSErrorrrr", "NSError"));
+ }
+
+ #[test]
+ fn test_create() {
+ let _ = create_null_error().0;
+ }
+}
diff --git a/third_party/rust/objc2/src/__macro_helpers/os_version.rs b/third_party/rust/objc2/src/__macro_helpers/os_version.rs
@@ -0,0 +1,429 @@
+//! Utilities for checking the runtime availability of APIs.
+//!
+//! TODO: Upstream some of this to `std`?
+use core::cmp::Ordering;
+use core::fmt;
+
+#[cfg(target_vendor = "apple")]
+mod apple;
+
+/// The size of the fields here are limited by Mach-O's `LC_BUILD_VERSION`.
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct OSVersion {
+ // Shuffle the versions around a little so that OSVersion has the same bit
+ // representation as the `u32` returned from `to_u32`, allowing
+ // comparisons to compile down to just between two `u32`s.
+ #[cfg(target_endian = "little")]
+ pub patch: u8,
+ #[cfg(target_endian = "little")]
+ pub minor: u8,
+ #[cfg(target_endian = "little")]
+ pub major: u16,
+
+ #[cfg(target_endian = "big")]
+ pub major: u16,
+ #[cfg(target_endian = "big")]
+ pub minor: u8,
+ #[cfg(target_endian = "big")]
+ pub patch: u8,
+}
+
+#[track_caller]
+const fn parse_usize(mut bytes: &[u8]) -> (usize, &[u8]) {
+ // Ensure we have at least one digit (that is not just a period).
+ let mut ret: usize = if let Some((&ascii, rest)) = bytes.split_first() {
+ bytes = rest;
+
+ match ascii {
+ b'0'..=b'9' => (ascii - b'0') as usize,
+ _ => panic!("found invalid digit when parsing version"),
+ }
+ } else {
+ panic!("found empty version number part")
+ };
+
+ // Parse the remaining digits.
+ while let Some((&ascii, rest)) = bytes.split_first() {
+ let digit = match ascii {
+ b'0'..=b'9' => ascii - b'0',
+ _ => break,
+ };
+
+ bytes = rest;
+
+ // This handles leading zeroes as well.
+ match ret.checked_mul(10) {
+ Some(val) => match val.checked_add(digit as _) {
+ Some(val) => ret = val,
+ None => panic!("version is too large"),
+ },
+ None => panic!("version is too large"),
+ };
+ }
+
+ (ret, bytes)
+}
+
+impl OSVersion {
+ const MIN: Self = Self {
+ major: 0,
+ minor: 0,
+ patch: 0,
+ };
+
+ const MAX: Self = Self {
+ major: u16::MAX,
+ minor: u8::MAX,
+ patch: u8::MAX,
+ };
+
+ /// Parse the version from a string at `const` time.
+ #[track_caller]
+ pub const fn from_str(version: &str) -> Self {
+ Self::from_bytes(version.as_bytes())
+ }
+
+ #[track_caller]
+ pub(crate) const fn from_bytes(bytes: &[u8]) -> Self {
+ let (major, bytes) = parse_usize(bytes);
+ if major > u16::MAX as usize {
+ panic!("major version is too large");
+ }
+ let major = major as u16;
+
+ let bytes = if let Some((period, bytes)) = bytes.split_first() {
+ if *period != b'.' {
+ panic!("expected period between major and minor version")
+ }
+ bytes
+ } else {
+ return Self {
+ major,
+ minor: 0,
+ patch: 0,
+ };
+ };
+
+ let (minor, bytes) = parse_usize(bytes);
+ if minor > u8::MAX as usize {
+ panic!("minor version is too large");
+ }
+ let minor = minor as u8;
+
+ let bytes = if let Some((period, bytes)) = bytes.split_first() {
+ if *period != b'.' {
+ panic!("expected period after minor version")
+ }
+ bytes
+ } else {
+ return Self {
+ major,
+ minor,
+ patch: 0,
+ };
+ };
+
+ let (patch, bytes) = parse_usize(bytes);
+ if patch > u8::MAX as usize {
+ panic!("patch version is too large");
+ }
+ let patch = patch as u8;
+
+ if !bytes.is_empty() {
+ panic!("too many parts to version");
+ }
+
+ Self {
+ major,
+ minor,
+ patch,
+ }
+ }
+
+ /// Pack the version into a `u32`.
+ ///
+ /// This is used for faster comparisons.
+ #[inline]
+ pub const fn to_u32(self) -> u32 {
+ // See comments in `OSVersion`, this should compile down to nothing.
+ let (major, minor, patch) = (self.major as u32, self.minor as u32, self.patch as u32);
+ (major << 16) | (minor << 8) | patch
+ }
+
+ /// Construct the version from a `u32`.
+ #[inline]
+ pub const fn from_u32(version: u32) -> Self {
+ // See comments in `OSVersion`, this should compile down to nothing.
+ let major = (version >> 16) as u16;
+ let minor = (version >> 8) as u8;
+ let patch = version as u8;
+ Self {
+ major,
+ minor,
+ patch,
+ }
+ }
+}
+
+impl PartialEq for OSVersion {
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ self.to_u32() == other.to_u32()
+ }
+}
+
+impl PartialOrd for OSVersion {
+ #[inline]
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ self.to_u32().partial_cmp(&other.to_u32())
+ }
+}
+
+impl fmt::Debug for OSVersion {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Same ordering on little and big endian.
+ f.debug_struct("OSVersion")
+ .field("major", &self.major)
+ .field("minor", &self.minor)
+ .field("patch", &self.patch)
+ .finish()
+ }
+}
+
+/// The combined availability.
+///
+/// This generally works closely together with the `available!` macro to make
+/// syntax checking inside that easier.
+#[derive(Clone, Copy, Debug)]
+pub struct AvailableVersion {
+ pub macos: OSVersion,
+ pub ios: OSVersion,
+ pub tvos: OSVersion,
+ pub watchos: OSVersion,
+ pub visionos: OSVersion,
+ #[doc(hidden)]
+ pub __others: OSVersion,
+}
+
+impl AvailableVersion {
+ pub const MIN: Self = Self {
+ macos: OSVersion::MIN,
+ ios: OSVersion::MIN,
+ tvos: OSVersion::MIN,
+ watchos: OSVersion::MIN,
+ visionos: OSVersion::MIN,
+ __others: OSVersion::MIN,
+ };
+
+ pub const MAX: Self = Self {
+ macos: OSVersion::MAX,
+ ios: OSVersion::MAX,
+ tvos: OSVersion::MAX,
+ watchos: OSVersion::MAX,
+ visionos: OSVersion::MAX,
+ __others: OSVersion::MAX,
+ };
+}
+
+#[inline]
+pub fn is_available(version: AvailableVersion) -> bool {
+ let version = if cfg!(target_os = "macos") {
+ version.macos
+ } else if cfg!(target_os = "ios") {
+ version.ios
+ } else if cfg!(target_os = "tvos") {
+ version.tvos
+ } else if cfg!(target_os = "watchos") {
+ version.watchos
+ } else if cfg!(target_os = "visionos") {
+ version.visionos
+ } else {
+ version.__others
+ };
+
+ // In the special case that `version` was set to `OSVersion::MAX`, we
+ // assume that there can never be an OS version that large, and hence we
+ // want to avoid checking at all.
+ //
+ // This is useful for platforms where the version hasn't been specified.
+ if version == OSVersion::MAX {
+ return false;
+ }
+
+ #[cfg(target_vendor = "apple")]
+ {
+ // If the deployment target is high enough, the API is always available.
+ //
+ // This check should be optimized away at compile time.
+ if version <= apple::DEPLOYMENT_TARGET {
+ return true;
+ }
+
+ // Otherwise, compare against the version at runtime.
+ version <= apple::current_version()
+ }
+
+ #[cfg(not(target_vendor = "apple"))]
+ return true;
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use crate::{__available_version, available};
+
+ #[test]
+ fn test_parse() {
+ #[track_caller]
+ fn check(expected: (u16, u8, u8), actual: OSVersion) {
+ assert_eq!(
+ OSVersion {
+ major: expected.0,
+ minor: expected.1,
+ patch: expected.2,
+ },
+ actual,
+ )
+ }
+
+ check((1, 0, 0), __available_version!(1));
+ check((1, 2, 0), __available_version!(1.2));
+ check((1, 2, 3), __available_version!(1.2.3));
+ check((9999, 99, 99), __available_version!(9999.99.99));
+
+ // Ensure that the macro handles leading zeroes correctly
+ check((10, 0, 0), __available_version!(010));
+ check((10, 20, 0), __available_version!(010.020));
+ check((10, 20, 30), __available_version!(010.020.030));
+ check(
+ (10000, 100, 100),
+ __available_version!(000010000.00100.00100),
+ );
+ }
+
+ #[test]
+ fn test_compare() {
+ #[track_caller]
+ fn check_lt(expected: (u16, u8, u8), actual: (u16, u8, u8)) {
+ assert!(
+ OSVersion {
+ major: expected.0,
+ minor: expected.1,
+ patch: expected.2,
+ } < OSVersion {
+ major: actual.0,
+ minor: actual.1,
+ patch: actual.2,
+ },
+ )
+ }
+
+ check_lt((4, 99, 99), (5, 5, 5));
+ check_lt((5, 4, 99), (5, 5, 5));
+ check_lt((5, 5, 4), (5, 5, 5));
+
+ check_lt((10, 7, 0), (10, 10, 0));
+ }
+
+ #[test]
+ #[should_panic = "too many parts to version"]
+ fn test_too_many_version_parts() {
+ let _ = __available_version!(1.2.3 .4);
+ }
+
+ #[test]
+ #[should_panic = "found invalid digit when parsing version"]
+ fn test_macro_with_identifiers() {
+ let _ = __available_version!(A.B);
+ }
+
+ #[test]
+ #[should_panic = "found empty version number part"]
+ fn test_empty_version() {
+ let _ = __available_version!();
+ }
+
+ #[test]
+ #[should_panic = "found invalid digit when parsing version"]
+ fn test_only_period() {
+ let _ = __available_version!(.);
+ }
+
+ #[test]
+ #[should_panic = "found invalid digit when parsing version"]
+ fn test_has_leading_period() {
+ let _ = __available_version!(.1);
+ }
+
+ #[test]
+ #[should_panic = "found empty version number part"]
+ fn test_has_trailing_period() {
+ let _ = __available_version!(1.);
+ }
+
+ #[test]
+ #[should_panic = "major version is too large"]
+ fn test_major_too_large() {
+ let _ = __available_version!(100000);
+ }
+
+ #[test]
+ #[should_panic = "minor version is too large"]
+ fn test_minor_too_large() {
+ let _ = __available_version!(1.1000);
+ }
+
+ #[test]
+ #[should_panic = "patch version is too large"]
+ fn test_patch_too_large() {
+ let _ = __available_version!(1.1.1000);
+ }
+
+ #[test]
+ fn test_general_available() {
+ // Always available
+ assert!(available!(..));
+
+ // Never available
+ assert!(!available!());
+
+ // Low versions, always available
+ assert!(available!(
+ macos = 10.0,
+ ios = 1.0,
+ tvos = 1.0,
+ watchos = 1.0,
+ visionos = 1.0,
+ ..
+ ));
+
+ // High versions, never available
+ assert!(!available!(
+ macos = 99,
+ ios = 99,
+ tvos = 99,
+ watchos = 99,
+ visionos = 99
+ ));
+
+ if !cfg!(target_os = "tvos") {
+ // Available nowhere except tvOS
+ assert!(!available!(tvos = 1.2));
+
+ // Available everywhere, except low tvOS versions
+ assert!(available!(tvos = 1.2, ..));
+ }
+ }
+
+ #[test]
+ fn test_u32_roundtrip() {
+ let version = OSVersion {
+ major: 1000,
+ minor: 100,
+ patch: 200,
+ };
+ assert_eq!(version, OSVersion::from_u32(version.to_u32()));
+ }
+}
diff --git a/third_party/rust/objc2/src/__macro_helpers/os_version/apple.rs b/third_party/rust/objc2/src/__macro_helpers/os_version/apple.rs
@@ -0,0 +1,562 @@
+//! Heavily copied from:
+//! <https://github.com/rust-lang/rust/pull/138944>
+//!
+//! Once in MSRV, we should be able to replace this with just using that
+//! symbol.
+use core::ffi::{c_char, c_int, c_uint, c_void, CStr};
+use core::ptr::null_mut;
+use core::sync::atomic::{AtomicU32, Ordering};
+use std::env;
+use std::ffi::CString;
+use std::fs;
+use std::os::unix::ffi::OsStringExt;
+use std::path::{Path, PathBuf};
+
+use super::OSVersion;
+
+/// The deployment target for the current OS.
+pub(crate) const DEPLOYMENT_TARGET: OSVersion = {
+ // Intentionally use `#[cfg]` guards instead of `cfg!` here, to avoid
+ // recompiling when unrelated environment variables change.
+ #[cfg(target_os = "macos")]
+ let var = option_env!("MACOSX_DEPLOYMENT_TARGET");
+ #[cfg(target_os = "ios")] // Also used on Mac Catalyst.
+ let var = option_env!("IPHONEOS_DEPLOYMENT_TARGET");
+ #[cfg(target_os = "tvos")]
+ let var = option_env!("TVOS_DEPLOYMENT_TARGET");
+ #[cfg(target_os = "watchos")]
+ let var = option_env!("WATCHOS_DEPLOYMENT_TARGET");
+ #[cfg(target_os = "visionos")]
+ let var = option_env!("XROS_DEPLOYMENT_TARGET");
+
+ if let Some(var) = var {
+ OSVersion::from_str(var)
+ } else {
+ // Default operating system version.
+ // See <https://github.com/rust-lang/rust/blob/1e5719bdc40bb553089ce83525f07dfe0b2e71e9/compiler/rustc_target/src/spec/base/apple/mod.rs#L207-L215>
+ //
+ // Note that we cannot do as they suggest, and use
+ // `rustc --print=deployment-target`, as this has to work at `const`
+ // time.
+ #[allow(clippy::if_same_then_else)]
+ let os_min = if cfg!(target_os = "macos") {
+ (10, 12, 0)
+ } else if cfg!(target_os = "ios") {
+ (10, 0, 0)
+ } else if cfg!(target_os = "tvos") {
+ (10, 0, 0)
+ } else if cfg!(target_os = "watchos") {
+ (5, 0, 0)
+ } else if cfg!(target_os = "visionos") {
+ (1, 0, 0)
+ } else {
+ panic!("unknown Apple OS")
+ };
+
+ // On certain targets it makes sense to raise the minimum OS version.
+ //
+ // See <https://github.com/rust-lang/rust/blob/1e5719bdc40bb553089ce83525f07dfe0b2e71e9/compiler/rustc_target/src/spec/base/apple/mod.rs#L217-L231>
+ //
+ // Note that we cannot do all the same checks as `rustc` does, because
+ // we have no way of knowing if the architecture is `arm64e` without
+ // reading the target triple itself (and we want to get rid of build
+ // scripts).
+ #[allow(clippy::if_same_then_else)]
+ let min = if cfg!(all(target_os = "macos", target_arch = "aarch64")) {
+ (11, 0, 0)
+ } else if cfg!(all(
+ target_os = "ios",
+ target_arch = "aarch64",
+ target_abi_macabi
+ )) {
+ (14, 0, 0)
+ } else if cfg!(all(
+ target_os = "ios",
+ target_arch = "aarch64",
+ target_simulator
+ )) {
+ (14, 0, 0)
+ } else if cfg!(all(target_os = "tvos", target_arch = "aarch64")) {
+ (14, 0, 0)
+ } else if cfg!(all(target_os = "watchos", target_arch = "aarch64")) {
+ (7, 0, 0)
+ } else {
+ os_min
+ };
+
+ OSVersion {
+ major: min.0,
+ minor: min.1,
+ patch: min.2,
+ }
+ }
+};
+
+/// Get the current OS version.
+///
+/// # Semantics
+///
+/// The reported version on macOS might be 10.16 if the SDK version of the binary is less than 11.0.
+/// This is a workaround that Apple implemented to handle applications that assumed that macOS
+/// versions would always start with "10", see:
+/// <https://github.com/apple-oss-distributions/xnu/blob/xnu-11215.81.4/libsyscall/wrappers/system-version-compat.c>
+///
+/// It _is_ possible to get the real version regardless of the SDK version of the binary, this is
+/// what Zig does:
+/// <https://github.com/ziglang/zig/blob/0.13.0/lib/std/zig/system/darwin/macos.zig>
+///
+/// We choose to not do that, and instead follow Apple's behaviour here, and return 10.16 when
+/// compiled with an older SDK; the user should instead upgrade their tooling.
+///
+/// NOTE: `rustc` currently doesn't set the right SDK version when linking with ld64, so this will
+/// have the wrong behaviour with `-Clinker=ld` on x86_64. But that's a `rustc` bug:
+/// <https://github.com/rust-lang/rust/issues/129432>
+#[inline]
+pub(crate) fn current_version() -> OSVersion {
+ // Cache the lookup for performance.
+ //
+ // 0.0.0 is never going to be a valid version ("vtool" reports "n/a" on 0 versions), so we use
+ // that as our sentinel value.
+ static CURRENT_VERSION: AtomicU32 = AtomicU32::new(0);
+
+ // We use relaxed atomics instead of e.g. a `Once`, it doesn't matter if multiple threads end up
+ // racing to read or write the version, `lookup_version` should be idempotent and always return
+ // the same value.
+ //
+ // `compiler-rt` uses `dispatch_once`, but that's overkill for the reasons above.
+ let version = CURRENT_VERSION.load(Ordering::Relaxed);
+ OSVersion::from_u32(if version == 0 {
+ let version = lookup_version();
+ CURRENT_VERSION.store(version, Ordering::Relaxed);
+ version
+ } else {
+ version
+ })
+}
+
+/// Look up the os version.
+///
+/// # Aborts
+///
+/// Aborts if reading or parsing the version fails (or if the system was out of memory).
+///
+/// We deliberately choose to abort, as having this silently return an invalid OS version would be
+/// impossible for a user to debug.
+// The lookup is costly and should be on the cold path because of the cache in `current_version`.
+#[cold]
+// Micro-optimization: We use `extern "C"` to abort on panic, allowing `current_version` (inlined)
+// to be free of unwind handling.
+extern "C" fn lookup_version() -> u32 {
+ // Try to read from `sysctl` first (faster), but if that fails, fall back to reading the
+ // property list (this is roughly what `_availability_version_check` does internally).
+ let version = version_from_sysctl().unwrap_or_else(version_from_plist);
+
+ // Try to make it clearer to the optimizer that this will never return 0.
+ assert_ne!(version, OSVersion::MIN, "version cannot be 0.0.0");
+ version.to_u32()
+}
+
+/// Read the version from `kern.osproductversion` or `kern.iossupportversion`.
+///
+/// This is faster than `version_from_plist`, since it doesn't need to invoke `dlsym`.
+fn version_from_sysctl() -> Option<OSVersion> {
+ // This won't work in the simulator, as `kern.osproductversion` returns the host macOS version,
+ // and `kern.iossupportversion` returns the host macOS' iOSSupportVersion (while you can run
+ // simulators with many different iOS versions).
+ if cfg!(target_simulator) {
+ // Fall back to `version_from_plist` on these targets.
+ return None;
+ }
+
+ // SAFETY: Same signatures as in `libc`.
+ //
+ // NOTE: We do not need to link this, that will be done by `std` by linking `libSystem`
+ // (which is required on macOS/Darwin).
+ extern "C" {
+ fn sysctlbyname(
+ name: *const c_char,
+ oldp: *mut c_void,
+ oldlenp: *mut usize,
+ newp: *mut c_void,
+ newlen: usize,
+ ) -> c_uint;
+ }
+
+ let sysctl_version = |name: &[u8]| {
+ let mut buf: [u8; 32] = [0; 32];
+ let mut size = buf.len();
+ let ptr = buf.as_mut_ptr().cast();
+ let ret = unsafe { sysctlbyname(name.as_ptr().cast(), ptr, &mut size, null_mut(), 0) };
+ if ret != 0 {
+ // This sysctl is not available.
+ return None;
+ }
+ let buf = &buf[..(size - 1)];
+
+ if buf.is_empty() {
+ // The buffer may be empty when using `kern.iossupportversion` on an actual iOS device,
+ // or on visionOS when running under "Designed for iPad".
+ //
+ // In that case, fall back to `kern.osproductversion`.
+ return None;
+ }
+
+ Some(OSVersion::from_bytes(buf))
+ };
+
+ // When `target_os = "ios"`, we may be in many different states:
+ // - Native iOS device.
+ // - iOS Simulator.
+ // - Mac Catalyst.
+ // - Mac + "Designed for iPad".
+ // - Native visionOS device + "Designed for iPad".
+ // - visionOS simulator + "Designed for iPad".
+ //
+ // Of these, only native, Mac Catalyst and simulators can be differentiated at compile-time
+ // (with `target_abi = ""`, `target_abi = "macabi"` and `target_abi = "sim"` respectively).
+ //
+ // That is, "Designed for iPad" will act as iOS at compile-time, but the `ProductVersion` will
+ // still be the host macOS or visionOS version.
+ //
+ // Furthermore, we can't even reliably differentiate between these at runtime, since
+ // `dyld_get_active_platform` isn't publicly available.
+ //
+ // Fortunately, we won't need to know any of that; we can simply attempt to get the
+ // `iOSSupportVersion` (which may be set on native iOS too, but then it will be set to the host
+ // iOS version), and if that fails, fall back to the `ProductVersion`.
+ if cfg!(target_os = "ios") {
+ // https://github.com/apple-oss-distributions/xnu/blob/xnu-11215.81.4/bsd/kern/kern_sysctl.c#L2077-L2100
+ if let Some(ios_support_version) = sysctl_version(b"kern.iossupportversion\0") {
+ return Some(ios_support_version);
+ }
+
+ // On Mac Catalyst, if we failed looking up `iOSSupportVersion`, we don't want to
+ // accidentally fall back to `ProductVersion`.
+ if cfg!(target_abi_macabi) {
+ return None;
+ }
+ }
+
+ // Introduced in macOS 10.13.4.
+ // https://github.com/apple-oss-distributions/xnu/blob/xnu-11215.81.4/bsd/kern/kern_sysctl.c#L2015-L2051
+ sysctl_version(b"kern.osproductversion\0")
+}
+
+/// Look up the current OS version(s) from `/System/Library/CoreServices/SystemVersion.plist`.
+///
+/// More specifically, from the `ProductVersion` and `iOSSupportVersion` keys, and from
+/// `$IPHONE_SIMULATOR_ROOT/System/Library/CoreServices/SystemVersion.plist` on the simulator.
+///
+/// This file was introduced in macOS 10.3, which is well below the minimum supported version by
+/// `rustc`, which is (at the time of writing) macOS 10.12.
+///
+/// # Implementation
+///
+/// We do roughly the same thing in here as `compiler-rt`, and dynamically look up CoreFoundation
+/// utilities for parsing PLists (to avoid having to re-implement that in here, as pulling in a full
+/// PList parser into `std` seems costly).
+///
+/// If this is found to be undesirable, we _could_ possibly hack it by parsing the PList manually
+/// (it seems to use the plain-text "xml1" encoding/format in all versions), but that seems brittle.
+fn version_from_plist() -> OSVersion {
+ // The root directory relative to where all files are located.
+ let root = if cfg!(target_simulator) {
+ PathBuf::from(env::var_os("IPHONE_SIMULATOR_ROOT").expect(
+ "environment variable `IPHONE_SIMULATOR_ROOT` must be set when executing under simulator",
+ ))
+ } else {
+ PathBuf::from("/")
+ };
+
+ // Read `SystemVersion.plist`. Always present on Apple platforms, reading it cannot fail.
+ let path = root.join("System/Library/CoreServices/SystemVersion.plist");
+ let plist_buffer = fs::read(&path).unwrap_or_else(|e| panic!("failed reading {path:?}: {e}"));
+ parse_version_from_plist(&root, &plist_buffer)
+}
+
+/// Split out from [`version_from_plist`] to allow for testing.
+#[allow(non_upper_case_globals, non_snake_case)]
+fn parse_version_from_plist(root: &Path, plist_buffer: &[u8]) -> OSVersion {
+ const RTLD_LAZY: c_int = 0x1;
+ const RTLD_LOCAL: c_int = 0x4;
+
+ extern "C" {
+ fn dlopen(filename: *const c_char, flag: c_int) -> *mut c_void;
+ fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
+ fn dlerror() -> *mut c_char;
+ fn dlclose(handle: *mut c_void) -> c_int;
+ }
+
+ // Link to the CoreFoundation dylib, and look up symbols from that.
+ // We explicitly use non-versioned path here, to allow this to work on older iOS devices.
+ let cf_path = root.join("System/Library/Frameworks/CoreFoundation.framework/CoreFoundation");
+
+ let cf_path =
+ CString::new(cf_path.into_os_string().into_vec()).expect("failed allocating string");
+ let cf_handle = unsafe { dlopen(cf_path.as_ptr(), RTLD_LAZY | RTLD_LOCAL) };
+ if cf_handle.is_null() {
+ let err = unsafe { CStr::from_ptr(dlerror()) };
+ panic!("could not open CoreFoundation.framework: {err:?}");
+ }
+ let _cf_handle_free = Deferred(|| {
+ // Ignore errors when closing. This is also what `libloading` does:
+ // https://docs.rs/libloading/0.8.6/src/libloading/os/unix/mod.rs.html#374
+ let _ = unsafe { dlclose(cf_handle) };
+ });
+
+ macro_rules! dlsym {
+ (
+ unsafe fn $name:ident($($param:ident: $param_ty:ty),* $(,)?) $(-> $ret:ty)?;
+ ) => {{
+ let ptr = unsafe {
+ dlsym(
+ cf_handle,
+ concat!(stringify!($name), '\0').as_bytes().as_ptr().cast(),
+ )
+ };
+ if ptr.is_null() {
+ let err = unsafe { CStr::from_ptr(dlerror()) };
+ panic!("could not find function {}: {err:?}", stringify!($name));
+ }
+ // SAFETY: Just checked that the symbol isn't NULL, and caller verifies that the
+ // signature is correct.
+ unsafe {
+ core::mem::transmute::<
+ *mut c_void,
+ unsafe extern "C" fn($($param_ty),*) $(-> $ret)?,
+ >(ptr)
+ }
+ }};
+ }
+
+ // MacTypes.h
+ type Boolean = u8;
+ // CoreFoundation/CFBase.h
+ type CFTypeID = usize;
+ type CFOptionFlags = usize;
+ type CFIndex = isize;
+ type CFTypeRef = *mut c_void;
+ type CFAllocatorRef = CFTypeRef;
+ const kCFAllocatorDefault: CFAllocatorRef = null_mut();
+ // Available: in all CF versions.
+ let allocator_null = unsafe { dlsym(cf_handle, b"kCFAllocatorNull\0".as_ptr().cast()) };
+ if allocator_null.is_null() {
+ let err = unsafe { CStr::from_ptr(dlerror()) };
+ panic!("could not find kCFAllocatorNull: {err:?}");
+ }
+ let kCFAllocatorNull = unsafe { *allocator_null.cast::<CFAllocatorRef>() };
+ let CFRelease = dlsym!(
+ // Available: in all CF versions.
+ unsafe fn CFRelease(cf: CFTypeRef);
+ );
+ let CFGetTypeID = dlsym!(
+ // Available: in all CF versions.
+ unsafe fn CFGetTypeID(cf: CFTypeRef) -> CFTypeID;
+ );
+ // CoreFoundation/CFError.h
+ type CFErrorRef = CFTypeRef;
+ // CoreFoundation/CFData.h
+ type CFDataRef = CFTypeRef;
+ let CFDataCreateWithBytesNoCopy = dlsym!(
+ // Available: in all CF versions.
+ unsafe fn CFDataCreateWithBytesNoCopy(
+ allocator: CFAllocatorRef,
+ bytes: *const u8,
+ length: CFIndex,
+ bytes_deallocator: CFAllocatorRef,
+ ) -> CFDataRef;
+ );
+ // CoreFoundation/CFPropertyList.h
+ const kCFPropertyListImmutable: CFOptionFlags = 0;
+ type CFPropertyListFormat = CFIndex;
+ type CFPropertyListRef = CFTypeRef;
+ let CFPropertyListCreateWithData = dlsym!(
+ // Available: since macOS 10.6.
+ unsafe fn CFPropertyListCreateWithData(
+ allocator: CFAllocatorRef,
+ data: CFDataRef,
+ options: CFOptionFlags,
+ format: *mut CFPropertyListFormat,
+ error: *mut CFErrorRef,
+ ) -> CFPropertyListRef;
+ );
+ // CoreFoundation/CFString.h
+ type CFStringRef = CFTypeRef;
+ type CFStringEncoding = u32;
+ const kCFStringEncodingUTF8: CFStringEncoding = 0x08000100;
+ let CFStringGetTypeID = dlsym!(
+ // Available: in all CF versions.
+ unsafe fn CFStringGetTypeID() -> CFTypeID;
+ );
+ let CFStringCreateWithCStringNoCopy = dlsym!(
+ // Available: in all CF versions.
+ unsafe fn CFStringCreateWithCStringNoCopy(
+ alloc: CFAllocatorRef,
+ c_str: *const c_char,
+ encoding: CFStringEncoding,
+ contents_deallocator: CFAllocatorRef,
+ ) -> CFStringRef;
+ );
+ let CFStringGetCString = dlsym!(
+ // Available: in all CF versions.
+ unsafe fn CFStringGetCString(
+ the_string: CFStringRef,
+ buffer: *mut c_char,
+ buffer_size: CFIndex,
+ encoding: CFStringEncoding,
+ ) -> Boolean;
+ );
+ // CoreFoundation/CFDictionary.h
+ type CFDictionaryRef = CFTypeRef;
+ let CFDictionaryGetTypeID = dlsym!(
+ // Available: in all CF versions.
+ unsafe fn CFDictionaryGetTypeID() -> CFTypeID;
+ );
+ let CFDictionaryGetValue = dlsym!(
+ // Available: in all CF versions.
+ unsafe fn CFDictionaryGetValue(
+ the_dict: CFDictionaryRef,
+ key: *const c_void,
+ ) -> *const c_void;
+ );
+
+ // MARK: Done declaring symbols.
+
+ let plist_data = unsafe {
+ CFDataCreateWithBytesNoCopy(
+ kCFAllocatorDefault,
+ plist_buffer.as_ptr(),
+ plist_buffer.len() as CFIndex,
+ kCFAllocatorNull,
+ )
+ };
+ assert!(!plist_data.is_null(), "failed creating data");
+ let _plist_data_release = Deferred(|| unsafe { CFRelease(plist_data) });
+
+ let plist = unsafe {
+ CFPropertyListCreateWithData(
+ kCFAllocatorDefault,
+ plist_data,
+ kCFPropertyListImmutable,
+ null_mut(), // Don't care about the format of the PList.
+ null_mut(), // Don't care about the error data.
+ )
+ };
+ assert!(
+ !plist.is_null(),
+ "failed reading PList in SystemVersion.plist"
+ );
+ let _plist_release = Deferred(|| unsafe { CFRelease(plist) });
+
+ assert_eq!(
+ unsafe { CFGetTypeID(plist) },
+ unsafe { CFDictionaryGetTypeID() },
+ "SystemVersion.plist did not contain a dictionary at the top level"
+ );
+ let plist = plist as CFDictionaryRef;
+
+ let get_string_key = |plist, lookup_key: &[u8]| {
+ let cf_lookup_key = unsafe {
+ CFStringCreateWithCStringNoCopy(
+ kCFAllocatorDefault,
+ lookup_key.as_ptr().cast(),
+ kCFStringEncodingUTF8,
+ kCFAllocatorNull,
+ )
+ };
+ assert!(!cf_lookup_key.is_null(), "failed creating CFString");
+ let _lookup_key_release = Deferred(|| unsafe { CFRelease(cf_lookup_key) });
+
+ let value = unsafe { CFDictionaryGetValue(plist, cf_lookup_key) as CFTypeRef };
+ // ^ getter, so don't release.
+ if value.is_null() {
+ return None;
+ }
+
+ assert_eq!(
+ unsafe { CFGetTypeID(value) },
+ unsafe { CFStringGetTypeID() },
+ "key in SystemVersion.plist must be a string"
+ );
+ let value = value as CFStringRef;
+
+ let mut version_str = [0u8; 32];
+ let ret = unsafe {
+ CFStringGetCString(
+ value,
+ version_str.as_mut_ptr().cast::<c_char>(),
+ version_str.len() as CFIndex,
+ kCFStringEncodingUTF8,
+ )
+ };
+ assert_ne!(ret, 0, "failed getting string from CFString");
+
+ let version_str =
+ CStr::from_bytes_until_nul(&version_str).expect("failed converting to CStr");
+
+ Some(OSVersion::from_bytes(version_str.to_bytes()))
+ };
+
+ // Same logic as in `version_from_sysctl`.
+ if cfg!(target_os = "ios") {
+ if let Some(ios_support_version) = get_string_key(plist, b"iOSSupportVersion\0") {
+ return ios_support_version;
+ }
+
+ // Force Mac Catalyst to use iOSSupportVersion (do not fall back to ProductVersion).
+ if cfg!(target_abi_macabi) {
+ panic!("expected iOSSupportVersion in SystemVersion.plist");
+ }
+ }
+
+ // On all other platforms, we can find the OS version by simply looking at `ProductVersion`.
+ get_string_key(plist, b"ProductVersion\0")
+ .expect("expected ProductVersion in SystemVersion.plist")
+}
+
+struct Deferred<F: FnMut()>(F);
+
+impl<F: FnMut()> Drop for Deferred<F> {
+ fn drop(&mut self) {
+ (self.0)();
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use alloc::string::String;
+ use std::process::Command;
+
+ #[test]
+ fn sysctl_same_as_in_plist() {
+ if let Some(version) = version_from_sysctl() {
+ assert_eq!(version, version_from_plist());
+ }
+ }
+
+ #[test]
+ fn read_version() {
+ assert!(OSVersion::MIN < current_version(), "version cannot be min");
+ assert!(current_version() < OSVersion::MAX, "version cannot be max");
+ }
+
+ #[test]
+ #[cfg_attr(
+ not(target_os = "macos"),
+ ignore = "`sw_vers` is only available on macOS"
+ )]
+ fn compare_against_sw_vers() {
+ let expected = Command::new("sw_vers")
+ .arg("-productVersion")
+ .output()
+ .unwrap()
+ .stdout;
+ let expected = String::from_utf8(expected).unwrap();
+ let expected = OSVersion::from_str(expected.trim());
+
+ let actual = current_version();
+ assert_eq!(expected, actual);
+ }
+}
diff --git a/third_party/rust/objc2/src/__macro_helpers/retain_semantics.rs b/third_party/rust/objc2/src/__macro_helpers/retain_semantics.rs
@@ -0,0 +1,796 @@
+use core::mem::ManuallyDrop;
+
+use crate::encode::EncodeReturn;
+use crate::rc::{Allocated, PartialInit, Retained};
+use crate::runtime::{AnyClass, AnyObject, MessageReceiver, Sel};
+use crate::{sel, DefinedClass, Message};
+
+use super::defined_ivars::set_finalized;
+use super::{
+ AllocFamily, ConvertReturn, CopyFamily, InitFamily, MutableCopyFamily, NewFamily, NoneFamily,
+};
+
+// TODO: Differentiate between instance and class methods?
+
+#[derive(Debug, Copy, Clone)]
+pub struct KindSendMessageSuper;
+
+#[derive(Debug, Copy, Clone)]
+pub struct KindSendMessage;
+
+#[derive(Debug, Copy, Clone)]
+pub struct KindDefined;
+
+trait SendMessage {}
+
+impl SendMessage for KindSendMessage {}
+impl SendMessage for KindSendMessageSuper {}
+
+trait NotSuper {}
+
+impl NotSuper for KindSendMessage {}
+impl NotSuper for KindDefined {}
+
+trait IsSuper {}
+
+impl IsSuper for KindSendMessageSuper {}
+
+/// Trait for restricting the possible Receiver/Return type combinations.
+///
+/// This is used to convert receiver and return types in `extern_methods!`
+/// and `msg_send!`.
+///
+/// Having both the receiver, return type and whether the message send is a
+/// super-message send is especially important for `init` methods, which must
+/// return the same type as they were called with, and must restrict the
+/// receiver to either `Allocated` or `PartialInit` depending on the super-ness.
+///
+/// # Summary
+///
+/// ```text
+/// new: Receiver -> Option<Retained<Return>>
+/// alloc: &AnyClass -> Allocated<Return>
+/// init: Allocated<T> -> Option<Retained<T>>
+/// init super: PartialInit<T> -> Option<Retained<T>>
+/// copy: Receiver -> Option<Retained<Return>>
+/// mutableCopy: Receiver -> Option<Retained<Return>>
+/// others: Receiver -> Option<Retained<Return>>
+/// ```
+pub trait RetainSemantics<Receiver, Return, Kind>: Sized {
+ /// The inner receiver type that is used directly at the ABI boundary of
+ /// the message send.
+ type ReceiverInner: MessageReceiver;
+
+ /// Prepare the receiver for sending a message.
+ fn prepare_message_send(receiver: Receiver) -> Self::ReceiverInner;
+
+ /// Prepare the receiver for receiving a message.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The receiver must be valid.
+ unsafe fn prepare_defined_method(receiver: Self::ReceiverInner) -> Receiver;
+
+ /// The inner return type that is used directly at the ABI boundary of the
+ /// message send.
+ type ReturnInner: EncodeReturn;
+
+ /// Convert a received return value according to the specified retain
+ /// semantics.
+ ///
+ ///
+ /// # Panics
+ ///
+ /// If conversion fails (such as when converting to `Retained<T>`), this
+ /// uses the receiver and selector to panic with a nice error message.
+ ///
+ /// NOTE: The behavior that returning `Retained<T>` always unwraps
+ /// instead of using `unwrap_unchecked` is relied upon by
+ /// `header-translator` for soundness, see e.g. `parse_property_return`.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The return value must be valid, and the receiver pointer must have
+ /// come from `prepare_message_send`.
+ #[track_caller]
+ unsafe fn convert_message_return(
+ ret: Self::ReturnInner,
+ receiver_ptr: *mut AnyObject,
+ sel: Sel,
+ ) -> Return;
+
+ // TODO: Use this in `define_class!`.
+ fn convert_defined_return(ret: Return) -> Self::ReturnInner;
+}
+
+//
+// Implementations for all method families.
+//
+
+/// Generic implementation allowing message sending to normal encode return
+/// types.
+impl<Receiver, Return, Kind, MethodFamily> RetainSemantics<Receiver, Return, Kind> for MethodFamily
+where
+ Receiver: MessageReceiver,
+ Return: ConvertReturn<MethodFamily>,
+{
+ type ReceiverInner = Receiver;
+
+ #[inline]
+ fn prepare_message_send(receiver: Receiver) -> Receiver {
+ receiver
+ }
+
+ #[inline]
+ unsafe fn prepare_defined_method(receiver: Receiver) -> Receiver {
+ receiver
+ }
+
+ type ReturnInner = Return::Inner;
+
+ #[inline]
+ unsafe fn convert_message_return(
+ ret: Return::Inner,
+ receiver_ptr: *mut AnyObject,
+ sel: Sel,
+ ) -> Return {
+ // SAFETY: Upheld by caller.
+ unsafe { Return::convert_message_return(ret, receiver_ptr, sel) }
+ }
+
+ #[inline]
+ fn convert_defined_return(ret: Return) -> Return::Inner {
+ Return::convert_defined_return(ret)
+ }
+}
+
+/// Convenience implementation for sending messages to `&Retained<T>`.
+impl<'a, T, Return, Kind, MethodFamily> RetainSemantics<&'a Retained<T>, Return, Kind>
+ for MethodFamily
+where
+ T: Message,
+ // Only allow on message sends, this won't work in `define_class!`.
+ Kind: SendMessage,
+ MethodFamily: RetainSemantics<*const T, Return, Kind>,
+{
+ type ReceiverInner = *const T;
+
+ #[inline]
+ fn prepare_message_send(receiver: &'a Retained<T>) -> *const T {
+ &**receiver
+ }
+
+ #[inline]
+ unsafe fn prepare_defined_method(_receiver: *const T) -> &'a Retained<T> {
+ // Should be prevented statically by `Kind: SendMessage`.
+ unreachable!()
+ }
+
+ type ReturnInner = <Self as RetainSemantics<*const T, Return, Kind>>::ReturnInner;
+
+ #[inline]
+ unsafe fn convert_message_return(
+ ret: Self::ReturnInner,
+ receiver_ptr: *mut AnyObject,
+ sel: Sel,
+ ) -> Return {
+ unsafe {
+ <Self as RetainSemantics<*const T, Return, Kind>>::convert_message_return(
+ ret,
+ receiver_ptr,
+ sel,
+ )
+ }
+ }
+
+ #[inline]
+ fn convert_defined_return(ret: Return) -> Self::ReturnInner {
+ <Self as RetainSemantics<*const T, Return, Kind>>::convert_defined_return(ret)
+ }
+}
+
+/// Convenience implementation for sending messages to `ManuallyDrop<Retained<T>>`.
+impl<T, Return, Kind, MethodFamily> RetainSemantics<ManuallyDrop<Retained<T>>, Return, Kind>
+ for MethodFamily
+where
+ T: Message,
+ // Only allow on message sends, this doesn't make sense to do in
+ // `define_class!`.
+ Kind: SendMessage,
+ MethodFamily: RetainSemantics<*mut T, Return, Kind>,
+{
+ type ReceiverInner = *mut T;
+
+ #[inline]
+ fn prepare_message_send(receiver: ManuallyDrop<Retained<T>>) -> *mut T {
+ Retained::into_raw(ManuallyDrop::into_inner(receiver))
+ }
+
+ #[inline]
+ unsafe fn prepare_defined_method(_receiver: *mut T) -> ManuallyDrop<Retained<T>> {
+ // Should be prevented statically by `Kind: SendMessage`.
+ unreachable!()
+ }
+
+ type ReturnInner = <Self as RetainSemantics<*mut T, Return, Kind>>::ReturnInner;
+
+ #[inline]
+ unsafe fn convert_message_return(
+ ret: Self::ReturnInner,
+ receiver_ptr: *mut AnyObject,
+ sel: Sel,
+ ) -> Return {
+ unsafe {
+ <Self as RetainSemantics<*mut T, Return, Kind>>::convert_message_return(
+ ret,
+ receiver_ptr,
+ sel,
+ )
+ }
+ }
+
+ #[inline]
+ fn convert_defined_return(ret: Return) -> Self::ReturnInner {
+ <Self as RetainSemantics<*mut T, Return, Kind>>::convert_defined_return(ret)
+ }
+}
+
+//
+// NewFamily
+//
+// Receiver -> Option<Retained<Return>>
+// Receiver -> Retained<Return>
+//
+
+impl<T: Message> ConvertReturn<NewFamily> for Option<Retained<T>> {
+ type Inner = *mut T;
+
+ #[inline]
+ unsafe fn convert_message_return(
+ inner: Self::Inner,
+ _receiver_ptr: *mut AnyObject,
+ _sel: Sel,
+ ) -> Self {
+ // SAFETY: The selector is `new`, so this has +1 retain count.
+ //
+ // Validity of the pointer is upheld by the caller.
+ unsafe { Retained::from_raw(inner) }
+ }
+
+ #[inline]
+ fn convert_defined_return(self) -> Self::Inner {
+ // Return with +1 retain count.
+ Retained::consume_as_ptr_option(self)
+ }
+}
+
+impl<T: Message> ConvertReturn<NewFamily> for Retained<T> {
+ type Inner = *mut T;
+
+ #[inline]
+ unsafe fn convert_message_return(
+ inner: Self::Inner,
+ receiver_ptr: *mut AnyObject,
+ sel: Sel,
+ ) -> Self {
+ // SAFETY: The selector is `new`, so this has +1 retain count.
+ //
+ // Validity of the pointer is upheld by the caller.
+ let ret = unsafe { Retained::from_raw(inner) };
+ if let Some(ret) = ret {
+ ret
+ } else {
+ // SAFETY: The receiver is still valid after a message send to
+ // a `new` method - it would not be if the method was `init`.
+ let receiver = unsafe { receiver_ptr.as_ref() };
+ new_fail(receiver, sel)
+ }
+ }
+
+ #[inline]
+ fn convert_defined_return(self) -> Self::Inner {
+ // Return with +1 retain count.
+ Retained::into_raw(self)
+ }
+}
+
+#[cold]
+#[track_caller]
+fn new_fail(receiver: Option<&AnyObject>, sel: Sel) -> ! {
+ if let Some(receiver) = receiver {
+ let cls = receiver.class();
+ if cls.is_metaclass() {
+ if sel == sel!(new) {
+ panic!("failed creating new instance of {cls}")
+ } else {
+ panic!("failed creating new instance using +[{cls} {sel}]")
+ }
+ } else {
+ panic!("unexpected NULL returned from -[{cls} {sel}]")
+ }
+ } else {
+ panic!("unexpected NULL {sel}; receiver was NULL");
+ }
+}
+
+//
+// AllocFamily
+//
+// &AnyClass -> Allocated<Return>
+//
+
+// Restrict `alloc` methods to the simplest case of a class reference. This
+// could possibly be relaxed in the future, but there's likely no need for
+// that.
+impl<'a, Return, Kind> RetainSemantics<&'a AnyClass, Allocated<Return>, Kind> for AllocFamily
+where
+ Return: Message,
+{
+ type ReceiverInner = &'a AnyClass;
+
+ #[inline]
+ fn prepare_message_send(receiver: &'a AnyClass) -> &'a AnyClass {
+ receiver
+ }
+
+ #[inline]
+ unsafe fn prepare_defined_method(receiver: &'a AnyClass) -> &'a AnyClass {
+ receiver
+ }
+
+ type ReturnInner = *mut Return;
+
+ #[inline]
+ unsafe fn convert_message_return(
+ ret: *mut Return,
+ _receiver_ptr: *mut AnyObject,
+ _sel: Sel,
+ ) -> Allocated<Return> {
+ // SAFETY: The selector is `alloc`, so this has +1 retain count.
+ //
+ // Validity of the pointer is upheld by the caller.
+ unsafe { Allocated::new(ret) }
+ }
+
+ #[inline]
+ fn convert_defined_return(ret: Allocated<Return>) -> *mut Return {
+ // Return with +1 retain count.
+ Allocated::into_ptr(ret)
+ }
+}
+
+//
+// InitFamily
+//
+// We restrict `init` methods such that, if they return `Retained<T>`, the
+// receiver must be `Allocated<T>` with the same generic parameter.
+//
+// Simple return types have no restriction.
+//
+// See <https://clang.llvm.org/docs/AutomaticReferenceCounting.html#method-families>
+//
+// normal:
+// Allocated<T> -> Option<Retained<T>>
+// Allocated<T> -> Retained<T>
+//
+// super:
+// PartialInit<T> -> Option<Retained<T>>
+// PartialInit<T> -> Retained<T>
+//
+
+impl<T, Kind> RetainSemantics<Allocated<T>, Option<Retained<T>>, Kind> for InitFamily
+where
+ T: Message,
+ Kind: NotSuper,
+{
+ type ReceiverInner = *mut T;
+
+ #[inline]
+ fn prepare_message_send(receiver: Allocated<T>) -> *mut T {
+ // Pass the +1 retain count to the callee.
+ Allocated::into_ptr(receiver)
+ }
+
+ #[inline]
+ unsafe fn prepare_defined_method(receiver: *mut T) -> Allocated<T> {
+ // SAFETY: The receiver of an `init` method is `Allocated` and
+ // consumed, and thus has +1 retain count.
+ //
+ // Validity of the pointer is upheld by the caller.
+ unsafe { Allocated::new(receiver) }
+ }
+
+ type ReturnInner = *mut T;
+
+ #[inline]
+ unsafe fn convert_message_return(
+ ret: *mut T,
+ _receiver_ptr: *mut AnyObject,
+ _sel: Sel,
+ ) -> Option<Retained<T>> {
+ // SAFETY: The selector is `init`, so this has +1 retain count.
+ //
+ // Validity of the pointer is upheld by the caller.
+ unsafe { Retained::from_raw(ret) }
+ }
+
+ #[inline]
+ fn convert_defined_return(ret: Option<Retained<T>>) -> *mut T {
+ // Return with +1 retain count.
+ Retained::consume_as_ptr_option(ret)
+ }
+}
+
+impl<T, Kind> RetainSemantics<Allocated<T>, Retained<T>, Kind> for InitFamily
+where
+ T: Message,
+ Kind: NotSuper,
+{
+ type ReceiverInner = *mut T;
+
+ #[inline]
+ fn prepare_message_send(receiver: Allocated<T>) -> *mut T {
+ // Pass the +1 retain count to the callee.
+ Allocated::into_ptr(receiver)
+ }
+
+ #[inline]
+ unsafe fn prepare_defined_method(receiver: *mut T) -> Allocated<T> {
+ // SAFETY: The receiver of an `init` method is `Allocated` and
+ // consumed, and thus has +1 retain count.
+ //
+ // Validity of the pointer is upheld by the caller.
+ unsafe { Allocated::new(receiver) }
+ }
+
+ type ReturnInner = *mut T;
+
+ #[inline]
+ unsafe fn convert_message_return(
+ ret: *mut T,
+ receiver_ptr: *mut AnyObject,
+ sel: Sel,
+ ) -> Retained<T> {
+ // SAFETY: The selector is `init`, so this has +1 retain count.
+ //
+ // Validity of the pointer is upheld by the caller.
+ let ret = unsafe { Retained::from_raw(ret) };
+ if let Some(ret) = ret {
+ ret
+ } else {
+ init_fail(receiver_ptr, sel)
+ }
+ }
+
+ #[inline]
+ fn convert_defined_return(ret: Retained<T>) -> *mut T {
+ // Return with +1 retain count.
+ Retained::into_raw(ret)
+ }
+}
+
+impl<T, Kind> RetainSemantics<PartialInit<T>, Option<Retained<T>>, Kind> for InitFamily
+where
+ T: DefinedClass,
+ Kind: IsSuper,
+{
+ type ReceiverInner = *mut T;
+
+ #[inline]
+ fn prepare_message_send(receiver: PartialInit<T>) -> *mut T {
+ // Pass the +1 retain count to the callee.
+ PartialInit::into_ptr(receiver)
+ }
+
+ #[inline]
+ unsafe fn prepare_defined_method(receiver: *mut T) -> PartialInit<T> {
+ // SAFETY: The receiver of a super `init` method is `PartialInit` and
+ // consumed, and thus has +1 retain count.
+ //
+ // Validity of the pointer is upheld by the caller.
+ unsafe { PartialInit::new(receiver) }
+ }
+
+ type ReturnInner = *mut T;
+
+ #[inline]
+ unsafe fn convert_message_return(
+ ret: *mut T,
+ _receiver_ptr: *mut AnyObject,
+ _sel: Sel,
+ ) -> Option<Retained<T>> {
+ // SAFETY: The selector is `init`, so this has +1 retain count.
+ //
+ // Validity of the pointer is upheld by the caller.
+ let ret = unsafe { Retained::from_raw(ret) };
+ if let Some(ret) = ret {
+ // SAFETY: We just got back from `init`, so the super initializer
+ // will have run.
+ //
+ // Caller ensures that the pointer is valid.
+ unsafe { set_finalized(ret.as_nonnull_ptr()) };
+
+ Some(ret)
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn convert_defined_return(ret: Option<Retained<T>>) -> *mut T {
+ // Return with +1 retain count.
+ Retained::consume_as_ptr_option(ret)
+ }
+}
+
+impl<T, Kind> RetainSemantics<PartialInit<T>, Retained<T>, Kind> for InitFamily
+where
+ T: DefinedClass,
+ Kind: IsSuper,
+{
+ type ReceiverInner = *mut T;
+
+ #[inline]
+ fn prepare_message_send(receiver: PartialInit<T>) -> *mut T {
+ // Pass the +1 retain count to the callee.
+ PartialInit::into_ptr(receiver)
+ }
+
+ #[inline]
+ unsafe fn prepare_defined_method(receiver: *mut T) -> PartialInit<T> {
+ // SAFETY: The receiver of a super `init` method is `PartialInit` and
+ // consumed, and thus has +1 retain count.
+ //
+ // Validity of the pointer is upheld by the caller.
+ unsafe { PartialInit::new(receiver) }
+ }
+
+ type ReturnInner = *mut T;
+
+ #[inline]
+ unsafe fn convert_message_return(
+ ret: *mut T,
+ receiver_ptr: *mut AnyObject,
+ sel: Sel,
+ ) -> Retained<T> {
+ // SAFETY: The selector is `init`, so this has +1 retain count.
+ //
+ // Validity of the pointer is upheld by the caller.
+ let ret = unsafe { Retained::from_raw(ret) };
+ if let Some(ret) = ret {
+ // SAFETY: We just got back from `init`, so the super initializer
+ // will have run.
+ //
+ // Caller ensures that the pointer is valid.
+ unsafe { set_finalized(ret.as_nonnull_ptr()) };
+
+ ret
+ } else {
+ init_fail(receiver_ptr, sel)
+ }
+ }
+
+ #[inline]
+ fn convert_defined_return(ret: Retained<T>) -> *mut T {
+ // Return with +1 retain count.
+ Retained::into_raw(ret)
+ }
+}
+
+#[cold]
+#[track_caller]
+fn init_fail(receiver: *mut AnyObject, sel: Sel) -> ! {
+ if receiver.is_null() {
+ panic!("failed allocating object")
+ } else {
+ // We can't really display a more descriptive message here since the
+ // object is consumed by `init` and may not be valid any more.
+ if sel == sel!(init) {
+ panic!("failed initializing object")
+ } else {
+ panic!("failed initializing object with -{sel}")
+ }
+ }
+}
+
+//
+// CopyFamily
+//
+// Receiver -> Option<Retained<T>>
+// Receiver -> Retained<T>
+//
+
+impl<T: Message> ConvertReturn<CopyFamily> for Option<Retained<T>> {
+ type Inner = *mut T;
+
+ #[inline]
+ unsafe fn convert_message_return(
+ inner: Self::Inner,
+ _receiver_ptr: *mut AnyObject,
+ _sel: Sel,
+ ) -> Self {
+ // SAFETY: The selector is `copy`, so this has +1 retain count.
+ //
+ // Validity of the pointer is upheld by the caller.
+ unsafe { Retained::from_raw(inner) }
+ }
+
+ #[inline]
+ fn convert_defined_return(self) -> Self::Inner {
+ // Return with +1 retain count.
+ Retained::consume_as_ptr_option(self)
+ }
+}
+
+impl<T: Message> ConvertReturn<CopyFamily> for Retained<T> {
+ type Inner = *mut T;
+
+ #[inline]
+ unsafe fn convert_message_return(
+ inner: Self::Inner,
+ _receiver_ptr: *mut AnyObject,
+ _sel: Sel,
+ ) -> Self {
+ // SAFETY: The selector is `copy`, so this has +1 retain count.
+ //
+ // Validity of the pointer is upheld by the caller.
+ let ret = unsafe { Retained::from_raw(inner) };
+ if let Some(ret) = ret {
+ ret
+ } else {
+ copy_fail()
+ }
+ }
+
+ #[inline]
+ fn convert_defined_return(self) -> Self::Inner {
+ // Return with +1 retain count.
+ Retained::into_raw(self)
+ }
+}
+
+#[cold]
+#[track_caller]
+fn copy_fail() -> ! {
+ panic!("failed copying object")
+}
+
+//
+// MutableCopyFamily
+//
+// Receiver -> Option<Retained<T>>
+// Receiver -> Retained<T>
+//
+
+impl<T: Message> ConvertReturn<MutableCopyFamily> for Option<Retained<T>> {
+ type Inner = *mut T;
+
+ #[inline]
+ unsafe fn convert_message_return(
+ inner: Self::Inner,
+ _receiver_ptr: *mut AnyObject,
+ _sel: Sel,
+ ) -> Self {
+ // SAFETY: The selector is `mutableCopy`, so this has +1 retain count.
+ //
+ // Validity of the pointer is upheld by the caller.
+ unsafe { Retained::from_raw(inner) }
+ }
+
+ #[inline]
+ fn convert_defined_return(self) -> Self::Inner {
+ // Return with +1 retain count.
+ Retained::consume_as_ptr_option(self)
+ }
+}
+
+impl<T: Message> ConvertReturn<MutableCopyFamily> for Retained<T> {
+ type Inner = *mut T;
+
+ #[inline]
+ unsafe fn convert_message_return(
+ inner: Self::Inner,
+ _receiver_ptr: *mut AnyObject,
+ _sel: Sel,
+ ) -> Self {
+ // SAFETY: The selector is `mutableCopy`, so this has +1 retain count.
+ //
+ // Validity of the pointer is upheld by the caller.
+ let ret = unsafe { Retained::from_raw(inner) };
+ if let Some(ret) = ret {
+ ret
+ } else {
+ mutable_copy_fail()
+ }
+ }
+
+ #[inline]
+ fn convert_defined_return(self) -> Self::Inner {
+ // Return with +1 retain count.
+ Retained::into_raw(self)
+ }
+}
+
+#[cold]
+#[track_caller]
+fn mutable_copy_fail() -> ! {
+ panic!("failed copying object")
+}
+
+//
+// NoneFamily
+//
+// Receiver -> Option<Retained<T>>
+// Receiver -> Retained<T>
+//
+
+impl<T: Message> ConvertReturn<NoneFamily> for Option<Retained<T>> {
+ type Inner = *mut T;
+
+ #[inline]
+ unsafe fn convert_message_return(
+ inner: Self::Inner,
+ _receiver_ptr: *mut AnyObject,
+ _sel: Sel,
+ ) -> Self {
+ // NOTE: All code between the message send and `retain_autoreleased`
+ // must be able to be optimized away for it to work optimally.
+
+ // SAFETY: The selector is not `new`, `alloc`, `init`, `copy` nor
+ // `mutableCopy`, so the object must be manually retained.
+ //
+ // Validity of the pointer is upheld by the caller.
+ unsafe { Retained::retain_autoreleased(inner) }
+ }
+
+ #[inline]
+ fn convert_defined_return(self) -> Self::Inner {
+ Retained::autorelease_return_option(self)
+ }
+}
+
+impl<T: Message> ConvertReturn<NoneFamily> for Retained<T> {
+ type Inner = *mut T;
+
+ #[inline]
+ unsafe fn convert_message_return(
+ inner: Self::Inner,
+ receiver_ptr: *mut AnyObject,
+ sel: Sel,
+ ) -> Self {
+ // SAFETY: The selector is not `new`, `alloc`, `init`, `copy` nor
+ // `mutableCopy`, so the object must be manually retained.
+ //
+ // Validity of the pointer is upheld by the caller.
+ let ret = unsafe { Retained::retain_autoreleased(inner) };
+ if let Some(ret) = ret {
+ ret
+ } else {
+ // SAFETY: The receiver is still valid after a message send to
+ // a `none` method - it would not be if the method was `init`.
+ let receiver = unsafe { receiver_ptr.as_ref() };
+ none_fail(receiver, sel)
+ }
+ }
+
+ #[inline]
+ fn convert_defined_return(self) -> Self::Inner {
+ Retained::autorelease_return(self)
+ }
+}
+
+#[cold]
+#[track_caller]
+fn none_fail(receiver: Option<&AnyObject>, sel: Sel) -> ! {
+ if let Some(receiver) = receiver {
+ let cls = receiver.class();
+ panic!(
+ "unexpected NULL returned from {}[{cls} {sel}]",
+ if cls.is_metaclass() { "+" } else { "-" },
+ )
+ } else {
+ panic!("unexpected NULL {sel}; receiver was NULL");
+ }
+}
diff --git a/third_party/rust/objc2/src/__macro_helpers/sync_unsafe_cell.rs b/third_party/rust/objc2/src/__macro_helpers/sync_unsafe_cell.rs
@@ -0,0 +1,32 @@
+use core::cell::UnsafeCell;
+
+#[repr(transparent)]
+#[derive(Debug)]
+pub struct SyncUnsafeCell<T: ?Sized> {
+ value: UnsafeCell<T>,
+}
+
+// SAFETY: This type is used for making `static`s which contain `UnsafeCell`.
+// The user upholds that such usage is correct.
+unsafe impl<T: ?Sized + Sync> Sync for SyncUnsafeCell<T> {}
+
+impl<T> SyncUnsafeCell<T> {
+ #[inline]
+ pub const fn new(value: T) -> Self {
+ Self {
+ value: UnsafeCell::new(value),
+ }
+ }
+
+ #[inline]
+ pub fn into_inner(self) -> T {
+ self.value.into_inner()
+ }
+}
+
+impl<T: ?Sized> SyncUnsafeCell<T> {
+ #[inline]
+ pub const fn get(&self) -> *mut T {
+ self.value.get()
+ }
+}
diff --git a/third_party/rust/objc2/src/__macro_helpers/writeback.rs b/third_party/rust/objc2/src/__macro_helpers/writeback.rs
@@ -0,0 +1,478 @@
+//! Support for passing "out"-parameters to `msg_send!` and family.
+//!
+//! See clang's documentation:
+//! <https://clang.llvm.org/docs/AutomaticReferenceCounting.html#passing-to-an-out-parameter-by-writeback>
+//!
+//! Note: We differ from that in that we do not create a temporary, whose
+//! address we then work on; instead, we directly reuse the pointer that the
+//! user provides (since, if it's a mutable pointer, we know that it's not
+//! shared elsewhere in the program, and hence it is safe to modify directly).
+//!
+//! Another important consideration is unwinding; I haven't researched how
+//! Clang handles that, but the correct thing is to do the writeback
+//! retain/release dance regardless of whether the function unwinded or not.
+//! We ensure this by doing it in `Drop`.
+use core::mem::ManuallyDrop;
+use core::ptr::NonNull;
+
+use super::ConvertArgument;
+use crate::rc::Retained;
+use crate::Message;
+
+// Note the `'static` bound here - this may not be necessary, but I'm unsure
+// of the exact requirements, so we better keep it for now.
+impl<T: Message + 'static> ConvertArgument for &mut Retained<T> {
+ // We use `*mut T` as the inner value instead of `NonNull<T>`, since we
+ // want to do debug checking that the value hasn't unexpectedly been
+ // overwritten to contain NULL (which is clear UB, but the user might have
+ // made a mistake).
+ type __Inner = NonNull<*mut T>;
+
+ type __WritebackOnDrop = WritebackOnDrop<T>;
+
+ #[inline]
+ fn __from_defined_param(_inner: Self::__Inner) -> Self {
+ todo!("`&mut Retained<_>` is not supported in `define_class!` yet")
+ }
+
+ #[inline]
+ unsafe fn __into_argument(self) -> (Self::__Inner, Self::__WritebackOnDrop) {
+ let ptr: NonNull<Retained<T>> = NonNull::from(self);
+ // `Retained` is `#[repr(transparent)]` over `NonNull`.
+ let ptr: NonNull<NonNull<T>> = ptr.cast();
+
+ // SAFETY: The value came from `&mut _`, and we only read a pointer.
+ let old: NonNull<T> = unsafe { *ptr.as_ptr() };
+
+ // `NonNull<T>` has the same layout as `*mut T`.
+ let ptr: NonNull<*mut T> = ptr.cast();
+
+ (ptr, WritebackOnDrop { ptr, old })
+ }
+}
+
+#[derive(Debug)]
+pub struct WritebackOnDrop<T: Message> {
+ /// A copy of the argument, so that we can retain it after the message
+ /// send.
+ ///
+ /// Ideally, we'd work with e.g. `&mut *mut T`, but we can't do that
+ /// inside the generic context of `MessageArguments::__invoke`, while
+ /// working within Rust's aliasing rules.
+ ptr: NonNull<*mut T>,
+ /// The old value, stored so that we can release if after the message
+ /// send.
+ old: NonNull<T>,
+}
+
+impl<T: Message> Drop for WritebackOnDrop<T> {
+ #[inline]
+ fn drop(&mut self) {
+ // In terms of provenance, we roughly want to do the following:
+ // ```
+ // fn do(value: &mut Retained<T>) {
+ // let old = value.clone();
+ // msg_send![... value ...];
+ // let _ = value.clone();
+ // drop(old);
+ // }
+ // ```
+ //
+ // Which is definitely valid under stacked borrows! See also this
+ // playground link for testing something equivalent in Miri:
+ // <https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=5ad8fcff1f870819081aa534ec754b86>
+ //
+ //
+ //
+ // In Objective-C terms, we want to retain the new value and release
+ // the old, and importantly, in that order (such that we don't dealloc
+ // the value if it didn't change). So something like this:
+ // ```
+ // fn do(value: &mut Retained) {
+ // let old = *value;
+ // msg_send![... value ...];
+ // objc_retain(*value);
+ // objc_release(old);
+ // }
+ // ```
+
+ // SAFETY: Caller ensures that the pointer is either left as-is, or is
+ // safe to retain at this point.
+ let new: Option<Retained<T>> = unsafe { Retained::retain(*self.ptr.as_ptr()) };
+ // We ignore the result of `retain`, since it always returns the same
+ // value as was given (and it would be unnecessary work to write that
+ // value back into `ptr` again).
+ let _new = ManuallyDrop::new(new);
+ #[cfg(debug_assertions)]
+ if _new.is_none() {
+ panic!("found that NULL was written to `&mut Retained<_>`, which is UB! You should handle this with `&mut Option<Retained<_>>` instead");
+ }
+
+ // SAFETY: The old pointer was valid when it was constructed.
+ //
+ // If the message send modified the argument, they would have left a
+ // +1 retain count on the old pointer; so either we have +1 from that,
+ // or the message send didn't modify the pointer and we instead have
+ // +1 retain count from the `retain` above.
+ let _: Retained<T> = unsafe { Retained::new_nonnull(self.old) };
+ }
+}
+
+impl<T: Message + 'static> ConvertArgument for &mut Option<Retained<T>> {
+ type __Inner = NonNull<*mut T>;
+
+ type __WritebackOnDrop = WritebackOnDropNullable<T>;
+
+ #[inline]
+ fn __from_defined_param(_inner: Self::__Inner) -> Self {
+ todo!("`&mut Option<Retained<_>>` is not supported in `define_class!` yet")
+ }
+
+ #[inline]
+ unsafe fn __into_argument(self) -> (Self::__Inner, Self::__WritebackOnDrop) {
+ let ptr: NonNull<Option<Retained<T>>> = NonNull::from(self);
+ // `Option<Retained<T>>` has the same memory layout as `*mut T`.
+ let ptr: NonNull<*mut T> = ptr.cast();
+ // SAFETY: Same as for `&mut Retained`
+ let old: *mut T = unsafe { *ptr.as_ptr() };
+
+ (ptr, WritebackOnDropNullable { ptr, old })
+ }
+}
+
+/// Mostly the same as `WritebackOnDrop`, except that the old value is
+/// nullable.
+#[derive(Debug)]
+pub struct WritebackOnDropNullable<T: Message> {
+ ptr: NonNull<*mut T>,
+ old: *mut T,
+}
+
+impl<T: Message> Drop for WritebackOnDropNullable<T> {
+ #[inline]
+ fn drop(&mut self) {
+ // SAFETY: Same as for `&mut Retained`
+ let new: Option<Retained<T>> = unsafe { Retained::retain(*self.ptr.as_ptr()) };
+ let _ = ManuallyDrop::new(new);
+
+ // SAFETY: Same as for `&mut Retained`
+ //
+ // Note: We explicitly keep the `if old == nil { objc_release(old) }`
+ // check, since we expect that the user would often do:
+ //
+ // ```
+ // let mut value = None
+ // do(&mut value);
+ // ```
+ //
+ // And in that case, we can elide the `objc_release`!
+ let _: Option<Retained<T>> = unsafe { Retained::from_raw(self.old) };
+ }
+}
+
+// Note: For `Option<&mut ...>` we explicitly want to do the `if Some` checks
+// before anything else, since whether `None` or `Some` was passed is often
+// known at compile-time, and for the `None` case it would be detrimental to
+// have extra `retain/release` calls here.
+
+impl<T: Message + 'static> ConvertArgument for Option<&mut Retained<T>> {
+ type __Inner = Option<NonNull<*mut T>>;
+
+ type __WritebackOnDrop = Option<WritebackOnDrop<T>>;
+
+ #[inline]
+ fn __from_defined_param(_inner: Self::__Inner) -> Self {
+ todo!("`Option<&mut Retained<_>>` is not supported in `define_class!` yet")
+ }
+
+ #[inline]
+ unsafe fn __into_argument(self) -> (Self::__Inner, Self::__WritebackOnDrop) {
+ if let Some(this) = self {
+ // SAFETY: Upheld by caller.
+ let (ptr, helper) = unsafe { this.__into_argument() };
+ (Some(ptr), Some(helper))
+ } else {
+ (None, None)
+ }
+ }
+}
+
+impl<T: Message + 'static> ConvertArgument for Option<&mut Option<Retained<T>>> {
+ type __Inner = Option<NonNull<*mut T>>;
+
+ type __WritebackOnDrop = Option<WritebackOnDropNullable<T>>;
+
+ #[inline]
+ fn __from_defined_param(_inner: Self::__Inner) -> Self {
+ todo!("`Option<&mut Option<Retained<_>>>` is not supported in `define_class!` yet")
+ }
+
+ #[inline]
+ unsafe fn __into_argument(self) -> (Self::__Inner, Self::__WritebackOnDrop) {
+ if let Some(this) = self {
+ // SAFETY: Upheld by caller.
+ let (ptr, stored) = unsafe { this.__into_argument() };
+ (Some(ptr), Some(stored))
+ } else {
+ (None, None)
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::panic::{catch_unwind, AssertUnwindSafe};
+
+ use super::*;
+ use crate::rc::{autoreleasepool, Allocated, RcTestObject, ThreadTestData};
+ use crate::{msg_send, ClassType};
+
+ #[test]
+ fn test_bool_error() {
+ let mut expected = ThreadTestData::current();
+
+ fn bool_error(should_error: bool, error: Option<&mut Option<Retained<RcTestObject>>>) {
+ let cls = RcTestObject::class();
+ let did_succeed: bool =
+ unsafe { msg_send![cls, boolAndShouldError: should_error, error: error] };
+ assert_ne!(should_error, did_succeed);
+ }
+
+ bool_error(false, None);
+ bool_error(true, None);
+ expected.assert_current();
+
+ fn helper(
+ expected: &mut ThreadTestData,
+ should_error: bool,
+ mut error: Option<Retained<RcTestObject>>,
+ ) {
+ autoreleasepool(|_| {
+ bool_error(should_error, Some(&mut error));
+ if should_error {
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.autorelease += 1;
+ }
+ expected.assert_current();
+ });
+
+ if should_error {
+ expected.release += 1;
+ }
+ expected.assert_current();
+
+ if error.is_some() {
+ expected.release += 1;
+ expected.drop += 1;
+ }
+ drop(error);
+ expected.assert_current();
+ }
+
+ helper(&mut expected, false, None);
+
+ expected.retain += 1;
+ helper(&mut expected, true, None);
+
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.retain += 1;
+ expected.release += 1;
+ helper(&mut expected, false, Some(RcTestObject::new()));
+
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.retain += 1;
+ expected.release += 1;
+ expected.drop += 1;
+ helper(&mut expected, true, Some(RcTestObject::new()));
+ }
+
+ #[test]
+ #[cfg_attr(
+ any(
+ not(debug_assertions),
+ all(not(target_pointer_width = "64"), feature = "catch-all")
+ ),
+ ignore = "invokes UB which is only caught with debug_assertions"
+ )]
+ #[should_panic = "found that NULL was written to `&mut Retained<_>`, which is UB! You should handle this with `&mut Option<Retained<_>>` instead"]
+ fn test_debug_check_ub() {
+ let cls = RcTestObject::class();
+ let mut param: Retained<_> = RcTestObject::new();
+ let _: () = unsafe { msg_send![cls, outParamNull: &mut param] };
+ }
+
+ // TODO: Fix this in release mode with Apple's runtime
+ const AUTORELEASE_SKIPPED: bool = cfg!(feature = "gnustep-1-7");
+
+ #[test]
+ fn test_retained_interaction() {
+ let mut expected = ThreadTestData::current();
+ let cls = RcTestObject::class();
+
+ let mut err: Retained<RcTestObject> = RcTestObject::new();
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.assert_current();
+
+ autoreleasepool(|_| {
+ let obj: Option<Retained<RcTestObject>> =
+ unsafe { msg_send![cls, idAndShouldError: false, error: &mut err] };
+ expected.alloc += 1;
+ expected.init += 1;
+ if !AUTORELEASE_SKIPPED {
+ expected.autorelease += 1;
+ expected.retain += 1;
+ }
+
+ expected.retain += 1;
+ expected.release += 1;
+ expected.assert_current();
+
+ drop(obj);
+ expected.release += 1;
+ if AUTORELEASE_SKIPPED {
+ expected.drop += 1;
+ }
+ expected.assert_current();
+ });
+ if !AUTORELEASE_SKIPPED {
+ expected.release += 1;
+ expected.drop += 1;
+ }
+ expected.assert_current();
+
+ drop(err);
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+ }
+
+ #[test]
+ fn test_error_alloc() {
+ let mut expected = ThreadTestData::current();
+
+ // Succeeds
+ let mut error: Option<Retained<RcTestObject>> = None;
+ let res: Allocated<RcTestObject> = unsafe {
+ msg_send![RcTestObject::class(), allocAndShouldError: false, error: &mut error]
+ };
+ expected.alloc += 1;
+ expected.assert_current();
+ assert!(!Allocated::as_ptr(&res).is_null());
+ assert!(error.is_none());
+
+ drop(res);
+ expected.release += 1;
+ // Drop flag ensures uninitialized do not drop
+ // expected.drop += 1;
+ expected.assert_current();
+
+ // Errors
+ let res: Retained<RcTestObject> = autoreleasepool(|_pool| {
+ let mut error = None;
+ let res: Allocated<RcTestObject> = unsafe {
+ msg_send![RcTestObject::class(), allocAndShouldError: true, error: &mut error]
+ };
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.autorelease += 1;
+ expected.retain += 1;
+ expected.assert_current();
+ assert!(Allocated::as_ptr(&res).is_null());
+ error.unwrap()
+ });
+ expected.release += 1;
+ expected.assert_current();
+
+ drop(res);
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+ }
+
+ fn will_panic(param: Option<&mut Option<Retained<RcTestObject>>>, panic_after: bool) {
+ unsafe { msg_send![RcTestObject::class(), willPanicWith: param, panicsAfter: panic_after] }
+ }
+
+ #[test]
+ #[cfg_attr(
+ feature = "catch-all",
+ ignore = "panics intentionally, which catch-all interferes with"
+ )]
+ fn basic_method_panics() {
+ let expected = ThreadTestData::current();
+
+ let res = catch_unwind(|| {
+ will_panic(None, false);
+ });
+ assert!(res.is_err());
+ expected.assert_current();
+
+ let res = catch_unwind(|| {
+ will_panic(None, true);
+ });
+ assert!(res.is_err());
+ expected.assert_current();
+ }
+
+ #[test]
+ #[cfg_attr(
+ any(feature = "catch-all", panic = "abort"),
+ ignore = "panics intentionally"
+ )]
+ fn method_panics() {
+ let cases = [
+ (false, None),
+ (true, None),
+ // Pre-existing parameter passed in.
+ (false, Some(RcTestObject::new())),
+ (true, Some(RcTestObject::new())),
+ ];
+
+ let mut expected = ThreadTestData::current();
+
+ for (panic_after, mut param) in cases {
+ let initially_set = param.is_some();
+
+ autoreleasepool(|_| {
+ let unwindsafe = AssertUnwindSafe(&mut param);
+ let res = catch_unwind(|| {
+ let param = unwindsafe;
+ will_panic(Some(param.0), panic_after);
+ });
+ assert!(res.is_err());
+
+ if panic_after {
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.autorelease += 1;
+ }
+ if panic_after || initially_set {
+ expected.retain += 1;
+ }
+ if initially_set {
+ expected.release += 1;
+ if panic_after {
+ expected.drop += 1;
+ }
+ }
+ expected.assert_current();
+ });
+
+ if panic_after {
+ expected.release += 1;
+ }
+ expected.assert_current();
+
+ drop(param);
+ if panic_after || initially_set {
+ expected.release += 1;
+ expected.drop += 1;
+ }
+ expected.assert_current();
+ }
+ }
+}
diff --git a/third_party/rust/objc2/src/downcast.rs b/third_party/rust/objc2/src/downcast.rs
@@ -0,0 +1,50 @@
+use crate::ClassType;
+
+/// Classes that can be safely downcasted to.
+///
+/// [`DowncastTarget`] is an unsafe marker trait that can be implemented on
+/// types that also implement [`ClassType`].
+///
+/// Ideally, every type that implements `ClassType` would also be a valid
+/// downcast target, however this would be unsound when used with generics,
+/// because we can only trivially decide whether the "base container" is an
+/// instance of some class type, but anything related to the generic arguments
+/// is unknown.
+///
+/// This trait is implemented automatically by the [`extern_class!`] and
+/// [`define_class!`] macros.
+///
+/// [`extern_class!`]: crate::extern_class
+/// [`define_class!`]: crate::define_class
+///
+///
+/// # Safety
+///
+/// The type must not have any generic arguments other than [`AnyObject`].
+///
+/// [`AnyObject`]: crate::runtime::AnyObject
+///
+///
+/// # Examples
+///
+/// Implementing [`DowncastTarget`] for `NSString`:
+///
+/// ```ignore
+/// // SAFETY: NSString does not have any generic parameters.
+/// unsafe impl DowncastTarget for NSString {}
+/// ```
+///
+/// However, implementing it for `NSArray` can only be done when the object
+/// type is `AnyObject`.
+///
+/// ```ignore
+/// // SAFETY: NSArray does not have any generic parameters set (the generic
+/// // defaults to `AnyObject`).
+/// unsafe impl DowncastTarget for NSArray {}
+///
+/// // This would not be valid, since downcasting can only trivially determine
+/// // whether the base class (in this case `NSArray`) matches the receiver
+/// // class type.
+/// // unsafe impl<T: Message> DowncastTarget for NSArray<T> {}
+/// ```
+pub unsafe trait DowncastTarget: ClassType + 'static {}
diff --git a/third_party/rust/objc2/src/encode.rs b/third_party/rust/objc2/src/encode.rs
@@ -0,0 +1,1033 @@
+//! # Support for type-encodings.
+//!
+//! The Objective-C runtime includes encodings for each method that describe
+//! the argument and return types. This module contains traits for annotating
+//! types that has an Objective-C type-encoding: Specifically [`Encode`] for
+//! structs/numeric types and [`RefEncode`] for references.
+//!
+//! Additionally, this exports the [`Encoding`] and [`EncodingBox`] types from
+//! [`objc2-encode`][objc2_encode], see that crate for a few more details on
+//! what Objective-C type-encodings are.
+//!
+//!
+//! ## Examples
+//!
+//! Implementing [`Encode`] and [`RefEncode`] for a custom type:
+//!
+//! ```
+//! use objc2::encode::{Encode, Encoding, RefEncode};
+//!
+//! #[repr(C)]
+//! struct MyStruct {
+//! a: f32, // float
+//! b: i16, // int16_t
+//! }
+//!
+//! unsafe impl Encode for MyStruct {
+//! const ENCODING: Encoding = Encoding::Struct(
+//! "MyStruct", // Must use the same name as defined in C header files
+//! &[
+//! f32::ENCODING, // Same as Encoding::Float
+//! i16::ENCODING, // Same as Encoding::Short
+//! ],
+//! );
+//! }
+//!
+//! // @encode(MyStruct) -> "{MyStruct=fs}"
+//! assert!(MyStruct::ENCODING.equivalent_to_str("{MyStruct=fs}"));
+//!
+//! unsafe impl RefEncode for MyStruct {
+//! const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
+//! }
+//!
+//! // @encode(MyStruct*) -> "^{MyStruct=fs}"
+//! assert!(MyStruct::ENCODING_REF.equivalent_to_str("^{MyStruct=fs}"));
+//! ```
+//!
+//! Implementing [`Encode`] for a few core-graphics types.
+//!
+//! Note that these are available in `objc2-foundation`, so the implementation
+//! here is mostly for demonstration.
+//!
+//! ```
+#![doc = include_str!("../examples/encode_core_graphics.rs")]
+//! ```
+//!
+//! Implementing [`Encode`] and [`RefEncode`] for a transparent newtype.
+//!
+//! ```
+#![doc = include_str!("../examples/encode_nsuinteger.rs")]
+//! ```
+//!
+//! Implementing [`RefEncode`] for an object, in this case `NSString`.
+//!
+//! ```
+#![doc = include_str!("../examples/encode_nsstring.rs")]
+//! ```
+//!
+//! Implementing [`RefEncode`] for a type where you don't necessarily know
+//! about the exact internals / the internals are not representable in Rust.
+//!
+//! ```
+#![doc = include_str!("../examples/encode_opaque_type.rs")]
+//! ```
+
+use core::cell::{Cell, UnsafeCell};
+use core::ffi::c_void;
+use core::mem::{self, ManuallyDrop, MaybeUninit};
+use core::num::{
+ NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU16, NonZeroU32,
+ NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping,
+};
+use core::ptr::NonNull;
+use core::sync::atomic;
+
+#[doc(inline)]
+pub use objc2_encode::{Encoding, EncodingBox, ParseError};
+
+use crate::runtime::{AnyObject, Imp, Sel};
+
+/// Types that have an Objective-C type-encoding.
+///
+/// Usually you will want to implement [`RefEncode`] as well.
+///
+/// If your type is an opaque type you should not need to implement this;
+/// there you will only need [`RefEncode`].
+///
+///
+/// # Safety
+///
+/// The type must be FFI-safe, meaning a C-compatible `repr` (`repr(C)`,
+/// `repr(u8)`, `repr(transparent)` where the inner types are C-compatible,
+/// and so on). See the [nomicon on other `repr`s][reprs].
+///
+/// Objective-C will make assumptions about the type (like its size, alignment
+/// and ABI) from its encoding, so the implementer must verify that the
+/// encoding is accurate.
+///
+/// Concretely, [`Self::ENCODING`] must match the result of running `@encode`
+/// in Objective-C with the type in question.
+///
+/// You should also beware of having [`Drop`] types implement this, since when
+/// passed to Objective-C via `objc2::msg_send!` their destructor will not be
+/// called!
+///
+///
+/// # Examples
+///
+/// Implementing for a struct:
+///
+/// ```
+/// # use objc2::encode::{Encode, Encoding, RefEncode};
+/// # use core::ffi::c_void;
+/// #
+/// #[repr(C)]
+/// struct MyType {
+/// a: i32,
+/// b: f64,
+/// c: *const c_void,
+/// }
+///
+/// unsafe impl Encode for MyType {
+/// const ENCODING: Encoding = Encoding::Struct(
+/// // The name of the type that Objective-C sees.
+/// "MyType",
+/// &[
+/// // Delegate to field's implementations.
+/// // The order is the same as in the definition.
+/// i32::ENCODING,
+/// f64::ENCODING,
+/// <*const c_void>::ENCODING,
+/// ],
+/// );
+/// }
+///
+/// // Note: You would also implement `RefEncode` for this type.
+/// ```
+///
+/// [reprs]: https://doc.rust-lang.org/nomicon/other-reprs.html
+pub unsafe trait Encode {
+ /// The Objective-C type-encoding for this type.
+ const ENCODING: Encoding;
+}
+
+/// Types whose references has an Objective-C type-encoding.
+///
+/// Implementing this for `T` provides [`Encode`] implementations for:
+/// - `*const T`
+/// - `*mut T`
+/// - `&T`
+/// - `&mut T`
+/// - `NonNull<T>`
+/// - `Option<&T>`
+/// - `Option<&mut T>`
+/// - `Option<NonNull<T>>`
+///
+///
+/// # Reasoning behind this trait's existence
+///
+/// External crates cannot implement [`Encode`] for pointers or [`Option`]s
+/// containing references, so instead, they can implement this trait.
+/// Additionally it would be very cumbersome if every type had to implement
+/// [`Encode`] for all possible pointer types.
+///
+/// Finally, having this trait allows for much cleaner generic code that need
+/// to represent types that can be encoded as pointers.
+///
+///
+/// # Safety
+///
+/// References to the object must be FFI-safe.
+///
+/// See the nomicon entry on [representing opaque structs][opaque] for
+/// information on how to represent objects that you don't know the layout of
+/// (or use `extern type` ([RFC-1861]) if you're using nightly).
+///
+/// Objective-C will make assumptions about the type (like its size, alignment
+/// and ABI) from its encoding, so the implementer must verify that the
+/// encoding is accurate.
+///
+/// Concretely, [`Self::ENCODING_REF`] must match the result of running
+/// `@encode` in Objective-C with a pointer to the type in question.
+///
+/// [opaque]: https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
+/// [RFC-1861]: https://rust-lang.github.io/rfcs/1861-extern-types.html
+pub unsafe trait RefEncode {
+ /// The Objective-C type-encoding for a reference of this type.
+ ///
+ /// Should be one of [`Encoding::Object`], [`Encoding::Block`],
+ /// [`Encoding::Class`], [`Encoding::Pointer`], [`Encoding::Sel`] or
+ /// [`Encoding::Unknown`].
+ ///
+ ///
+ /// # Examples
+ ///
+ /// This is usually implemented either as an object pointer:
+ /// ```
+ /// # use objc2::encode::{Encoding, RefEncode};
+ /// # #[repr(C)]
+ /// # struct MyObject {
+ /// # _priv: [u8; 0],
+ /// # }
+ /// # unsafe impl RefEncode for MyObject {
+ /// const ENCODING_REF: Encoding = Encoding::Object;
+ /// # }
+ /// ```
+ ///
+ /// Or as a pointer to the type, delegating the rest to the [`Encode`]
+ /// implementation:
+ /// ```
+ /// # use objc2::encode::{Encode, Encoding, RefEncode};
+ /// # #[repr(transparent)]
+ /// # struct MyType(i32);
+ /// # unsafe impl Encode for MyType {
+ /// # const ENCODING: Encoding = i32::ENCODING;
+ /// # }
+ /// # unsafe impl RefEncode for MyType {
+ /// const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
+ /// # }
+ /// ```
+ const ENCODING_REF: Encoding;
+}
+
+/// A helper trait for types that follow the "null pointer optimization", and
+/// are encodable inside an [`Option`].
+///
+/// See [the `Option` documentation][option-repr] for details on which types
+/// this holds for, and [the nomicon][nomicon-npo] for more details on the
+/// null pointer optimization.
+///
+/// This trait used to work around the orphan rule, which would normally
+/// prevent you from implementing [`Encode`]/[`RefEncode`] for
+/// `Option<CustomType>`.
+///
+/// [option-repr]: https://doc.rust-lang.org/1.75.0/std/option/index.html#representation
+/// [nomicon-npo]: https://doc.rust-lang.org/nightly/nomicon/ffi.html#the-nullable-pointer-optimization
+///
+///
+/// # Safety
+///
+/// You must ensure that the implemented type `T` has the same layout and
+/// function call ABI as `Option<T>`.
+///
+///
+/// # Examples
+///
+/// ```
+/// use objc2::encode::{Encode, Encoding, OptionEncode};
+/// use core::ptr::NonNull;
+/// use core::ffi::c_void;
+///
+/// #[repr(transparent)]
+/// struct MyBlockType(NonNull<c_void>);
+///
+/// // SAFETY: `MyBlockType` is meant to represent a pointer to a block
+/// unsafe impl Encode for MyBlockType {
+/// const ENCODING: Encoding = Encoding::Block;
+/// }
+///
+/// // SAFETY: `MyBlockType` is `repr(transparent)` over `NonNull`, which
+/// // means that `Option<MyBlockType>` has the same layout.
+/// unsafe impl OptionEncode for MyBlockType {}
+///
+/// assert_eq!(<Option<MyBlockType>>::ENCODING, MyBlockType::ENCODING);
+/// ```
+pub unsafe trait OptionEncode {}
+
+// SAFETY: Implementor of `OptionEncode` guarantees this impl is sound
+unsafe impl<T: Encode + OptionEncode> Encode for Option<T> {
+ const ENCODING: Encoding = {
+ if mem::size_of::<T>() != mem::size_of::<Option<T>>() {
+ panic!("invalid OptionEncode + Encode implementation");
+ }
+ T::ENCODING
+ };
+}
+
+// SAFETY: Implementor of `OptionEncode` guarantees this impl is sound
+unsafe impl<T: RefEncode + OptionEncode> RefEncode for Option<T> {
+ const ENCODING_REF: Encoding = {
+ if mem::size_of::<T>() != mem::size_of::<Option<T>>() {
+ panic!("invalid OptionEncode + RefEncode implementation");
+ }
+ T::ENCODING_REF
+ };
+}
+
+mod return_private {
+ pub trait Sealed {}
+}
+
+/// Types that are safe as the return value from Objective-C.
+///
+/// This is a sealed trait, and should not need to be implemented manually.
+///
+///
+/// # Safety
+///
+/// Similar to [`Encode`], except the value is only guaranteed to be valid as
+/// a return value, both from functions/methods you're calling, and from
+/// declared functions/methods.
+///
+/// It does not have to be valid as e.g. an instance variable, or as an
+/// argument to a function.
+pub unsafe trait EncodeReturn: return_private::Sealed {
+ /// The Objective-C type-encoding for this type.
+ const ENCODING_RETURN: Encoding;
+}
+
+impl return_private::Sealed for () {}
+// SAFETY: `()` is the same as C's `void` type, which is a valid return type
+unsafe impl EncodeReturn for () {
+ const ENCODING_RETURN: Encoding = Encoding::Void;
+}
+
+impl<T: Encode> return_private::Sealed for T {}
+// SAFETY: All `Encode` types are also valid as return types
+unsafe impl<T: Encode> EncodeReturn for T {
+ const ENCODING_RETURN: Encoding = T::ENCODING;
+}
+
+mod argument_private {
+ pub trait Sealed {}
+}
+
+/// Types that are safe as arguments to Objective-C methods.
+///
+/// This is a sealed trait, and should not need to be implemented manually.
+///
+///
+/// # Safety
+///
+/// Similar to [`Encode`], except the value is only guaranteed to be valid as
+/// an argument or a parameter, both from functions/methods you're calling and
+/// from declared functions/methods.
+///
+/// It does not have to be valid as e.g. an instance variable, or as an
+/// argument to a function.
+//
+// Note: This is mostly implemented for consistency; there are (not that I've
+// found at least) no places where this is not just `Encode`.
+//
+// You might be tempted to think that `bool` could work in this, but that
+// would be a mistake (even ignoring that its size is different on certain
+// targets) because it cannot be safely used in custom defined methods.
+pub unsafe trait EncodeArgument: argument_private::Sealed {
+ /// The Objective-C type-encoding for this type.
+ const ENCODING_ARGUMENT: Encoding;
+}
+
+impl<T: Encode> argument_private::Sealed for T {}
+// SAFETY: All `Encode` types are also valid as argument types
+unsafe impl<T: Encode> EncodeArgument for T {
+ const ENCODING_ARGUMENT: Encoding = T::ENCODING;
+}
+
+mod args_private {
+ pub trait Sealed {}
+}
+
+/// Types that represent an ordered group of function arguments, where each
+/// argument has an Objective-C type-encoding, or can be converted from one.
+///
+/// This is implemented for tuples of up to 16 arguments, where each argument
+/// implements [`EncodeArgument`]. It is a sealed trait, and should not need
+/// to be implemented manually - it is primarily used to make generic code a
+/// bit easier to read and understand.
+///
+/// Note that tuples themselves don't implement [`Encode`] directly, because
+/// they're not FFI-safe!
+pub trait EncodeArguments: args_private::Sealed {
+ /// The encodings for the arguments.
+ const ENCODINGS: &'static [Encoding];
+
+ /// Invoke a message sending function with the given object, selector,
+ /// and arguments.
+ ///
+ /// Implementation-wise, this is a bit ugly, but simply just easiest to
+ /// have the method on this trait, since inside `MessageReceiver` we only
+ /// want to publicly require `EncodeArguments`, and not another private
+ /// trait.
+ #[doc(hidden)]
+ unsafe fn __invoke<R: EncodeReturn>(
+ msg_send_fn: Imp,
+ receiver: *mut AnyObject,
+ sel: Sel,
+ args: Self,
+ ) -> R;
+}
+
+macro_rules! encode_args_impl {
+ ($($a:ident: $T: ident),*) => {
+ impl<$($T: EncodeArgument),*> args_private::Sealed for ($($T,)*) {}
+
+ impl<$($T: EncodeArgument),*> EncodeArguments for ($($T,)*) {
+ const ENCODINGS: &'static [Encoding] = &[
+ $($T::ENCODING_ARGUMENT),*
+ ];
+
+ #[inline]
+ unsafe fn __invoke<R: EncodeReturn>(msg_send_fn: Imp, receiver: *mut AnyObject, sel: Sel, ($($a,)*): Self) -> R {
+ // Message sending works by passing the receiver as the first
+ // argument, the selector as the second argument, and the rest
+ // of the arguments after that.
+ //
+ // The imp must be cast to the appropriate function pointer
+ // type before being called; contrary to how the headers and
+ // documentation describe them, the msgSend functions are not
+ // parametric on all platforms, instead they "trampoline" to
+ // the actual method implementations.
+ //
+ // SAFETY: We're transmuting an `unsafe` function pointer to
+ // another `unsafe` function pointer.
+ let msg_send_fn: unsafe extern "C-unwind" fn(*mut AnyObject, Sel $(, $T)*) -> R = unsafe {
+ mem::transmute(msg_send_fn)
+ };
+
+ // SAFETY: Caller upholds that the imp is safe to call with
+ // the given receiver, selector and arguments.
+ //
+ // TODO: On x86_64 it would be more efficient to use a GOT
+ // entry here (e.g. adding `nonlazybind` in LLVM).
+ // Same can be said of e.g. `objc_retain` and `objc_release`.
+ unsafe { msg_send_fn(receiver, sel $(, $a)*) }
+ }
+ }
+ };
+}
+
+encode_args_impl!();
+encode_args_impl!(a: A);
+encode_args_impl!(a: A, b: B);
+encode_args_impl!(a: A, b: B, c: C);
+encode_args_impl!(a: A, b: B, c: C, d: D);
+encode_args_impl!(a: A, b: B, c: C, d: D, e: E);
+encode_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F);
+encode_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);
+encode_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
+encode_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
+encode_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
+encode_args_impl!(
+ a: A,
+ b: B,
+ c: C,
+ d: D,
+ e: E,
+ f: F,
+ g: G,
+ h: H,
+ i: I,
+ j: J,
+ k: K
+);
+encode_args_impl!(
+ a: A,
+ b: B,
+ c: C,
+ d: D,
+ e: E,
+ f: F,
+ g: G,
+ h: H,
+ i: I,
+ j: J,
+ k: K,
+ l: L
+);
+encode_args_impl!(
+ a: A,
+ b: B,
+ c: C,
+ d: D,
+ e: E,
+ f: F,
+ g: G,
+ h: H,
+ i: I,
+ j: J,
+ k: K,
+ l: L,
+ m: M
+);
+encode_args_impl!(
+ a: A,
+ b: B,
+ c: C,
+ d: D,
+ e: E,
+ f: F,
+ g: G,
+ h: H,
+ i: I,
+ j: J,
+ k: K,
+ l: L,
+ m: M,
+ n: N
+);
+encode_args_impl!(
+ a: A,
+ b: B,
+ c: C,
+ d: D,
+ e: E,
+ f: F,
+ g: G,
+ h: H,
+ i: I,
+ j: J,
+ k: K,
+ l: L,
+ m: M,
+ n: N,
+ o: O
+);
+encode_args_impl!(
+ a: A,
+ b: B,
+ c: C,
+ d: D,
+ e: E,
+ f: F,
+ g: G,
+ h: H,
+ i: I,
+ j: J,
+ k: K,
+ l: L,
+ m: M,
+ n: N,
+ o: O,
+ p: P
+);
+
+// TODO: Implement for `PhantomData` and `PhantomPinned`?
+
+/// Helper for implementing [`Encode`].
+macro_rules! encode_impls {
+ ($($t:ty => $e:ident,)*) => ($(
+ unsafe impl Encode for $t {
+ const ENCODING: Encoding = Encoding::$e;
+ }
+ )*);
+}
+
+encode_impls!(
+ i8 => Char,
+ i16 => Short,
+ i32 => Int,
+ i64 => LongLong,
+ u8 => UChar,
+ u16 => UShort,
+ u32 => UInt,
+ u64 => ULongLong,
+ f32 => Float,
+ f64 => Double,
+
+ // TODO: i128 & u128
+ // https://github.com/rust-lang/rust/issues/54341
+);
+
+// TODO: Structs in core::arch?
+
+macro_rules! encode_impls_size {
+ ($($t:ty => ($t16:ty, $t32:ty, $t64:ty),)*) => ($(
+ // SAFETY: `usize` and `isize` is ABI compatible with `uN`/`iN` of the
+ // same size.
+ // <https://doc.rust-lang.org/nightly/std/primitive.fn.html#abi-compatibility>
+ #[doc = concat!("The encoding of [`", stringify!($t), "`] varies based on the target pointer width.")]
+ unsafe impl Encode for $t {
+ #[cfg(target_pointer_width = "16")]
+ const ENCODING: Encoding = <$t16>::ENCODING;
+ #[cfg(target_pointer_width = "32")]
+ const ENCODING: Encoding = <$t32>::ENCODING;
+ #[cfg(target_pointer_width = "64")]
+ const ENCODING: Encoding = <$t64>::ENCODING;
+ }
+ )*);
+}
+
+encode_impls_size!(
+ isize => (i16, i32, i64),
+ usize => (u16, u32, u64),
+);
+
+/// Helper for implementing [`RefEncode`].
+macro_rules! pointer_refencode_impl {
+ ($($t:ty),*) => ($(
+ unsafe impl RefEncode for $t {
+ const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
+ }
+ )*);
+}
+
+pointer_refencode_impl!(i16, i32, i64, isize, u16, u32, u64, usize, f32, f64);
+
+/// Pointers to [`i8`] use the special [`Encoding::String`] encoding.
+unsafe impl RefEncode for i8 {
+ const ENCODING_REF: Encoding = Encoding::String;
+}
+
+/// Pointers to [`u8`] use the special [`Encoding::String`] encoding.
+unsafe impl RefEncode for u8 {
+ const ENCODING_REF: Encoding = Encoding::String;
+}
+
+/// Helper for implementing [`Encode`] for nonzero integer types.
+macro_rules! encode_impls_nonzero {
+ ($($nonzero:ident => $type:ty,)*) => ($(
+ unsafe impl Encode for $nonzero {
+ const ENCODING: Encoding = <$type>::ENCODING;
+ }
+
+ unsafe impl RefEncode for $nonzero {
+ const ENCODING_REF: Encoding = <$type>::ENCODING_REF;
+ }
+
+ // SAFETY: nonzero types have a NUL niche that is exploited by Option
+ unsafe impl OptionEncode for $nonzero {}
+ )*);
+}
+
+encode_impls_nonzero!(
+ NonZeroI8 => i8,
+ NonZeroI16 => i16,
+ NonZeroI32 => i32,
+ NonZeroI64 => i64,
+ NonZeroIsize => isize,
+ NonZeroU8 => u8,
+ NonZeroU16 => u16,
+ NonZeroU32 => u32,
+ NonZeroU64 => u64,
+ NonZeroUsize => usize,
+);
+
+/// Helper for implementing for atomic types.
+macro_rules! encode_atomic_impls {
+ ($(
+ $(#[$m:meta])*
+ $atomic:ident => $type:ty,
+ )*) => ($(
+ // SAFETY: C11 `_Atomic` types use compatible synchronization
+ // primitives, and the atomic type is guaranteed to have the same
+ // in-memory representation as the underlying type.
+ $(#[$m])*
+ unsafe impl Encode for atomic::$atomic {
+ const ENCODING: Encoding = Encoding::Atomic(&<$type>::ENCODING);
+ }
+
+ $(#[$m])*
+ unsafe impl RefEncode for atomic::$atomic {
+ const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
+ }
+ )*);
+}
+
+encode_atomic_impls!(
+ #[cfg(target_has_atomic = "8")]
+ AtomicI8 => i8,
+ #[cfg(target_has_atomic = "8")]
+ AtomicU8 => u8,
+
+ #[cfg(target_has_atomic = "16")]
+ AtomicI16 => i16,
+ #[cfg(target_has_atomic = "16")]
+ AtomicU16 => u16,
+
+ #[cfg(target_has_atomic = "32")]
+ AtomicI32 => i32,
+ #[cfg(target_has_atomic = "32")]
+ AtomicU32 => u32,
+
+ #[cfg(target_has_atomic = "64")]
+ AtomicI64 => i64,
+ #[cfg(target_has_atomic = "64")]
+ AtomicU64 => u64,
+
+ // TODO
+ // #[cfg(target_has_atomic = "128")]
+ // AtomicI128 => i128,
+ // #[cfg(target_has_atomic = "128")]
+ // AtomicU128 => u128,
+
+ #[cfg(target_has_atomic = "ptr")]
+ AtomicIsize => isize,
+ #[cfg(target_has_atomic = "ptr")]
+ AtomicUsize => usize,
+);
+
+// SAFETY: Guaranteed to have the same in-memory representation as `*mut T`.
+#[cfg(target_has_atomic = "ptr")]
+unsafe impl<T: RefEncode> Encode for atomic::AtomicPtr<T> {
+ const ENCODING: Encoding = Encoding::Atomic(&T::ENCODING_REF);
+}
+
+#[cfg(target_has_atomic = "ptr")]
+unsafe impl<T: RefEncode> RefEncode for atomic::AtomicPtr<T> {
+ const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
+}
+
+unsafe impl RefEncode for c_void {
+ const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Void);
+}
+
+unsafe impl<T: Encode, const LENGTH: usize> Encode for [T; LENGTH] {
+ const ENCODING: Encoding = Encoding::Array(LENGTH as u64, &T::ENCODING);
+}
+
+unsafe impl<T: Encode, const LENGTH: usize> RefEncode for [T; LENGTH] {
+ const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
+}
+
+macro_rules! encode_impls_transparent {
+ ($($t:ident<T $(: ?$b:ident)?>,)*) => ($(
+ unsafe impl<T: Encode $(+ ?$b)?> Encode for $t<T> {
+ const ENCODING: Encoding = T::ENCODING;
+ }
+
+ unsafe impl<T: RefEncode $(+ ?$b)?> RefEncode for $t<T> {
+ const ENCODING_REF: Encoding = T::ENCODING_REF;
+ }
+ )*);
+}
+
+encode_impls_transparent! {
+ // SAFETY: Guaranteed to have the same layout as `T`, and is subject to
+ // the same layout optimizations as `T`.
+ // TODO: With specialization: `impl Encode for ManuallyDrop<Box<T>>`
+ ManuallyDrop<T: ?Sized>,
+
+ // SAFETY: Guaranteed to have the same in-memory representation `T`.
+ //
+ // The fact that this has `repr(no_niche)` has no effect on us, since we
+ // don't unconditionally implement `Encode` generically over `Option`.
+ // (e.g. an `Option<UnsafeCell<&u8>>` impl is not available).
+ UnsafeCell<T: ?Sized>,
+
+ // SAFETY: Guaranteed to have the same layout as `UnsafeCell<T>`.
+ Cell<T: ?Sized>,
+
+ // The inner field is not public, so may not be safe.
+ // TODO: Pin<T>,
+
+ // SAFETY: Guaranteed to have the same size, alignment, and ABI as `T`.
+ MaybeUninit<T>,
+
+ // SAFETY: Guaranteed to have the same layout and ABI as `T`.
+ Wrapping<T>,
+
+ // TODO: Types that need to be made repr(transparent) first:
+ // - core::cell::Ref?
+ // - core::cell::RefCell?
+ // - core::cell::RefMut?
+ // - core::panic::AssertUnwindSafe<T>
+ // TODO: core::num::Saturating when that is stabilized
+ // TODO: core::cmp::Reverse?
+}
+
+/// Helper for implementing `Encode`/`RefEncode` for pointers to types that
+/// implement `RefEncode`.
+///
+/// Using `?Sized` is safe here because we delegate to other implementations
+/// (which will verify that the implementation is safe for the unsized type).
+macro_rules! encode_pointer_impls {
+ (unsafe impl<T: RefEncode> $x:ident for Pointer<T> {
+ const $c:ident = $e:expr;
+ }) => (
+ unsafe impl<T: RefEncode + ?Sized> $x for *const T {
+ const $c: Encoding = $e;
+ }
+
+ unsafe impl<T: RefEncode + ?Sized> $x for *mut T {
+ const $c: Encoding = $e;
+ }
+
+ unsafe impl<'a, T: RefEncode + ?Sized> $x for &'a T {
+ const $c: Encoding = $e;
+ }
+
+ unsafe impl<'a, T: RefEncode + ?Sized> $x for &'a mut T {
+ const $c: Encoding = $e;
+ }
+
+ unsafe impl<T: RefEncode + ?Sized> $x for NonNull<T> {
+ const $c: Encoding = $e;
+ }
+ );
+}
+
+// Implement `Encode` for types that are `RefEncode`.
+//
+// This allows users to implement `Encode` for custom types that have a
+// specific encoding as a pointer, instead of having to implement it for each
+// pointer-like type in turn.
+encode_pointer_impls!(
+ unsafe impl<T: RefEncode> Encode for Pointer<T> {
+ const ENCODING = T::ENCODING_REF;
+ }
+);
+
+// Implement `RefEncode` for pointers to types that are `RefEncode`.
+//
+// This implements `Encode` for pointers to pointers (to pointers, and so on),
+// which would otherwise be very cumbersome to do manually.
+encode_pointer_impls!(
+ unsafe impl<T: RefEncode> RefEncode for Pointer<T> {
+ const ENCODING_REF = Encoding::Pointer(&T::ENCODING_REF);
+ }
+);
+
+// SAFETY: References and `NonNull` have a NULL niche
+unsafe impl<T: RefEncode + ?Sized> OptionEncode for &T {}
+unsafe impl<T: RefEncode + ?Sized> OptionEncode for &mut T {}
+unsafe impl<T: RefEncode + ?Sized> OptionEncode for NonNull<T> {}
+
+/// Helper for implementing [`Encode`]/[`RefEncode`] for function pointers
+/// whose arguments implement [`Encode`].
+///
+/// Ideally we'd implement it for all function pointers, but due to coherence
+/// issues, see <https://github.com/rust-lang/rust/issues/56105>, function
+/// pointers that are higher-ranked over lifetimes don't get implemented.
+///
+/// We could fix it by adding those impls and allowing `coherence_leak_check`,
+/// but it would have to be done for _all_ references, `Option<&T>` and such
+/// as well. So trying to do it quickly requires generating a polynomial
+/// amount of implementations, which IMO is overkill for such a small issue.
+///
+/// Using `?Sized` is probably not safe here because C functions can only take
+/// and return items with a known size.
+macro_rules! encode_fn_pointer_impl {
+ (@ $FnTy: ty, $($Arg: ident),*) => {
+ unsafe impl<Ret: EncodeReturn, $($Arg: EncodeArgument),*> Encode for $FnTy {
+ const ENCODING: Encoding = Encoding::Pointer(&Encoding::Unknown);
+ }
+ unsafe impl<Ret: EncodeReturn, $($Arg: EncodeArgument),*> RefEncode for $FnTy {
+ const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
+ }
+ // SAFETY: Function pointers have a NULL niche
+ unsafe impl<Ret: EncodeReturn, $($Arg: EncodeArgument),*> OptionEncode for $FnTy {}
+ };
+ (# $abi:literal; $($Arg: ident),+) => {
+ // Normal functions
+ encode_fn_pointer_impl!(@ extern $abi fn($($Arg),+) -> Ret, $($Arg),+ );
+ encode_fn_pointer_impl!(@ unsafe extern $abi fn($($Arg),+) -> Ret, $($Arg),+ );
+ // Variadic functions
+ encode_fn_pointer_impl!(@ extern $abi fn($($Arg),+ , ...) -> Ret, $($Arg),+ );
+ encode_fn_pointer_impl!(@ unsafe extern $abi fn($($Arg),+ , ...) -> Ret, $($Arg),+ );
+ };
+ (# $abi:literal; ) => {
+ // No variadic functions with 0 parameters
+ encode_fn_pointer_impl!(@ extern $abi fn() -> Ret, );
+ encode_fn_pointer_impl!(@ unsafe extern $abi fn() -> Ret, );
+ };
+ ($($Arg: ident),*) => {
+ encode_fn_pointer_impl!(# "C"; $($Arg),*);
+ encode_fn_pointer_impl!(# "C-unwind"; $($Arg),*);
+ };
+}
+
+// Up to 16 arguments
+encode_fn_pointer_impl!();
+encode_fn_pointer_impl!(A);
+encode_fn_pointer_impl!(A, B);
+encode_fn_pointer_impl!(A, B, C);
+encode_fn_pointer_impl!(A, B, C, D);
+encode_fn_pointer_impl!(A, B, C, D, E);
+encode_fn_pointer_impl!(A, B, C, D, E, F);
+encode_fn_pointer_impl!(A, B, C, D, E, F, G);
+encode_fn_pointer_impl!(A, B, C, D, E, F, G, H);
+encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I);
+encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I, J);
+encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I, J, K);
+encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I, J, K, L);
+encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M);
+encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
+encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
+encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use core::sync::atomic::*;
+
+ #[test]
+ fn test_c_string() {
+ assert_eq!(i8::ENCODING, Encoding::Char);
+ assert_eq!(u8::ENCODING, Encoding::UChar);
+
+ assert_eq!(<*const i8>::ENCODING, Encoding::String);
+ assert_eq!(<&u8>::ENCODING, Encoding::String);
+ assert_eq!(i8::ENCODING_REF, Encoding::String);
+ assert_eq!(i8::ENCODING_REF, Encoding::String);
+
+ assert_eq!(
+ <*const *const i8>::ENCODING,
+ Encoding::Pointer(&Encoding::String)
+ );
+ assert_eq!(<&&u8>::ENCODING, Encoding::Pointer(&Encoding::String));
+ }
+
+ #[test]
+ fn test_i32() {
+ assert_eq!(i32::ENCODING, Encoding::Int);
+ assert_eq!(<&i32>::ENCODING, Encoding::Pointer(&Encoding::Int));
+ assert_eq!(
+ <&&i32>::ENCODING,
+ Encoding::Pointer(&Encoding::Pointer(&Encoding::Int))
+ );
+ }
+
+ #[test]
+ fn test_atomic() {
+ assert_eq!(AtomicI32::ENCODING, Encoding::Atomic(&Encoding::Int));
+ assert_eq!(
+ AtomicI32::ENCODING_REF,
+ Encoding::Pointer(&Encoding::Atomic(&Encoding::Int))
+ );
+ assert_eq!(
+ AtomicPtr::<i32>::ENCODING,
+ Encoding::Atomic(&Encoding::Pointer(&Encoding::Int))
+ );
+
+ assert_eq!(AtomicI8::ENCODING, Encoding::Atomic(&Encoding::Char));
+ assert_eq!(
+ AtomicI8::ENCODING_REF,
+ Encoding::Pointer(&Encoding::Atomic(&Encoding::Char))
+ );
+ assert_eq!(
+ AtomicPtr::<i8>::ENCODING,
+ Encoding::Atomic(&Encoding::String)
+ );
+ }
+
+ #[test]
+ fn test_void() {
+ assert_eq!(
+ <*const c_void>::ENCODING,
+ Encoding::Pointer(&Encoding::Void)
+ );
+ assert_eq!(<&c_void>::ENCODING, Encoding::Pointer(&Encoding::Void));
+ assert_eq!(
+ <&*const c_void>::ENCODING,
+ Encoding::Pointer(&Encoding::Pointer(&Encoding::Void))
+ );
+ assert_eq!(
+ <AtomicPtr<c_void>>::ENCODING,
+ Encoding::Atomic(&Encoding::Pointer(&Encoding::Void))
+ );
+ }
+
+ #[test]
+ fn test_transparent() {
+ assert_eq!(<ManuallyDrop<u8>>::ENCODING, u8::ENCODING);
+ assert_eq!(<ManuallyDrop<&u8>>::ENCODING, u8::ENCODING_REF);
+ assert_eq!(<ManuallyDrop<Option<&u8>>>::ENCODING, u8::ENCODING_REF);
+ assert_eq!(<&ManuallyDrop<Option<&u8>>>::ENCODING, <&&u8>::ENCODING);
+
+ assert_eq!(<UnsafeCell<u8>>::ENCODING, u8::ENCODING);
+ assert_eq!(<UnsafeCell<&u8>>::ENCODING, <&u8>::ENCODING);
+ assert_eq!(<Cell<u8>>::ENCODING, u8::ENCODING);
+ assert_eq!(<Cell<&u8>>::ENCODING, <&u8>::ENCODING);
+ // assert_eq!(<Pin<u8>>::ENCODING, u8::ENCODING);
+ assert_eq!(<MaybeUninit<u8>>::ENCODING, u8::ENCODING);
+ assert_eq!(<Wrapping<u8>>::ENCODING, u8::ENCODING);
+ }
+
+ #[test]
+ fn test_extern_fn_pointer() {
+ assert_eq!(
+ <extern "C" fn()>::ENCODING,
+ Encoding::Pointer(&Encoding::Unknown)
+ );
+ assert_eq!(
+ <extern "C" fn(x: i32) -> u32>::ENCODING,
+ Encoding::Pointer(&Encoding::Unknown)
+ );
+ assert_eq!(
+ <Option<unsafe extern "C" fn()>>::ENCODING,
+ Encoding::Pointer(&Encoding::Unknown)
+ );
+ assert_eq!(
+ <extern "C-unwind" fn()>::ENCODING,
+ Encoding::Pointer(&Encoding::Unknown)
+ );
+ }
+
+ #[test]
+ fn test_extern_fn_pointer_elided_lifetime() {
+ fn impls_encode<T: Encode>(_x: T) {}
+
+ extern "C" fn my_fn1(_x: &i32) {}
+ extern "C" fn my_fn2(_x: &i32, _y: &u8) {}
+ extern "C" fn my_fn3(x: &u8) -> &u8 {
+ x
+ }
+ extern "C" fn my_fn4<'a>(x: &'a u8, _y: &i32) -> &'a u8 {
+ x
+ }
+
+ impls_encode(my_fn1 as extern "C" fn(_));
+ impls_encode(my_fn2 as extern "C" fn(_, _));
+ impls_encode(my_fn3 as extern "C" fn(_) -> _);
+ impls_encode(my_fn4 as extern "C" fn(_, _) -> _);
+ }
+
+ #[test]
+ fn test_return() {
+ assert_eq!(<i32>::ENCODING_RETURN, <i32>::ENCODING);
+ assert_eq!(<()>::ENCODING_RETURN, Encoding::Void);
+ }
+
+ #[test]
+ fn test_argument() {
+ assert_eq!(<i32>::ENCODING_ARGUMENT, <i32>::ENCODING);
+ }
+
+ #[test]
+ fn test_arguments() {
+ assert_eq!(<()>::ENCODINGS, &[] as &[Encoding]);
+ assert_eq!(<(i8,)>::ENCODINGS, &[i8::ENCODING]);
+ assert_eq!(<(i8, u32)>::ENCODINGS, &[i8::ENCODING, u32::ENCODING]);
+ }
+}
diff --git a/third_party/rust/objc2/src/exception.rs b/third_party/rust/objc2/src/exception.rs
@@ -0,0 +1,434 @@
+//! # `@throw` and `@try/@catch` exceptions.
+//!
+//! By default, if a message send (such as those generated with the
+//! [`msg_send!`] and [`extern_methods!`] macros) causes an exception to be
+//! thrown, `objc2` will simply let it unwind into Rust.
+//!
+//! While not UB, it will likely end up aborting the process, since Rust
+//! cannot catch foreign exceptions like Objective-C's. However, `objc2` has
+//! the `"catch-all"` Cargo feature, which, when enabled, wraps each message
+//! send in a `@catch` and instead panics if an exception is caught, which
+//! might lead to slightly better error messages.
+//!
+//! Most of the functionality in this module is only available when the
+//! `"exception"` feature is enabled.
+//!
+//! See the following links for more information:
+//! - [Exception Programming Topics for Cocoa](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Exceptions/Exceptions.html)
+//! - [The Objective-C Programming Language - Exception Handling](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocExceptionHandling.html)
+//! - [Exception Handling in LLVM](https://llvm.org/docs/ExceptionHandling.html)
+//!
+//! [`msg_send!`]: crate::msg_send
+
+// TODO: Test this with panic=abort, and ensure that the code-size is
+// reasonable in that case.
+
+#[cfg(feature = "exception")]
+use core::ffi::c_void;
+use core::ffi::CStr;
+use core::fmt;
+#[cfg(feature = "exception")]
+use core::mem;
+use core::ops::Deref;
+use core::panic::RefUnwindSafe;
+use core::panic::UnwindSafe;
+#[cfg(feature = "exception")]
+use core::ptr;
+use std::error::Error;
+
+use crate::encode::{Encoding, RefEncode};
+#[cfg(feature = "exception")]
+use crate::ffi;
+#[cfg(feature = "catch-all")]
+use crate::ffi::NSUInteger;
+#[cfg(feature = "catch-all")]
+use crate::msg_send;
+use crate::rc::{autoreleasepool_leaking, Retained};
+use crate::runtime::__nsstring::nsstring_to_str;
+use crate::runtime::{AnyClass, AnyObject, NSObject, NSObjectProtocol};
+use crate::{extern_methods, sel, Message};
+
+/// An Objective-C exception.
+///
+/// While highly recommended that any exceptions you intend to throw are
+/// subclasses of `NSException`, this is not required by the runtime (similar
+/// to how Rust can panic with arbitrary payloads using [`panic_any`]).
+///
+/// [`panic_any`]: std::panic::panic_any
+#[repr(transparent)]
+pub struct Exception(AnyObject);
+
+unsafe impl RefEncode for Exception {
+ const ENCODING_REF: Encoding = Encoding::Object;
+}
+
+unsafe impl Message for Exception {}
+
+impl Deref for Exception {
+ type Target = AnyObject;
+
+ #[inline]
+ fn deref(&self) -> &AnyObject {
+ &self.0
+ }
+}
+
+impl AsRef<AnyObject> for Exception {
+ #[inline]
+ fn as_ref(&self) -> &AnyObject {
+ self
+ }
+}
+
+impl Exception {
+ fn is_nsexception(&self) -> Option<bool> {
+ if self.class().responds_to(sel!(isKindOfClass:)) {
+ // SAFETY: We only use `isKindOfClass:` on NSObject
+ let obj: *const Exception = self;
+ let obj = unsafe { obj.cast::<NSObject>().as_ref().unwrap() };
+ // Get class dynamically instead of with `class!` macro
+ let name = CStr::from_bytes_with_nul(b"NSException\0").unwrap();
+ Some(obj.isKindOfClass(AnyClass::get(name)?))
+ } else {
+ Some(false)
+ }
+ }
+
+ #[cfg(feature = "catch-all")]
+ pub(crate) fn stack_trace(&self) -> impl fmt::Display + '_ {
+ struct Helper<'a>(&'a Exception);
+
+ impl fmt::Display for Helper<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if let Some(true) = self.0.is_nsexception() {
+ autoreleasepool_leaking(|pool| {
+ // SAFETY: The object is an `NSException`.
+ // Returns `NSArray<NSString *>`.
+ let call_stack_symbols: Option<Retained<NSObject>> =
+ unsafe { msg_send![self.0, callStackSymbols] };
+ if let Some(call_stack_symbols) = call_stack_symbols {
+ writeln!(f, "stack backtrace:")?;
+
+ // SAFETY: `call_stack_symbols` is an `NSArray`, and
+ // `count` returns `NSUInteger`.
+ let count: NSUInteger =
+ unsafe { msg_send![&call_stack_symbols, count] };
+ let mut i = 0;
+ while i < count {
+ // SAFETY: The index is in-bounds (so no exception will be thrown).
+ let symbol: Retained<NSObject> =
+ unsafe { msg_send![&call_stack_symbols, objectAtIndex: i] };
+ // SAFETY: The symbol is an NSString, and is not used
+ // beyond this scope.
+ let symbol = unsafe { nsstring_to_str(&symbol, pool) };
+ writeln!(f, "{symbol}")?;
+ i += 1;
+ }
+ }
+ Ok(())
+ })
+ } else {
+ Ok(())
+ }
+ }
+ }
+
+ Helper(self)
+ }
+}
+
+impl Exception {
+ extern_methods!(
+ // Only safe on NSException
+ // Returns NSString
+ #[unsafe(method(name))]
+ #[unsafe(method_family = none)]
+ unsafe fn name(&self) -> Option<Retained<NSObject>>;
+
+ // Only safe on NSException
+ // Returns NSString
+ #[unsafe(method(reason))]
+ #[unsafe(method_family = none)]
+ unsafe fn reason(&self) -> Option<Retained<NSObject>>;
+ );
+}
+
+// Note: We can't implement `Send` nor `Sync` since the exception could be
+// anything!
+
+impl fmt::Debug for Exception {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "exception ")?;
+
+ // Attempt to present a somewhat usable error message if the exception
+ // is an instance of NSException.
+ if let Some(true) = self.is_nsexception() {
+ autoreleasepool_leaking(|pool| {
+ // SAFETY: Just checked that object is an NSException
+ let (name, reason) = unsafe { (self.name(), self.reason()) };
+
+ // SAFETY:
+ // - `name` and `reason` are guaranteed to be `NSString`s.
+ // - We control the scope in which they are alive, so we know
+ // they are not moved outside the current autorelease pool.
+ //
+ // Note that these strings are immutable (`NSException` is
+ // immutable, and the properties are marked as `readonly` and
+ // `copy` and are copied upon creation), so we also don't have
+ // to worry about the string being mutated under our feet.
+ let name = name
+ .as_deref()
+ .map(|name| unsafe { nsstring_to_str(name, pool) });
+ let reason = reason
+ .as_deref()
+ .map(|reason| unsafe { nsstring_to_str(reason, pool) });
+
+ let obj: &AnyObject = self.as_ref();
+ write!(f, "{obj:?} '{}'", name.unwrap_or_default())?;
+ if let Some(reason) = reason {
+ write!(f, " reason: {reason}")?;
+ } else {
+ write!(f, " reason: (NULL)")?;
+ }
+ Ok(())
+ })
+ } else {
+ // Fall back to `AnyObject` Debug
+ write!(f, "{:?}", self.0)
+ }
+ }
+}
+
+impl fmt::Display for Exception {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ autoreleasepool_leaking(|pool| {
+ if let Some(true) = self.is_nsexception() {
+ // SAFETY: Just checked that object is an NSException
+ let reason = unsafe { self.reason() };
+
+ if let Some(reason) = &reason {
+ // SAFETY: Same as above in `Debug`.
+ let reason = unsafe { nsstring_to_str(reason, pool) };
+ return write!(f, "{reason}");
+ }
+ }
+
+ write!(f, "unknown exception")
+ })
+ }
+}
+
+impl Error for Exception {}
+
+impl UnwindSafe for Exception {}
+impl RefUnwindSafe for Exception {}
+
+/// Throws an Objective-C exception.
+///
+/// This is the Objective-C equivalent of Rust's [`panic!`].
+///
+/// This unwinds from Objective-C, and the exception should be caught using an
+/// Objective-C exception handler like [`catch`]. It _may_ be caught by
+/// [`catch_unwind`], though the error message is unlikely to be great.
+///
+/// [`catch_unwind`]: std::panic::catch_unwind
+#[inline]
+#[cfg(feature = "exception")] // For consistency, not strictly required
+pub fn throw(exception: Retained<Exception>) -> ! {
+ // We consume the exception object since we can't make any guarantees
+ // about its mutability.
+ let ptr: *const AnyObject = &exception.0;
+ let ptr = ptr as *mut AnyObject;
+ // SAFETY: The object is valid and non-null (nil exceptions are not valid
+ // in the old runtime).
+ unsafe { ffi::objc_exception_throw(ptr) }
+}
+
+#[cfg(feature = "exception")]
+fn try_no_ret<F: FnOnce()>(closure: F) -> Result<(), Option<Retained<Exception>>> {
+ let f = {
+ extern "C-unwind" fn try_objc_execute_closure<F>(closure: &mut Option<F>)
+ where
+ F: FnOnce(),
+ {
+ // This is always passed Some, so it's safe to unwrap
+ let closure = closure.take().unwrap();
+ closure();
+ }
+
+ let f: extern "C-unwind" fn(&mut Option<F>) = try_objc_execute_closure;
+ let f: extern "C-unwind" fn(*mut c_void) = unsafe { mem::transmute(f) };
+ f
+ };
+
+ // Wrap the closure in an Option so it can be taken
+ let mut closure = Some(closure);
+ let context: *mut Option<F> = &mut closure;
+ let context = context.cast();
+
+ let mut exception = ptr::null_mut();
+ // SAFETY: The function pointer and context are valid.
+ //
+ // The exception catching itself is sound on the Rust side, because we
+ // correctly use `extern "C-unwind"`. Objective-C does not completely
+ // specify how foreign unwinds are handled, though they do have the
+ // `@catch (...)` construct intended for catching C++ exceptions, so it is
+ // likely that they intend to support Rust panics (and it works in
+ // practice).
+ //
+ // See also:
+ // https://github.com/rust-lang/rust/pull/128321
+ // https://github.com/rust-lang/reference/pull/1226
+ let success = unsafe { objc2_exception_helper::try_catch(f, context, &mut exception) };
+
+ if success == 0 {
+ Ok(())
+ } else {
+ // SAFETY:
+ // The exception is always a valid object or NULL.
+ //
+ // Since we do a retain in `objc2_exception_helper/src/try_catch.m`,
+ // the object has +1 retain count.
+ Err(unsafe { Retained::from_raw(exception.cast()) })
+ }
+}
+
+/// Tries to execute the given closure and catches an Objective-C exception
+/// if one is thrown.
+///
+/// This is the Objective-C equivalent of Rust's [`catch_unwind`].
+/// Accordingly, if your Rust code is compiled with `panic=abort`, or your
+/// Objective-C code with `-fno-objc-exceptions`, this cannot catch the
+/// exception.
+///
+/// [`catch_unwind`]: std::panic::catch_unwind
+///
+///
+/// # Errors
+///
+/// Returns a `Result` that is either `Ok` if the closure succeeded without an
+/// exception being thrown, or an `Err` with the exception. The exception is
+/// automatically released.
+///
+/// The exception is `None` in the extremely exceptional case that the
+/// exception object is `nil`. This should basically never happen, but is
+/// technically possible on some systems with `@throw nil`, or in OOM
+/// situations.
+///
+///
+/// # Panics
+///
+/// This panics if the given closure panics.
+///
+/// That is, it completely ignores Rust unwinding and simply lets that pass
+/// through unchanged.
+///
+/// It may also not catch all Objective-C exceptions (such as exceptions
+/// thrown when handling the memory management of the exception). These are
+/// mostly theoretical, and should only happen in utmost exceptional cases.
+#[cfg(feature = "exception")]
+pub fn catch<R>(
+ closure: impl FnOnce() -> R + UnwindSafe,
+) -> Result<R, Option<Retained<Exception>>> {
+ let mut value = None;
+ let value_ref = &mut value;
+ let closure = move || {
+ *value_ref = Some(closure());
+ };
+ let result = try_no_ret(closure);
+ // If the try succeeded, value was set so it's safe to unwrap
+ result.map(|()| value.unwrap_or_else(|| unreachable!()))
+}
+
+#[cfg(test)]
+#[cfg(feature = "exception")]
+mod tests {
+ use alloc::format;
+ use alloc::string::ToString;
+ use core::panic::AssertUnwindSafe;
+ use std::panic::catch_unwind;
+
+ use super::*;
+ use crate::msg_send;
+
+ #[test]
+ fn test_catch() {
+ let mut s = "Hello".to_string();
+ let result = catch(move || {
+ s.push_str(", World!");
+ s
+ });
+ assert_eq!(result.unwrap(), "Hello, World!");
+ }
+
+ #[test]
+ #[cfg_attr(
+ all(target_os = "macos", target_arch = "x86"),
+ ignore = "`NULL` exceptions are invalid on 32-bit / w. fragile runtime"
+ )]
+ fn test_catch_null() {
+ let s = "Hello".to_string();
+ let result = catch(move || {
+ if !s.is_empty() {
+ unsafe { ffi::objc_exception_throw(ptr::null_mut()) }
+ }
+ s.len()
+ });
+ assert!(result.unwrap_err().is_none());
+ }
+
+ #[test]
+ #[cfg_attr(
+ feature = "catch-all",
+ ignore = "Panics inside `catch` when catch-all is enabled"
+ )]
+ fn test_catch_unknown_selector() {
+ let obj = AssertUnwindSafe(NSObject::new());
+ let ptr = Retained::as_ptr(&obj);
+ let result = catch(|| {
+ let _: Retained<NSObject> = unsafe { msg_send![&*obj, copy] };
+ });
+ let err = result.unwrap_err().unwrap();
+
+ assert_eq!(
+ format!("{err}"),
+ format!("-[NSObject copyWithZone:]: unrecognized selector sent to instance {ptr:?}"),
+ );
+ }
+
+ #[test]
+ fn test_throw_catch_object() {
+ let obj = NSObject::new();
+ // TODO: Investigate why this is required on GNUStep!
+ let _obj2 = obj.clone();
+ let obj: Retained<Exception> = unsafe { Retained::cast_unchecked(obj) };
+ let ptr: *const Exception = &*obj;
+
+ let result = catch(|| throw(obj));
+ let obj = result.unwrap_err().unwrap();
+
+ assert_eq!(format!("{obj:?}"), format!("exception <NSObject: {ptr:p}>"));
+
+ assert!(ptr::eq(&*obj, ptr));
+ }
+
+ #[test]
+ #[ignore = "currently aborts"]
+ fn throw_catch_unwind() {
+ let obj = NSObject::new();
+ let obj: Retained<Exception> = unsafe { Retained::cast_unchecked(obj) };
+
+ let result = catch_unwind(|| throw(obj));
+ let _ = result.unwrap_err();
+ }
+
+ #[test]
+ #[should_panic = "test"]
+ #[cfg_attr(
+ all(target_os = "macos", target_arch = "x86", panic = "unwind"),
+ ignore = "panic won't start on 32-bit / w. fragile runtime, it'll just abort, since the runtime uses setjmp/longjump unwinding"
+ )]
+ fn does_not_catch_panic() {
+ let _ = catch(|| panic!("test"));
+ }
+}
diff --git a/third_party/rust/objc2/src/ffi/class.rs b/third_party/rust/objc2/src/ffi/class.rs
@@ -0,0 +1,166 @@
+#![allow(non_camel_case_types)]
+use core::ffi::{c_char, c_int, c_uint};
+
+use crate::runtime::{AnyClass, AnyProtocol, Bool, Imp, Method, Sel};
+#[cfg(any(doc, not(feature = "unstable-objfw")))]
+use crate::{
+ ffi::{objc_property, objc_property_attribute_t},
+ runtime::Ivar,
+};
+
+#[cfg(any(doc, not(feature = "unstable-objfw")))]
+/// This is `c_char` in GNUStep's libobjc2 and `uint8_t` in Apple's objc4.
+///
+/// The pointer represents opaque data, and is definitely not just an integer,
+/// so its signedness (i8 vs. u8) is not applicable.
+///
+/// So we assign it here as a private alias to u8, to not document the
+/// difference.
+type ivar_layout_type = u8;
+
+// May call `resolveClassMethod:` or `resolveInstanceMethod:`.
+extern_c_unwind! {
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn class_getClassMethod(
+ cls: *const AnyClass,
+ name: Sel,
+ ) -> *const Method;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))] // Available in newer versions
+ pub fn class_getInstanceMethod(
+ cls: *const AnyClass,
+ name: Sel,
+ ) -> *const Method;
+
+ pub fn class_respondsToSelector(cls: *const AnyClass, sel: Sel) -> Bool;
+
+ // #[deprecated = "use class_getMethodImplementation instead"]
+ // #[cfg(any(doc, target_vendor = "apple"))]
+ // pub fn class_lookupMethod
+ // #[deprecated = "use class_respondsToSelector instead"]
+ // #[cfg(any(doc, target_vendor = "apple"))]
+ // pub fn class_respondsToMethod
+}
+
+// TODO: Hooks registered with objc_setHook_getClass may be allowed to unwind?
+extern_c! {
+ pub fn objc_getClass(name: *const c_char) -> *const AnyClass;
+ pub fn objc_getRequiredClass(name: *const c_char) -> *const AnyClass;
+ pub fn objc_lookUpClass(name: *const c_char) -> *const AnyClass;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn objc_getMetaClass(name: *const c_char) -> *const AnyClass;
+ /// The returned array is deallocated with [`free`][crate::ffi::free].
+ pub fn objc_copyClassList(out_len: *mut c_uint) -> *mut *const AnyClass;
+ pub fn objc_getClassList(buffer: *mut *const AnyClass, buffer_len: c_int) -> c_int;
+
+ pub fn objc_allocateClassPair(
+ superclass: *const AnyClass,
+ name: *const c_char,
+ extra_bytes: usize,
+ ) -> *mut AnyClass;
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn objc_duplicateClass(
+ original: *const AnyClass,
+ name: *const c_char,
+ extra_bytes: usize,
+ ) -> *mut AnyClass;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn objc_disposeClassPair(cls: *mut AnyClass);
+ pub fn objc_registerClassPair(cls: *mut AnyClass);
+
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn class_addIvar(
+ cls: *mut AnyClass,
+ name: *const c_char,
+ size: usize,
+ alignment: u8,
+ types: *const c_char,
+ ) -> Bool;
+ pub fn class_addMethod(
+ cls: *mut AnyClass,
+ name: Sel,
+ imp: Imp,
+ types: *const c_char,
+ ) -> Bool;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn class_addProperty(
+ cls: *mut AnyClass,
+ name: *const c_char,
+ attributes: *const objc_property_attribute_t,
+ attributes_count: c_uint,
+ ) -> Bool;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn class_addProtocol(cls: *mut AnyClass, protocol: *const AnyProtocol) -> Bool;
+ pub fn class_conformsToProtocol(cls: *const AnyClass, protocol: *const AnyProtocol)
+ -> Bool;
+
+ #[cfg(any(doc, not(feature = "unstable-objfw")))] // Available in newer versions
+ /// The return value is deallocated with [`free`][crate::ffi::free].
+ pub fn class_copyIvarList(
+ cls: *const AnyClass,
+ out_len: *mut c_uint,
+ ) -> *mut *const Ivar;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))] // Available in newer versions
+ /// The returned array is deallocated with [`free`][crate::ffi::free].
+ pub fn class_copyMethodList(
+ cls: *const AnyClass,
+ out_len: *mut c_uint,
+ ) -> *mut *const Method;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))] // Available in newer versions
+ /// The returned array is deallocated with [`free`][crate::ffi::free].
+ pub fn class_copyPropertyList(
+ cls: *const AnyClass,
+ out_len: *mut c_uint,
+ ) -> *mut *const objc_property;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ /// The returned array is deallocated with [`free`][crate::ffi::free].
+ pub fn class_copyProtocolList(
+ cls: *const AnyClass,
+ out_len: *mut c_uint,
+ ) -> *mut *const AnyProtocol;
+
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn class_createInstance(cls: *const AnyClass, extra_bytes: usize) -> *mut crate::runtime::AnyObject;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn class_getClassVariable(cls: *const AnyClass, name: *const c_char) -> *const Ivar;
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn class_getImageName(cls: *const AnyClass) -> *const c_char;
+ pub fn class_getInstanceSize(cls: *const AnyClass) -> usize;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn class_getInstanceVariable(
+ cls: *const AnyClass,
+ name: *const c_char,
+ ) -> *const Ivar;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn class_getIvarLayout(cls: *const AnyClass) -> *const ivar_layout_type;
+ pub fn class_getName(cls: *const AnyClass) -> *const c_char;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn class_getProperty(cls: *const AnyClass, name: *const c_char) -> *const objc_property;
+ pub fn class_getSuperclass(cls: *const AnyClass) -> *const AnyClass;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn class_getVersion(cls: *const AnyClass) -> c_int;
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn class_getWeakIvarLayout(cls: *const AnyClass) -> *const ivar_layout_type;
+ pub fn class_isMetaClass(cls: *const AnyClass) -> Bool;
+ pub fn class_replaceMethod(
+ cls: *mut AnyClass,
+ name: Sel,
+ imp: Imp,
+ types: *const c_char,
+ ) -> Option<Imp>;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn class_replaceProperty(
+ cls: *mut AnyClass,
+ name: *const c_char,
+ attributes: *const objc_property_attribute_t,
+ attributes_len: c_uint,
+ );
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn class_setIvarLayout(cls: *mut AnyClass, layout: *const ivar_layout_type);
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn class_setVersion(cls: *mut AnyClass, version: c_int);
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn class_setWeakIvarLayout(cls: *mut AnyClass, layout: *const ivar_layout_type);
+
+ // #[deprecated = "not recommended"]
+ // pub fn class_setSuperclass
+}
diff --git a/third_party/rust/objc2/src/ffi/constants.rs b/third_party/rust/objc2/src/ffi/constants.rs
@@ -0,0 +1,65 @@
+//! Various common #defines and enum constants.
+#![allow(non_camel_case_types)]
+
+#[cfg(any(doc, target_vendor = "apple"))]
+use core::ffi::c_int;
+
+use crate::runtime::{AnyClass, AnyObject};
+
+/// A quick alias for a [`null_mut`][`core::ptr::null_mut`] object / instance.
+pub const nil: *mut AnyObject = 0 as *mut _;
+
+/// A quick alias for a [`null_mut`][`core::ptr::null_mut`] class.
+pub const Nil: *mut AnyClass = 0 as *mut _;
+
+/// Policies related to associative references.
+///
+/// These are options to [`objc_setAssociatedObject`].
+///
+/// [`objc_setAssociatedObject`]: crate::ffi::objc_setAssociatedObject
+pub type objc_AssociationPolicy = usize;
+/// Specifies a weak reference to the associated object.
+///
+/// This performs straight assignment, without message sends.
+pub const OBJC_ASSOCIATION_ASSIGN: objc_AssociationPolicy = 0;
+/// Specifies a strong reference to the associated object.
+///
+/// The association is not made atomically.
+pub const OBJC_ASSOCIATION_RETAIN_NONATOMIC: objc_AssociationPolicy = 1;
+/// Specifies that the associated object is copied.
+///
+/// The association is not made atomically.
+pub const OBJC_ASSOCIATION_COPY_NONATOMIC: objc_AssociationPolicy = 3;
+/// Specifies a strong reference to the associated object.
+///
+/// The association is made atomically.
+pub const OBJC_ASSOCIATION_RETAIN: objc_AssociationPolicy = 0o1401;
+/// Specifies that the associated object is copied.
+///
+/// The association is made atomically.
+pub const OBJC_ASSOCIATION_COPY: objc_AssociationPolicy = 0o1403;
+
+#[cfg(any(doc, target_vendor = "apple"))]
+pub const OBJC_SYNC_SUCCESS: c_int = 0;
+#[cfg(any(doc, target_vendor = "apple"))]
+pub const OBJC_SYNC_NOT_OWNING_THREAD_ERROR: c_int = -1;
+/// Only relevant before macOS 10.13
+#[cfg(any(doc, target_vendor = "apple"))]
+pub const OBJC_SYNC_TIMED_OUT: c_int = -2;
+/// Only relevant before macOS 10.13
+#[cfg(any(doc, target_vendor = "apple"))]
+pub const OBJC_SYNC_NOT_INITIALIZED: c_int = -3;
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ #[test]
+ fn test_association_policy() {
+ assert_eq!(OBJC_ASSOCIATION_RETAIN, 769);
+ assert_eq!(OBJC_ASSOCIATION_COPY, 771);
+
+ // What the GNUStep headers define
+ assert_eq!(OBJC_ASSOCIATION_RETAIN, 0x301);
+ assert_eq!(OBJC_ASSOCIATION_COPY, 0x303);
+ }
+}
diff --git a/third_party/rust/objc2/src/ffi/exception.rs b/third_party/rust/objc2/src/ffi/exception.rs
@@ -0,0 +1,115 @@
+//! Defined in:
+//! Apple: `objc-exception.h`
+//! GNUStep: `eh_personality.c`, which is a bit brittle to rely on, but I
+//! think it's fine...
+#![allow(non_camel_case_types)]
+
+// A few things here are defined differently depending on the __OBJC2__
+// variable, which is set for all platforms except 32-bit macOS.
+
+use crate::runtime::AnyObject;
+
+/// Remember that this is non-null!
+#[cfg(any(
+ doc,
+ all(
+ target_vendor = "apple",
+ not(all(target_os = "macos", target_arch = "x86"))
+ )
+))]
+type objc_exception_matcher = unsafe extern "C" fn(
+ catch_type: *mut crate::runtime::AnyClass,
+ exception: *mut AnyObject,
+) -> core::ffi::c_int;
+
+/// Remember that this is non-null!
+#[cfg(any(
+ doc,
+ all(
+ target_vendor = "apple",
+ not(all(target_os = "macos", target_arch = "x86"))
+ )
+))]
+type objc_exception_preprocessor =
+ unsafe extern "C" fn(exception: *mut AnyObject) -> *mut AnyObject;
+
+/// Remember that this is non-null!
+#[cfg(any(
+ doc,
+ all(
+ target_vendor = "apple",
+ not(all(target_os = "macos", target_arch = "x86"))
+ )
+))]
+type objc_uncaught_exception_handler = unsafe extern "C" fn(exception: *mut AnyObject);
+
+#[cfg(feature = "unstable-objfw")]
+type objc_uncaught_exception_handler = Option<unsafe extern "C" fn(exception: *mut AnyObject)>;
+
+/// Remember that this is non-null!
+#[cfg(any(
+ doc,
+ all(target_vendor = "apple", target_os = "macos", not(target_arch = "x86"))
+))]
+type objc_exception_handler =
+ unsafe extern "C" fn(unused: *mut AnyObject, context: *mut core::ffi::c_void);
+
+extern_c_unwind! {
+ /// See [`objc-exception.h`].
+ ///
+ /// [`objc-exception.h`]: https://github.com/apple-oss-distributions/objc4/blob/objc4-818.2/runtime/objc-exception.h
+ #[cold]
+ pub fn objc_exception_throw(exception: *mut AnyObject) -> !;
+
+ #[cfg(all(target_vendor = "apple", not(feature = "gnustep-1-7"), not(all(target_os = "macos", target_arch = "x86"))))]
+ #[cold]
+ pub fn objc_exception_rethrow() -> !;
+
+ #[cfg(feature = "gnustep-1-7")]
+ #[cold]
+ pub fn objc_exception_rethrow(exc_buf: *mut core::ffi::c_void) -> !;
+}
+
+extern_c! {
+ #[cfg(any(doc, feature = "gnustep-1-7", all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86")))))]
+ pub fn objc_begin_catch(exc_buf: *mut core::ffi::c_void) -> *mut AnyObject;
+ #[cfg(any(doc, feature = "gnustep-1-7", all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86")))))]
+ pub fn objc_end_catch();
+
+ #[cfg(any(doc, all(target_vendor = "apple", target_os = "macos", target_arch = "x86")))]
+ pub fn objc_exception_try_enter(exception_data: *const core::ffi::c_void);
+
+ #[cfg(any(doc, all(target_vendor = "apple", target_os = "macos", target_arch = "x86")))]
+ pub fn objc_exception_try_exit(exception_data: *const core::ffi::c_void);
+
+ // objc_exception_extract
+ // objc_exception_match
+ // objc_exception_get_functions
+ // objc_exception_set_functions
+
+ #[cfg(any(doc, all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86")))))]
+ pub fn objc_setExceptionMatcher(f: objc_exception_matcher) -> objc_exception_matcher;
+ #[cfg(any(doc, all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86")))))]
+ pub fn objc_setExceptionPreprocessor(
+ f: objc_exception_preprocessor,
+ ) -> objc_exception_preprocessor;
+ #[cfg(any(doc, all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86"))), feature = "unstable-objfw"))]
+ pub fn objc_setUncaughtExceptionHandler(
+ f: objc_uncaught_exception_handler,
+ ) -> objc_uncaught_exception_handler;
+
+ #[cfg(any(doc, all(target_vendor = "apple", target_os = "macos", not(target_arch = "x86"))))]
+ pub fn objc_addExceptionHandler(f: objc_exception_handler, context: *mut core::ffi::c_void) -> usize;
+ #[cfg(any(doc, all(target_vendor = "apple", target_os = "macos", not(target_arch = "x86"))))]
+ pub fn objc_removeExceptionHandler(token: usize);
+
+ // Only available when ENABLE_OBJCXX is set, and a useable C++ runtime is
+ // present when building libobjc2.
+ //
+ // #[cfg(any(doc, feature = "gnustep-1-7"))]
+ // pub fn objc_set_apple_compatible_objcxx_exceptions(newValue: core::ffi::c_int) -> core::ffi::c_int;
+
+ #[cold]
+ #[cfg(any(doc, all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86")))))]
+ pub fn objc_terminate() -> !;
+}
diff --git a/third_party/rust/objc2/src/ffi/libc.rs b/third_party/rust/objc2/src/ffi/libc.rs
@@ -0,0 +1,19 @@
+use core::ffi::c_void;
+
+// SAFETY: The signatures in here are the exact same as in `libc`.
+extern_c! {
+ /// The Objective-C runtime has several methods, usually with "`copy`" in
+ /// their name, whose return value is allocated with C's `malloc` and
+ /// deallocated with C's `free` method.
+ ///
+ /// As such, `free` is actually also part of the Objective-C runtime.
+ ///
+ /// We expose this instead of using [`libc::free`], to avoid having `libc`
+ /// as a dependency.
+ ///
+ /// [`libc::free`]: https://docs.rs/libc/latest/libc/fn.free.html
+ //
+ // Note: This is linked automatically by either `std` or transitively by
+ // `libobjc`.
+ pub fn free(p: *mut c_void);
+}
diff --git a/third_party/rust/objc2/src/ffi/message.rs b/third_party/rust/objc2/src/ffi/message.rs
@@ -0,0 +1,87 @@
+//! The `objc_msgSend` family of functions.
+//!
+//! Most of these are `cfg`-gated, these configs are semver-stable.
+//!
+//! TODO: Some of these are only supported on _some_ GNUStep targets!
+use crate::runtime::{AnyClass, AnyObject};
+#[cfg(any(doc, feature = "gnustep-1-7", feature = "unstable-objfw"))]
+use crate::runtime::{Imp, Sel};
+
+/// Specifies data used when sending messages to superclasses.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+// TODO: Does this belong in this file or in types.rs?
+pub struct objc_super {
+ /// The object / instance to send a message to.
+ pub receiver: *mut AnyObject,
+ /// The particular superclass of the instance to message.
+ ///
+ /// Named `class` in older Objective-C versions.
+ pub super_class: *const AnyClass,
+}
+
+// All message sending functions should use "C-unwind"!
+//
+// Note that lookup functions won't throw exceptions themselves, but they can
+// call hooks, `resolveClassMethod:` and `resolveInstanceMethod:`, so we have
+// to make those "C-unwind" as well!
+extern_c_unwind! {
+ #[cfg(any(doc, feature = "gnustep-1-7", feature = "unstable-objfw"))]
+ pub fn objc_msg_lookup(receiver: *mut AnyObject, sel: Sel) -> Option<Imp>;
+ #[cfg(any(doc, feature = "unstable-objfw"))]
+ pub fn objc_msg_lookup_stret(receiver: *mut AnyObject, sel: Sel) -> Option<Imp>;
+ #[cfg(any(doc, feature = "gnustep-1-7", feature = "unstable-objfw"))]
+ pub fn objc_msg_lookup_super(sup: *const objc_super, sel: Sel) -> Option<Imp>;
+ #[cfg(any(doc, feature = "unstable-objfw"))]
+ pub fn objc_msg_lookup_super_stret(sup: *const objc_super, sel: Sel) -> Option<Imp>;
+ // #[cfg(any(doc, feature = "gnustep-1-7"))]
+ // objc_msg_lookup_sender
+ // objc_msgLookup family available in macOS >= 10.12
+
+ // objc_msgSend_noarg
+
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn objc_msgSend();
+ // objc_msgSend_debug
+
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn objc_msgSendSuper();
+ // objc_msgSendSuper2
+ // objc_msgSendSuper2_debug
+
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn method_invoke();
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn _objc_msgForward();
+ pub fn class_getMethodImplementation();
+
+ // Struct return. Not available on __arm64__:
+
+ #[cfg(any(doc, all(not(feature = "unstable-objfw"), not(target_arch = "aarch64"))))]
+ pub fn objc_msgSend_stret();
+ // objc_msgSend_stret_debug
+
+ #[cfg(any(doc, all(target_vendor = "apple", not(target_arch = "aarch64"))))]
+ pub fn objc_msgSendSuper_stret();
+ // objc_msgSendSuper2_stret
+ // objc_msgSendSuper2_stret_debug
+
+ #[cfg(any(doc, all(target_vendor = "apple", not(target_arch = "aarch64"))))]
+ pub fn method_invoke_stret();
+ #[cfg(any(doc, all(target_vendor = "apple", not(target_arch = "aarch64"))))]
+ pub fn _objc_msgForward_stret();
+ #[cfg(any(doc, feature = "unstable-objfw", not(target_arch = "aarch64")))]
+ pub fn class_getMethodImplementation_stret();
+
+ // __x86_64__ and __i386__
+
+ #[cfg(any(doc, all(not(feature = "unstable-objfw"), any(target_arch = "x86_64", target_arch = "x86"))))]
+ pub fn objc_msgSend_fpret();
+ // objc_msgSend_fpret_debug
+
+ // __x86_64__
+
+ #[cfg(any(doc, all(target_vendor = "apple", target_arch = "x86_64")))]
+ pub fn objc_msgSend_fp2ret();
+ // objc_msgSend_fp2ret_debug
+}
diff --git a/third_party/rust/objc2/src/ffi/method.rs b/third_party/rust/objc2/src/ffi/method.rs
@@ -0,0 +1,47 @@
+use core::ffi::c_char;
+#[cfg(any(doc, not(feature = "unstable-objfw")))]
+use core::ffi::c_uint;
+
+use crate::runtime::{Imp, Method, Sel};
+
+/// Describes an Objective-C method.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct objc_method_description {
+ /// The name of the method.
+ pub name: Option<Sel>,
+ /// The types of the method arguments.
+ pub types: *const c_char,
+}
+
+extern_c! {
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ /// The return value is deallocated with [`free`][crate::ffi::free].
+ pub fn method_copyArgumentType(method: *const Method, index: c_uint) -> *mut c_char;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ /// The return value is deallocated with [`free`][crate::ffi::free].
+ pub fn method_copyReturnType(method: *const Method) -> *mut c_char;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn method_exchangeImplementations(method1: *mut Method, method2: *mut Method);
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn method_getArgumentType(
+ method: *const Method,
+ index: c_uint,
+ dst: *mut c_char,
+ dst_len: usize,
+ );
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn method_getDescription(m: *const Method) -> *const objc_method_description;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn method_getImplementation(method: *const Method) -> Option<Imp>;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn method_getName(method: *const Method) -> Option<Sel>;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn method_getNumberOfArguments(method: *const Method) -> c_uint;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn method_getReturnType(method: *const Method, dst: *mut c_char, dst_len: usize);
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn method_getTypeEncoding(method: *const Method) -> *const c_char;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn method_setImplementation(method: *const Method, imp: Imp) -> Option<Imp>;
+}
diff --git a/third_party/rust/objc2/src/ffi/mod.rs b/third_party/rust/objc2/src/ffi/mod.rs
@@ -0,0 +1,302 @@
+//! # Raw bindings to Objective-C runtimes
+//!
+//! These bindings contain almost no documentation, so it is highly
+//! recommended to read Apple's [documentation about the Objective-C
+//! runtime][runtime-guide], Apple's [runtime reference][apple], or to use
+//! the [`runtime`] module which provides a higher-level API.
+//!
+//! [runtime-guide]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html
+//! [apple]: https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc
+//! [`runtime`]: crate::runtime
+//!
+//!
+//! ## Runtime Support
+//!
+//! Objective-C has a runtime, different implementations of said runtime
+//! exist, and they act in slightly different ways. By default, Apple
+//! platforms link to Apple's runtime, but if you're using another runtime you
+//! must tell it to this library using feature flags (you might have to
+//! disable the default `apple` feature first).
+//!
+//! One could ask, why even bother supporting other runtimes? For me, the
+//! primary reasoning iss _robustness_. By testing with these alternative
+//! runtimes in CI, we become by extension much more confident that our
+//! implementation doesn't rely on brittle unspecified behaviour, and works
+//! across different macOS and iOS versions.
+//!
+//!
+//! ### Apple's [`objc4`](https://github.com/apple-oss-distributions/objc4)
+//!
+//! - Feature flag: `apple`.
+//!
+//! This is used by default, and has the highest support priority (all of
+//! `objc2` will work with this runtime).
+//!
+//!
+//! ### GNUStep's [`libobjc2`](https://github.com/gnustep/libobjc2)
+//!
+//! - Feature flag: `gnustep-1-7`, `gnustep-1-8`, `gnustep-1-9`, `gnustep-2-0`
+//! and `gnustep-2-1` depending on the version you're using.
+//!
+//!
+//! ### Microsoft's [`WinObjC`](https://github.com/microsoft/WinObjC)
+//!
+//! - Feature flag: `unstable-winobjc`.
+//!
+//! **Unstable: Hasn't been tested on Windows yet!**
+//!
+//! [A fork](https://github.com/microsoft/libobjc2) based on GNUStep's
+//! `libobjc2` version 1.8, with very few user-facing changes.
+//!
+//!
+//! ### [`ObjFW`](https://github.com/ObjFW/ObjFW)
+//!
+//! - Feature flag: `unstable-objfw`.
+//!
+//! **Unstable: Doesn't work yet!**
+//!
+//! TODO.
+//!
+//!
+//! ### Other runtimes
+//!
+//! This library will probably only ever support ["Modern"][modern]
+//! Objective-C runtimes, since support for reference-counting primitives like
+//! `objc_retain` and `objc_autoreleasePoolPop` is a vital requirement for
+//! most applications.
+//!
+//! This rules out the GCC [`libobjc`][gcc-libobjc] runtime (see
+//! [this][gcc-objc-support]), the [`mulle-objc`] runtime and [cocotron]. (But
+//! support for [`darling`] may be added). More information on different
+//! runtimes can be found in GNUStep's [Objective-C Compiler and Runtime
+//! FAQ][gnustep-faq].
+//!
+//! [modern]: https://en.wikipedia.org/wiki/Objective-C#Modern_Objective-C
+//! [gcc-libobjc]: https://github.com/gcc-mirror/gcc/tree/master/libobjc
+//! [gcc-objc-support]: https://gcc.gnu.org/onlinedocs/gcc/Standards.html#Objective-C-and-Objective-C_002b_002b-Languages
+//! [`mulle-objc`]: https://github.com/mulle-objc/mulle-objc-runtime
+//! [cocotron]: https://cocotron.org/
+//! [`darling`]: https://github.com/darlinghq/darling-objc4
+//! [gnustep-faq]: http://wiki.gnustep.org/index.php/Objective-C_Compiler_and_Runtime_FAQ
+//!
+//!
+//! ## Objective-C Compiler configuration
+//!
+//! Objective-C compilers like `clang` and `gcc` requires configuring the
+//! calling ABI to the runtime you're using:
+//! - `clang` uses the [`-fobjc-runtime`] flag, of which there are a few
+//! different [options][clang-objc-kinds].
+//! - `gcc` uses the [`-fgnu-runtime` or `-fnext-runtime`][gcc-flags] options.
+//! Note that Modern Objective-C features are ill supported.
+//!
+//! Furthermore, there are various flags that are expected in modern
+//! Objective-C, that are off by default. In particular you might want to
+//! enable the `-fobjc-exceptions` and `-fobjc-arc` flags.
+//!
+//! Example usage in your `build.rs` (using the `cc` crate) would be as
+//! follows:
+//!
+//! ```ignore
+//! fn main() {
+//! let mut builder = cc::Build::new();
+//! builder.compiler("clang");
+//! builder.file("my_objective_c_script.m");
+//!
+//! builder.flag("-fobjc-exceptions");
+//! builder.flag("-fobjc-arc");
+//! builder.flag("-fobjc-runtime=..."); // If not compiling for Apple
+//!
+//! builder.compile("libmy_objective_c_script.a");
+//! }
+//! ```
+//!
+//! [`-fobjc-runtime`]: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fobjc-runtime
+//! [clang-objc-kinds]: https://clang.llvm.org/doxygen/classclang_1_1ObjCRuntime.html#af19fe070a7073df4ecc666b44137c4e5
+//! [gcc-flags]: https://gcc.gnu.org/onlinedocs/gcc/Objective-C-and-Objective-C_002b_002b-Dialect-Options.html
+//!
+//!
+//! ## Design choices
+//!
+//! It is recognized that the most primary consumer of this module will be
+//! macOS and secondly iOS applications. Therefore it was chosen not to use
+//! `bindgen`[^1] in our build script to not add compilation cost to those
+//! targets.
+//!
+//! Deprecated functions are also not included for future compatibility, since
+//! they could be removed in any macOS release, and then our code would break.
+//! If you have a need for these, please open an issue and we can discuss it!
+//!
+//! Some items (in particular the `objc_msgSend_X` family) have `cfg`s that
+//! prevent their usage on different platforms; these are **semver-stable** in
+//! the sense that they will only get less restrictive, never more.
+//!
+//! [^1]: That said, most of this is created with the help of `bindgen`'s
+//! commandline interface, so huge thanks to them!
+
+#![allow(clippy::upper_case_acronyms)]
+#![allow(non_camel_case_types)]
+#![allow(non_upper_case_globals)]
+#![allow(non_snake_case)]
+#![allow(missing_debug_implementations)]
+#![allow(missing_docs)]
+
+use core::cell::UnsafeCell;
+use core::marker::{PhantomData, PhantomPinned};
+
+macro_rules! generate_linking_tests {
+ {
+ extern $abi:literal {$(
+ $(#[$m:meta])*
+ $v:vis fn $name:ident(
+ $($(#[$a_m:meta])* $a:ident: $t:ty),* $(,)?
+ ) $(-> $r:ty)?;
+ )+}
+ mod $test_name:ident;
+ } => {
+ extern $abi {$(
+ $(#[$m])*
+ $v fn $name($($(#[$a_m])* $a: $t),*) $(-> $r)?;
+ )+}
+
+ #[allow(deprecated)]
+ #[cfg(test)]
+ mod $test_name {
+ #[allow(unused)]
+ use super::*;
+
+ $(
+ $(#[$m])*
+ #[test]
+ fn $name() {
+ // Get function pointer to make the linker require the
+ // symbol to be available.
+ let f: unsafe extern $abi fn($($(#[$a_m])* $t),*) $(-> $r)? = crate::ffi::$name;
+ // Execute side-effect to ensure it is not optimized away.
+ std::println!("{:p}", f);
+ }
+ )+
+ }
+ };
+}
+
+macro_rules! extern_c {
+ {
+ $(
+ $(#[$m:meta])*
+ $v:vis fn $name:ident(
+ $($(#[$a_m:meta])* $a:ident: $t:ty),* $(,)?
+ ) $(-> $r:ty)?;
+ )+
+ } => {
+ generate_linking_tests! {
+ extern "C" {$(
+ $(#[$m])*
+ $v fn $name($($(#[$a_m])* $a: $t),*) $(-> $r)?;
+ )+}
+ mod test_linkable;
+ }
+ };
+}
+
+// A lot of places may call `+initialize`, but the runtime guards those calls
+// with `@try/@catch` blocks already, so we don't need to mark every function
+// "C-unwind", only certain ones!
+macro_rules! extern_c_unwind {
+ {
+ $(
+ $(#[$m:meta])*
+ $v:vis fn $name:ident(
+ $($(#[$a_m:meta])* $a:ident: $t:ty),* $(,)?
+ ) $(-> $r:ty)?;
+ )+
+ } => {
+ generate_linking_tests! {
+ extern "C-unwind" {$(
+ $(#[$m])*
+ $v fn $name($($(#[$a_m])* $a: $t),*) $(-> $r)?;
+ )+}
+ mod test_linkable_unwind;
+ }
+ };
+}
+
+mod class;
+mod constants;
+mod exception;
+mod libc;
+mod message;
+mod method;
+mod object;
+mod property;
+mod protocol;
+mod rc;
+mod selector;
+mod types;
+mod various;
+
+pub use self::class::*;
+pub use self::constants::*;
+pub use self::exception::*;
+pub use self::libc::*;
+pub use self::message::*;
+pub use self::method::*;
+pub use self::object::*;
+pub use self::property::*;
+pub use self::protocol::*;
+pub use self::rc::*;
+pub use self::selector::*;
+pub use self::types::*;
+pub use self::various::*;
+
+#[deprecated = "merged with `runtime::AnyClass`"]
+pub type objc_class = crate::runtime::AnyClass;
+
+#[deprecated = "merged with `runtime::AnyObject`"]
+pub type objc_object = crate::runtime::AnyObject;
+
+#[deprecated = "merged with `runtime::Imp`, and made non-null"]
+pub type IMP = Option<crate::runtime::Imp>;
+
+#[deprecated = "merged with `runtime::Imp`"]
+pub type objc_method = crate::runtime::Method;
+
+#[deprecated = "merged with `runtime::Ivar`"]
+pub type objc_ivar = crate::runtime::Ivar;
+
+/// A mutable pointer to an object / instance.
+#[deprecated = "use `AnyObject` directly"]
+pub type id = *mut crate::runtime::AnyObject;
+
+#[deprecated = "use `runtime::Bool`, or if using `msg_send!`, just bool directly"]
+pub type BOOL = crate::runtime::Bool;
+
+#[deprecated = "use `runtime::Bool::YES`"]
+pub const YES: crate::runtime::Bool = crate::runtime::Bool::YES;
+
+#[deprecated = "use `runtime::Bool::NO`"]
+pub const NO: crate::runtime::Bool = crate::runtime::Bool::NO;
+
+/// We don't know much about the actual structs, so better mark them `!Send`,
+/// `!Sync`, `!UnwindSafe`, `!RefUnwindSafe`, `!Unpin` and as mutable behind
+/// shared references.
+///
+/// Downstream libraries can always manually opt in to these types afterwards.
+/// (It's also less of a breaking change on our part if we re-add these).
+///
+/// TODO: Replace this with `extern type` to also mark it as `!Sized`.
+pub(crate) type OpaqueData = UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>;
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use core::ffi::CStr;
+
+ #[test]
+ fn smoke() {
+ // Verify that this library links and works fine by itself
+ let name = CStr::from_bytes_with_nul(b"abc:def:\0").unwrap();
+ let sel = unsafe { sel_registerName(name.as_ptr()).unwrap() };
+ let rtn = unsafe { CStr::from_ptr(sel_getName(sel)) };
+ assert_eq!(name, rtn);
+ }
+}
diff --git a/third_party/rust/objc2/src/ffi/object.rs b/third_party/rust/objc2/src/ffi/object.rs
@@ -0,0 +1,99 @@
+use core::ffi::c_char;
+#[cfg(any(doc, not(feature = "unstable-objfw")))]
+use core::ffi::c_void;
+
+#[cfg(any(doc, not(feature = "unstable-objfw")))]
+use crate::runtime::Ivar;
+use crate::runtime::{AnyClass, AnyObject};
+
+extern_c! {
+ pub fn object_getClass(obj: *const AnyObject) -> *const AnyClass;
+ pub fn object_getClassName(obj: *const AnyObject) -> *const c_char;
+ pub fn object_setClass(obj: *mut AnyObject, cls: *const AnyClass) -> *const AnyClass;
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn object_isClass(obj: *const AnyObject) -> crate::runtime::Bool;
+
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn object_getIndexedIvars(obj: *const AnyObject) -> *const c_void;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn object_getIvar(obj: *const AnyObject, ivar: *const Ivar) -> *const AnyObject;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn object_setIvar(obj: *mut AnyObject, ivar: *const Ivar, value: *mut AnyObject);
+
+ #[deprecated = "Not needed since ARC"]
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn object_copy(obj: *const AnyObject, size: usize) -> *mut AnyObject;
+
+ #[deprecated = "Not needed since ARC"]
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn object_dispose(obj: *mut AnyObject) -> *mut AnyObject;
+
+ #[deprecated = "Not needed since ARC"]
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn object_setInstanceVariable(
+ obj: *mut AnyObject,
+ name: *const c_char,
+ value: *mut c_void,
+ ) -> *const Ivar;
+
+ // Available in macOS 10.12
+ // #[deprecated = "Not needed since ARC"]
+ // #[cfg(any(doc, target_vendor = "apple"))]
+ // pub fn object_setInstanceVariableWithStrongDefault(
+ // obj: *mut AnyObject,
+ // name: *const c_char,
+ // value: *mut c_void,
+ // ) -> *const Ivar;
+
+ #[deprecated = "Not needed since ARC"]
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn object_getInstanceVariable(
+ obj: *const AnyObject,
+ name: *const c_char,
+ out_value: *mut *const c_void,
+ ) -> *const Ivar;
+
+ #[deprecated = "Not needed since ARC"]
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn objc_getFutureClass(name: *const c_char) -> *const AnyClass;
+ #[deprecated = "Not needed since ARC"]
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn objc_constructInstance(cls: *const AnyClass, bytes: *mut c_void) -> *mut AnyObject;
+ #[deprecated = "Not needed since ARC"]
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn objc_destructInstance(obj: *mut AnyObject) -> *mut c_void;
+
+ // TODO: Unsure if we should expose these; are they useful, and stable?
+ // Defined in objc-abi.h
+ // pub fn objc_getProperty(
+ // obj: *const AnyObject,
+ // sel: Sel,
+ // offset: isize,
+ // atomic: Bool,
+ // ) -> *mut c_void;
+ // pub fn objc_setProperty(
+ // obj: *const AnyObject,
+ // sel: Sel,
+ // offset: isize,
+ // newValue: *const c_void,
+ // atomic: Bool,
+ // shouldCopy: i8,
+ // );
+ // + the atomic versions
+
+ // This is generated in setters to struct properties.
+ // pub fn objc_copyStruct(
+ // dest: *mut c_void,
+ // src: *const c_void,
+ // size: isize,
+ // atomic: Bool,
+ // hasStrong: Bool,
+ // );
+
+ // #[deprecated = "use object_copy instead"]
+ // #[cfg(any(doc, all(target_vendor = "apple", target_os = "macos")))]
+ // object_copyFromZone
+ // #[deprecated = "use class_createInstance instead"]
+ // #[cfg(any(doc, all(target_vendor = "apple", target_os = "macos")))]
+ // class_createInstanceFromZone
+}
diff --git a/third_party/rust/objc2/src/ffi/property.rs b/third_party/rust/objc2/src/ffi/property.rs
@@ -0,0 +1,42 @@
+use core::ffi::c_char;
+#[cfg(any(doc, not(feature = "unstable-objfw")))]
+use core::ffi::c_uint;
+
+use crate::ffi::OpaqueData;
+
+/// An opaque type that describes a property in a class.
+#[repr(C)]
+pub struct objc_property {
+ _priv: [u8; 0],
+ _p: OpaqueData,
+}
+
+/// Describes an Objective-C property attribute.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct objc_property_attribute_t {
+ /// The name of the attribute.
+ pub name: *const c_char,
+ /// The value of the attribute
+ ///
+ /// Usually NULL.
+ pub value: *const c_char,
+}
+
+extern_c! {
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ /// The returned array is deallocated with [`free`][crate::ffi::free].
+ pub fn property_copyAttributeList(
+ property: *const objc_property,
+ out_len: *mut c_uint,
+ ) -> *mut objc_property_attribute_t;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn property_copyAttributeValue(
+ property: *const objc_property,
+ attribute_name: *const c_char,
+ ) -> *mut c_char;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn property_getAttributes(property: *const objc_property) -> *const c_char;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn property_getName(property: *const objc_property) -> *const c_char;
+}
diff --git a/third_party/rust/objc2/src/ffi/protocol.rs b/third_party/rust/objc2/src/ffi/protocol.rs
@@ -0,0 +1,87 @@
+use core::ffi::c_char;
+#[cfg(any(doc, not(feature = "unstable-objfw")))]
+use core::ffi::c_uint;
+
+#[cfg(any(doc, not(feature = "unstable-objfw")))]
+use crate::ffi::{objc_method_description, objc_property, objc_property_attribute_t};
+use crate::runtime::{AnyProtocol, Bool, Sel};
+
+extern_c! {
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn objc_getProtocol(name: *const c_char) -> *const AnyProtocol;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ /// The returned array is deallocated with [`free`][crate::ffi::free].
+ pub fn objc_copyProtocolList(out_len: *mut c_uint) -> *mut *const AnyProtocol;
+
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn objc_allocateProtocol(name: *const c_char) -> *mut AnyProtocol;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn objc_registerProtocol(proto: *mut AnyProtocol);
+
+ pub fn protocol_conformsToProtocol(
+ proto: *const AnyProtocol,
+ other: *const AnyProtocol,
+ ) -> Bool;
+ pub fn protocol_isEqual(proto: *const AnyProtocol, other: *const AnyProtocol) -> Bool;
+ pub fn protocol_getName(proto: *const AnyProtocol) -> *const c_char;
+
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn protocol_addMethodDescription(
+ proto: *mut AnyProtocol,
+ name: Sel,
+ types: *const c_char,
+ is_required_method: Bool,
+ is_instance_method: Bool,
+ );
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn protocol_addProperty(
+ proto: *mut AnyProtocol,
+ name: *const c_char,
+ attributes: *const objc_property_attribute_t,
+ attributes_len: c_uint,
+ is_required_property: Bool,
+ is_instance_property: Bool,
+ );
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn protocol_addProtocol(proto: *mut AnyProtocol, addition: *const AnyProtocol);
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ /// The returned array is deallocated with [`free`][crate::ffi::free].
+ pub fn protocol_copyMethodDescriptionList(
+ proto: *const AnyProtocol,
+ is_required_method: Bool,
+ is_instance_method: Bool,
+ out_len: *mut c_uint,
+ ) -> *mut objc_method_description;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ /// The returned array is deallocated with [`free`][crate::ffi::free].
+ pub fn protocol_copyPropertyList(
+ proto: *const AnyProtocol,
+ out_len: *mut c_uint,
+ ) -> *mut *const objc_property;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ /// The returned array is deallocated with [`free`][crate::ffi::free].
+ pub fn protocol_copyProtocolList(
+ proto: *const AnyProtocol,
+ out_len: *mut c_uint,
+ ) -> *mut *const AnyProtocol;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn protocol_getMethodDescription(
+ proto: *const AnyProtocol,
+ sel: Sel,
+ is_required_method: Bool,
+ is_instance_method: Bool,
+ ) -> objc_method_description;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn protocol_getProperty(
+ proto: *const AnyProtocol,
+ name: *const c_char,
+ is_required_property: Bool,
+ is_instance_property: Bool,
+ ) -> *const objc_property;
+
+ // #[cfg(any(doc, macos >= 10.12))]
+ // protocol_copyPropertyList2
+
+ // #[cfg(any(doc, feature = "gnustep-1-7"))]
+ // _protocol_getMethodTypeEncoding
+}
diff --git a/third_party/rust/objc2/src/ffi/rc.rs b/third_party/rust/objc2/src/ffi/rc.rs
@@ -0,0 +1,83 @@
+//! ARC functions.
+//!
+//! These are documented in Clang's [documentation][ARC], and available since
+//! macOS `10.7` unless otherwise noted, so they are safe to rely on.
+//!
+//! Defined in:
+//! - Apple: `objc-internal.h`
+//! - GNUStep: `objc-arc.h`
+//! - ObjFW: `runtime/arc.m`
+//!
+//! [ARC]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#runtime-support
+use core::ffi::c_void;
+
+use crate::runtime::AnyObject;
+
+// All of these very rarely unwind, but may if the user defined methods
+// `retain`, `release`, `autorelease` or `dealloc` do.
+extern_c_unwind! {
+ // Autoreleasepool
+ // ObjFW: Defined in `autorelease.h`, not available with libobjfw-rt!
+
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn objc_autoreleasePoolPop(pool: *mut c_void);
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn objc_autoreleasePoolPush() -> *mut c_void;
+
+ // Autorelease
+
+ pub fn objc_autorelease(value: *mut AnyObject) -> *mut AnyObject;
+ pub fn objc_autoreleaseReturnValue(value: *mut AnyObject) -> *mut AnyObject;
+
+ // Weak pointers
+
+ pub fn objc_copyWeak(to: *mut *mut AnyObject, from: *mut *mut AnyObject);
+ pub fn objc_destroyWeak(addr: *mut *mut AnyObject);
+ pub fn objc_initWeak(addr: *mut *mut AnyObject, value: *mut AnyObject) -> *mut AnyObject;
+ // Defined in runtime.h
+ pub fn objc_loadWeak(addr: *mut *mut AnyObject) -> *mut AnyObject;
+ pub fn objc_loadWeakRetained(addr: *mut *mut AnyObject) -> *mut AnyObject;
+ pub fn objc_moveWeak(to: *mut *mut AnyObject, from: *mut *mut AnyObject);
+
+ // Retain / release
+
+ pub fn objc_release(value: *mut AnyObject);
+ pub fn objc_retain(value: *mut AnyObject) -> *mut AnyObject;
+ pub fn objc_retainAutorelease(value: *mut AnyObject) -> *mut AnyObject;
+ pub fn objc_retainAutoreleaseReturnValue(value: *mut AnyObject) -> *mut AnyObject;
+ pub fn objc_retainAutoreleasedReturnValue(value: *mut AnyObject) -> *mut AnyObject;
+ // Defined in objc-abi.h
+ pub fn objc_retainBlock(value: *mut AnyObject) -> *mut AnyObject;
+
+ // Storing values
+
+ pub fn objc_storeStrong(addr: *mut *mut AnyObject, value: *mut AnyObject);
+ // Defined in runtime.h
+ pub fn objc_storeWeak(addr: *mut *mut AnyObject, value: *mut AnyObject)
+ -> *mut AnyObject;
+
+ // TODO: Decide about nonstandard extensions like these:
+ // #[cfg(any(doc, feature = "gnustep-1-7"))]
+ // pub fn objc_delete_weak_refs(obj: *mut AnyObject) -> Bool;
+
+ // Fast paths for certain selectors.
+ //
+ // These are not defined in the ARC documentation, but are emitted by
+ // `clang` and included (and intended to be included) in the final
+ // binary, so very likely safe to use.
+ //
+ // TODO: Unsure why these are not available in the old fragile runtime,
+ // the headers seem to indicate that they are.
+ //
+ // <https://github.com/llvm/llvm-project/blob/llvmorg-17.0.5/clang/include/clang/Basic/ObjCRuntime.h#L229>
+
+ // Available since macOS 10.9.
+ #[cfg(any(doc, all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86")))))]
+ pub fn objc_alloc(value: *const crate::runtime::AnyClass) -> *mut AnyObject;
+
+ // Available since macOS 10.9.
+ #[cfg(any(doc, all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86")))))]
+ pub fn objc_allocWithZone(value: *const crate::runtime::AnyClass) -> *mut AnyObject;
+
+ // TODO: objc_alloc_init once supported
+}
diff --git a/third_party/rust/objc2/src/ffi/selector.rs b/third_party/rust/objc2/src/ffi/selector.rs
@@ -0,0 +1,15 @@
+use core::ffi::c_char;
+
+use crate::runtime::{Bool, Sel};
+
+extern_c! {
+ pub fn sel_getName(sel: Sel) -> *const c_char;
+ pub fn sel_isEqual(lhs: Sel, rhs: Sel) -> Bool;
+ pub fn sel_registerName(name: *const c_char) -> Option<Sel>;
+
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn sel_getUid(name: *const c_char) -> Option<Sel>;
+
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn sel_isMapped(sel: Sel) -> Bool;
+}
diff --git a/third_party/rust/objc2/src/ffi/types.rs b/third_party/rust/objc2/src/ffi/types.rs
@@ -0,0 +1,115 @@
+//! Objective-C type aliases.
+#![allow(non_camel_case_types)]
+
+// # Why isize/usize is correct for NSInteger/NSUInteger
+//
+// ## Apple
+// The documentation clearly states:
+//
+// > When building 32-bit applications, NSInteger is a 32-bit integer. A
+// 64-bit application treats NSInteger as a 64-bit integer.
+//
+// And the header file defines them like so:
+//
+// #if __LP64__ || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
+// typedef long NSInteger;
+// typedef unsigned long NSUInteger;
+// #else
+// typedef int NSInteger;
+// typedef unsigned int NSUInteger;
+// #endif
+//
+// Rust (or at least `libc`) has no targets where c_int/c_uint are not 32-bit,
+// so that part is correct. By manual inspection it is found that the only
+// platform where c_long/c_ulong differs from isize/usize is on Windows.
+// However Apple's libraries are only designed to work on 32-bit Windows, so
+// this case should be fine as well.
+//
+// Likewise for NSUInteger.
+//
+//
+// ## GNUStep / WinObjC
+//
+// Defined as intptr_t/uintptr_t, which is exactly the same as isize/usize.
+//
+//
+// ## ObjFW
+//
+// Doesn't define these, but e.g. -[OFString length] returns size_t, so our
+// definitions should be correct on effectively all targets.
+//
+// Things might change slightly in the future, see
+// <https://internals.rust-lang.org/t/pre-rfc-usize-is-not-size-t/15369>.
+
+/// A signed integer value type.
+///
+/// This is guaranteed to always be a type-alias to [`isize`]. That means it
+/// is valid to use `#[repr(isize)]` on enums and structs with size
+/// `NSInteger`.
+///
+/// See also the [corresponding documentation entry][docs].
+///
+/// [docs]: https://developer.apple.com/documentation/objectivec/nsinteger?language=objc
+///
+///
+/// # Examples
+///
+/// ```
+/// use core::mem::size_of;
+/// use objc2::ffi::NSInteger;
+///
+/// #[repr(isize)]
+/// pub enum NSComparisonResult {
+/// NSOrderedAscending = -1,
+/// NSOrderedSame = 0,
+/// NSOrderedDescending = 1,
+/// }
+///
+/// assert_eq!(size_of::<NSComparisonResult>(), size_of::<NSInteger>());
+/// ```
+pub type NSInteger = isize;
+
+/// Describes an unsigned integer.
+///
+/// This is guaranteed to always be a type-alias to [`usize`]. That means it
+/// is valid to use `#[repr(usize)]` on enums and structs with size
+/// `NSUInteger`.
+///
+/// See also the [corresponding documentation entry][docs].
+///
+/// [docs]: https://developer.apple.com/documentation/objectivec/nsuinteger?language=objc
+///
+///
+/// # Examples
+///
+/// ```
+/// use objc2::ffi::NSUInteger;
+///
+/// extern "C-unwind" {
+/// fn some_external_function() -> NSUInteger;
+/// }
+/// ```
+///
+/// ```
+/// use core::mem::size_of;
+/// use objc2::ffi::NSUInteger;
+///
+/// #[repr(usize)]
+/// enum CLRegionState {
+/// Unknown = 0,
+/// Inside = 1,
+/// Outside = 2,
+/// }
+///
+/// assert_eq!(size_of::<CLRegionState>(), size_of::<NSUInteger>());
+/// ```
+pub type NSUInteger = usize;
+
+/// The maximum value for a [`NSInteger`].
+pub const NSIntegerMax: NSInteger = NSInteger::MAX;
+
+/// The minimum value for a [`NSInteger`].
+pub const NSIntegerMin: NSInteger = NSInteger::MIN;
+
+/// The maximum value for a [`NSUInteger`].
+pub const NSUIntegerMax: NSUInteger = NSUInteger::MAX;
diff --git a/third_party/rust/objc2/src/ffi/various.rs b/third_party/rust/objc2/src/ffi/various.rs
@@ -0,0 +1,107 @@
+#[cfg(any(doc, not(feature = "unstable-objfw")))]
+use core::ffi::c_char;
+use core::ffi::c_int;
+#[cfg(any(doc, target_vendor = "apple"))]
+use core::ffi::c_uint;
+use core::ffi::c_void;
+
+use crate::runtime::AnyObject;
+use crate::runtime::Imp;
+#[cfg(any(doc, not(feature = "unstable-objfw")))]
+use crate::{
+ ffi::objc_AssociationPolicy,
+ runtime::{Bool, Ivar},
+};
+
+// /// Remember that this is non-null!
+// #[cfg(any(doc, all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86")))))]
+// type objc_hook_getClass =
+// unsafe extern "C" fn(name: *const c_char, out_cls: *mut *const crate::runtime::AnyClass) -> Bool;
+//
+// /// Remember that this is non-null!
+// #[cfg(any(doc, all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86")))))]
+// type objc_hook_lazyClassNamer =
+// unsafe extern "C" fn(cls: *const crate::runtime::AnyClass) -> *const c_char;
+
+extern_c_unwind! {
+ // Instead of being able to change this, it's a weak symbol on GNUStep.
+ #[cfg(any(doc, target_vendor = "apple", feature = "unstable-objfw"))]
+ pub fn objc_enumerationMutation(obj: *mut AnyObject);
+}
+
+extern_c! {
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn imp_getBlock(imp: Imp) -> *mut AnyObject;
+ // See also <https://landonf.org/code/objc/imp_implementationWithBlock.20110413.html>
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn imp_implementationWithBlock(block: *mut AnyObject) -> Imp;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn imp_removeBlock(imp: Imp) -> Bool;
+
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn ivar_getName(ivar: *const Ivar) -> *const c_char;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn ivar_getOffset(ivar: *const Ivar) -> isize;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn ivar_getTypeEncoding(ivar: *const Ivar) -> *const c_char;
+
+ #[cfg(any(doc, target_vendor = "apple"))]
+ pub fn objc_copyClassNamesForImage(
+ image: *const c_char,
+ out_len: *mut c_uint,
+ ) -> *mut *const c_char;
+ #[cfg(any(doc, target_vendor = "apple"))]
+ /// The returned array is deallocated with [`free`][crate::ffi::free].
+ pub fn objc_copyImageNames(out_len: *mut c_uint) -> *mut *const c_char;
+
+ #[cfg(any(doc, target_vendor = "apple", feature = "unstable-objfw"))]
+ pub fn objc_setEnumerationMutationHandler(
+ handler: Option<unsafe extern "C-unwind" fn(obj: *mut AnyObject)>,
+ );
+
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn objc_getAssociatedObject(
+ object: *const AnyObject,
+ key: *const c_void,
+ ) -> *const AnyObject;
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn objc_setAssociatedObject(
+ object: *mut AnyObject,
+ key: *const c_void,
+ value: *mut AnyObject,
+ policy: objc_AssociationPolicy,
+ );
+ #[cfg(any(doc, not(feature = "unstable-objfw")))]
+ pub fn objc_removeAssociatedObjects(object: *mut AnyObject);
+
+ #[cfg(any(doc, target_vendor = "apple", feature = "unstable-objfw"))]
+ pub fn objc_setForwardHandler(fwd: *mut c_void, fwd_stret: *mut c_void);
+ // These two are defined in:
+ // - Apple: objc-sync.h
+ // - GNUStep: dtable.h / associate.m
+ // - ObjFW: ObjFW-RT.h
+ pub fn objc_sync_enter(obj: *mut AnyObject) -> c_int;
+ pub fn objc_sync_exit(obj: *mut AnyObject) -> c_int;
+
+ // Available in macOS 10.14.4
+ // /// Remember that this is non-null!
+ // #[cfg(any(doc, all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86")))))]
+ // pub fn objc_setHook_getClass(
+ // new_value: objc_hook_getClass,
+ // out_old_value: *mut objc_hook_getClass,
+ // );
+ // Available in macOS 11
+ // /// Remember that this is non-null!
+ // #[cfg(any(doc, all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86")))))]
+ // pub fn objc_setHook_lazyClassNamer(
+ // new_value: objc_hook_lazyClassNamer,
+ // out_old_value: *mut objc_hook_lazyClassNamer,
+ // );
+
+ // #[deprecated = "not recommended"]
+ // #[cfg(any(doc, target_vendor = "apple"))]
+ // pub fn _objc_flush_caches
+
+ // #[cfg(any(doc, feature = "gnustep-1-7"))]
+ // objc_test_capability
+}
diff --git a/third_party/rust/objc2/src/lib.rs b/third_party/rust/objc2/src/lib.rs
@@ -0,0 +1,238 @@
+//! # Objective-C interface and runtime bindings
+//!
+//! Quick links:
+//! - [All Topics][crate::topics].
+//! - [All examples].
+//! - [About framework crates][crate::topics::about_generated].
+//! - [List of framework crates][crate::topics::about_generated::list].
+//!
+#![doc = concat!("[All examples]: https://github.com/madsmtm/objc2/tree/objc2-", env!("CARGO_PKG_VERSION"), "/examples")]
+//!
+//! Objective-C was the standard programming language on Apple platforms like
+//! macOS, iOS, iPadOS, tvOS and watchOS. It is an object-oriented language
+//! centered around "sending messages" to its instances - this can for the
+//! most part be viewed as a function call.
+//!
+//! It has since been superseded by Swift, but most of the core libraries and
+//! frameworks that are in use on Apple systems are still written in
+//! Objective-C, and hence we would like the ability to interact with these
+//! using Rust. This crate enables bi-directional interop with Objective-C, in
+//! as safe a manner as possible.
+//!
+//!
+//! ## Example
+//!
+//! Most of the time, you'll want to use one of [the framework crates], which
+//! contain bindings to `CoreFoundation`, `Foundation`, `AppKit`, `Metal`,
+//! `UIKit`, `WebKit` and so on.
+//!
+//! In this example we're going to be using [`objc2-foundation`] and
+//! [`objc2-app-kit`] to create a simple GUI application that displays a
+//! "Hello World" label.
+//!
+//! ```console
+//! $ # Add the necessary crates to your project.
+//! $ cargo add objc2 objc2-foundation objc2-app-kit
+//! ```
+//!
+#![cfg_attr(target_os = "macos", doc = "```no_run")]
+#![cfg_attr(not(target_os = "macos"), doc = "```ignore")]
+#![doc = include_str!("../examples/hello_world_app.rs")]
+//! ```
+//!
+//! [the framework crates]: crate::topics::about_generated
+//! [`objc2-foundation`]: https://docs.rs/objc2-foundation
+//! [`objc2-app-kit`]: https://docs.rs/objc2-app-kit
+//!
+//!
+//! ## Crate features
+//!
+//! This crate exports several optional cargo features, see [`Cargo.toml`] for
+//! an overview and description of these.
+//!
+//! The features in the framework crates are described [here][cr-feat]. Note
+//! that if you're developing a library for others to use, you might want to
+//! reduce compile times by disabling default features and only enabling the
+//! features you need.
+//!
+#![doc = concat!(
+ "[`Cargo.toml`]: https://docs.rs/crate/objc2/",
+ env!("CARGO_PKG_VERSION"),
+ "/source/Cargo.toml.orig",
+)]
+//! [cr-feat]: crate::topics::about_generated::cargo_features
+//!
+//!
+//! ## Supported operating systems
+//!
+//! - macOS: `10.12-15.5`
+//! - iOS: `10.0-18.5` (including iPadOS and Mac Catalyst)
+//! - tvOS: `10.0-18.5`
+//! - watchOS: `5.0-11.5`
+//! - visionOS: `1.0-2.5`
+//!
+//! The minimum versions are the same as those supported by `rustc`. Higher
+//! versions will also work, but the framework crates will not have bindings
+//! available for newer APIs.
+//!
+//! The framework bindings are generated from the SDKs in Xcode 16.4. The
+//! Xcode version are updated usually within a week of [GitHub Actions]
+//! supporting the new Xcode version, and we try to schedule crate releases
+//! such that align fairly closely with Xcode updates. We only support stable
+//! Xcode versions.
+//!
+//! Note that the bindings are currently generated in a very macOS-centric
+//! manner, so they may try to use types from AppKit, even on iOS, see for
+//! example [#637](https://github.com/madsmtm/objc2/issues/637).
+//!
+//! The bindings _can_ also be used on Linux or *BSD utilizing the
+//! [GNUstep Objective-C runtime](https://github.com/gnustep/libobjc2), see
+//! the [`ffi`] module for how to configure this, but this is very much
+//! second-class.
+//!
+//! [GitHub actions]: https://github.com/actions/runner-images
+//!
+//!
+//! ## Minimum Supported Rust Version (MSRV)
+//!
+//! The _currently_ minimum supported Rust version is `1.71` (to be able to
+//! use `extern "C-unwind"` functions); this is _not_ defined by policy,
+//! though, so it may change in at any time in a patch release.
+//!
+//! Help us define a policy over in [#203].
+//!
+//! [#203]: https://github.com/madsmtm/objc2/issues/203
+
+#![no_std]
+#![cfg_attr(
+ feature = "unstable-autoreleasesafe",
+ feature(negative_impls, auto_traits)
+)]
+#![cfg_attr(
+ feature = "unstable-arbitrary-self-types",
+ feature(arbitrary_self_types)
+)]
+#![cfg_attr(
+ feature = "unstable-coerce-pointee",
+ feature(derive_coerce_pointee, trait_upcasting)
+)]
+// Note: `doc_notable_trait` doesn't really make sense for us, it's only shown
+// for functions returning a specific trait.
+#![cfg_attr(docsrs, feature(doc_cfg))]
+#![cfg_attr(docsrs, doc(auto_cfg(hide(feature = "unstable-objfw"))))]
+#![warn(missing_docs)]
+#![warn(missing_debug_implementations)]
+#![warn(clippy::missing_errors_doc)]
+#![warn(clippy::missing_panics_doc)]
+// Update in Cargo.toml as well.
+#![doc(html_root_url = "https://docs.rs/objc2/0.6.3")]
+
+#[cfg(not(feature = "alloc"))]
+compile_error!("The `alloc` feature currently must be enabled.");
+
+#[cfg(not(feature = "std"))]
+compile_error!("The `std` feature currently must be enabled.");
+
+extern crate alloc;
+extern crate std;
+
+pub use self::downcast::DowncastTarget;
+#[doc(no_inline)]
+pub use self::encode::{Encode, Encoding, RefEncode};
+pub use self::main_thread_marker::MainThreadMarker;
+pub use self::top_level_traits::{
+ AnyThread, ClassType, DefinedClass, MainThreadOnly, Message, ProtocolType, ThreadKind,
+};
+
+#[cfg(any(feature = "unstable-static-sel", feature = "unstable-static-class"))]
+#[doc(hidden)]
+pub use objc2_proc_macros::__hash_idents;
+
+#[cfg(not(any(feature = "unstable-static-sel", feature = "unstable-static-class")))]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __hash_idents {
+ // Noop; used to make our other macros a bit easier to read
+ ($($x:tt)*) => {
+ ()
+ };
+}
+
+// Note: While this is not public, it is still a breaking change to change,
+// since framework crates rely on it.
+#[doc(hidden)]
+pub mod __framework_prelude;
+#[doc(hidden)]
+pub mod __macro_helpers;
+mod downcast;
+pub mod encode;
+pub mod exception;
+pub mod ffi;
+mod macros;
+mod main_thread_marker;
+pub mod rc;
+pub mod runtime;
+#[cfg(test)]
+mod test_utils;
+mod top_level_traits;
+#[cfg(any(docsrs, doc, doctest, test))]
+pub mod topics;
+mod verify;
+
+/// Deprecated location for a few things that are now in the [`runtime`]
+/// module.
+#[deprecated = "Moved to the `runtime` module"]
+pub mod declare {
+ use super::runtime;
+ pub use super::runtime::{ClassBuilder, ProtocolBuilder};
+
+ /// Use [`runtime::ClassBuilder`] instead.
+ #[deprecated = "Use `runtime::ClassBuilder` instead."]
+ pub type ClassDecl = runtime::ClassBuilder;
+
+ /// Use [`runtime::ProtocolBuilder`] instead.
+ #[deprecated = "Use `runtime::ProtocolBuilder` instead."]
+ pub type ProtocolDecl = runtime::ProtocolBuilder;
+}
+
+/// Deprecated alias of [`DefinedClass`].
+#[deprecated = "renamed to DefinedClass"]
+pub use DefinedClass as DeclaredClass;
+
+/// Deprecated alias of [`AnyThread`].
+#[deprecated = "renamed to AnyThread"]
+pub use AnyThread as AllocAnyThread;
+
+#[cfg(not(feature = "std"))]
+compile_error!("The `std` feature currently must be enabled.");
+
+#[cfg(all(
+ not(docsrs),
+ not(any(
+ target_vendor = "apple",
+ feature = "unstable-compiler-rt",
+ feature = "gnustep-1-7",
+ feature = "unstable-objfw",
+ ))
+))]
+compile_error!("`objc2` only works on Apple platforms. Pass `--target aarch64-apple-darwin` or similar to compile for macOS.\n(If you're absolutely certain that you're using GNUStep, you can specify that with the `gnustep-x-y` Cargo feature instead).");
+
+#[cfg(all(feature = "gnustep-1-7", feature = "unstable-objfw"))]
+compile_error!("Only one runtime may be selected");
+
+#[cfg(feature = "unstable-objfw")]
+compile_error!("ObjFW is not yet supported");
+
+// Link to libobjc
+#[cfg_attr(not(feature = "unstable-objfw"), link(name = "objc", kind = "dylib"))]
+// Link to libobjfw-rt
+#[cfg_attr(feature = "unstable-objfw", link(name = "objfw-rt", kind = "dylib"))]
+extern "C" {}
+
+// Link to Foundation to make NSObject and OS version lookup work.
+#[cfg_attr(target_vendor = "apple", link(name = "Foundation", kind = "framework"))]
+#[cfg_attr(
+ all(feature = "gnustep-1-7", not(feature = "unstable-compiler-rt")),
+ link(name = "gnustep-base", kind = "dylib")
+)]
+extern "C" {}
diff --git a/third_party/rust/objc2/src/macros/__attribute_helpers.rs b/third_party/rust/objc2/src/macros/__attribute_helpers.rs
@@ -0,0 +1,1041 @@
+/// Parse the given attributes, and gate the output on any `cfg` attributes
+/// that were present in the set.
+///
+/// This is implemented as a tt-muncher, taking the following arguments:
+/// - The attributes to be processed
+/// - The output that the `cfg` attributes will be attached to
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extract_and_apply_cfg_attributes {
+ // Base case
+ {
+ () // No attributes left to process
+ $($output:tt)*
+ } => {
+ $($output)*
+ };
+ // `cfg` attribute
+ {
+ (
+ #[cfg $($args:tt)*]
+ $($rest:tt)*
+ )
+ $($output:tt)*
+ } => {
+ // Apply the attribute and continue
+ #[cfg $($args)*]
+ {
+ $crate::__extract_and_apply_cfg_attributes! {
+ ($($rest)*)
+ $($output)*
+ }
+ }
+ };
+ // Other attributes
+ {
+ (
+ #[$($m_ignored:tt)*]
+ $($rest:tt)*
+ )
+ $($output:tt)*
+ } => {
+ // Ignore the attribute, and continue parsing the rest
+ $crate::__extract_and_apply_cfg_attributes! {
+ ($($rest)*)
+ $($output)*
+ }
+ };
+}
+
+/// Extract our custom method attributes, and send it to another macro.
+///
+/// Handles:
+/// - `#[unsafe(method(...))]` or `#[unsafe(method_id(...))]`.
+/// - `#[unsafe(method_family(...))]`.
+/// - `#[optional]`.
+///
+/// This will ensure that there is one and only one of the `method` attributes
+/// present.
+///
+/// This takes the following arguments:
+/// 1. The attributes to parse.
+/// ($($m:tt)*)
+///
+/// 2. The output macro.
+/// ($out_macro:path)
+///
+/// Further arguments are passed on to the output macro, with the following
+/// arguments appended to it:
+/// 1. The `method` or `method_id` attribute.
+/// ($method_or_method_id:ident($($sel:tt)*))
+///
+/// 2. The requested method family, if any was present.
+///
+/// One of `new`, `alloc`, `init`, `copy`, `mutableCopy` or `none`.
+/// ($($method_family:tt)*)
+///
+/// 3. The `optional` attribute, if any.
+/// ($(#[optional])?)
+///
+/// 4. The remaining attributes that should be placed on the method definition
+/// itself.
+/// ($(#[$($attr_method:tt)*])*)
+///
+/// 5. Attributes like `cfg` and `allow` that should be placed on the usage
+/// site of the method.
+/// ($(#[$($attr_use:tt)*])*)
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extract_method_attributes {
+ {
+ ($($m:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__extract_method_attributes_inner! {
+ ($($m)*)
+ // No already parsed attributes
+ () // method/method_id
+ () // method family
+ () // optional
+ () // attr_method
+ () // attr_use
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extract_method_attributes_inner {
+ // No method/method_id attribute found
+ {
+ // No attributes left to process
+ ()
+
+ // And we found no `method` or `method_id` attributes
+ ()
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__macro_helpers::compile_error!("must specify the desired selector using `#[unsafe(method(...))]` or `#[unsafe(method_id(...))]`");
+
+ // Try to output anyhow, for better UI.
+ $out_macro! {
+ $($out_args)*
+ // Append attributes to the end of the macro arguments
+ (method(invalidSelector))
+ ($($method_family)*)
+ ($($optional)*)
+ ($($attr_method)*)
+ ($($attr_use)*)
+ }
+ };
+
+ // Base case
+ {
+ // No attributes left to process
+ ()
+
+ // And we found a `method` or `method_id` attribute
+ ($($method:tt)*)
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ // Output
+ $out_macro! {
+ $($out_args)*
+ // Append attributes to the end of the macro arguments
+ ($($method)*)
+ ($($method_family)*)
+ ($($optional)*)
+ ($($attr_method)*)
+ ($($attr_use)*)
+ }
+ };
+
+ // `unsafe(method)` attribute
+ {
+ (
+ #[unsafe(method($($parsed:tt)*))]
+ $($rest:tt)*
+ )
+
+ ($($method:tt)*)
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__handle_duplicate!("method`/`method_id"; $($method)*);
+ $crate::__extract_method_attributes_inner! {
+ ($($rest)*)
+
+ // Add method attribute
+ (method($($parsed)*))
+ ($($method_family)*)
+ ($($optional)*)
+ ($($attr_method)*)
+ ($($attr_use)*)
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+
+ // `method` attribute
+ {
+ (
+ #[method($($parsed:tt)*)]
+ $($rest:tt)*
+ )
+
+ ($($method:tt)*)
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__macro_helpers::compile_error!($crate::__macro_helpers::concat!(
+ "The #[method] attribute is now unsafe, and must be used as #[unsafe(method(",
+ $crate::__macro_helpers::stringify!($($parsed)*),
+ "))]",
+ ));
+
+ // Continue for better UI.
+ $crate::__handle_duplicate!("method`/`method_id"; $($method)*);
+ $crate::__extract_method_attributes_inner! {
+ ($($rest)*)
+
+ (method($($parsed)*))
+ ($($method_family)*)
+ ($($optional)*)
+ ($($attr_method)*)
+ ($($attr_use)*)
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+
+ // `unsafe(method_id)` attribute
+ {
+ (
+ #[unsafe(method_id($($parsed:tt)*))]
+ $($rest:tt)*
+ )
+
+ ($($method:tt)*)
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__handle_duplicate!("method`/`method_id"; $($method)*);
+ $crate::__extract_method_attributes_inner! {
+ ($($rest)*)
+
+ // Add method_id attribute
+ (method_id($($parsed)*))
+ ($($method_family)*)
+ ($($optional)*)
+ ($($attr_method)*)
+ ($($attr_use)*)
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+
+ // `method_id` attribute
+ {
+ (
+ #[method_id($($parsed:tt)*)]
+ $($rest:tt)*
+ )
+
+ ($($method:tt)*)
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__macro_helpers::compile_error!($crate::__macro_helpers::concat!(
+ "The #[method_id] attribute is now unsafe, and must be used as #[unsafe(method_id(",
+ $crate::__macro_helpers::stringify!($($parsed)*),
+ "))]",
+ ));
+
+ // Continue for better UI.
+ $crate::__handle_duplicate!("method`/`method_id"; $($method)*);
+ $crate::__extract_method_attributes_inner! {
+ ($($rest)*)
+
+ (method_id($($parsed)*))
+ ($($method_family)*)
+ ($($optional)*)
+ ($($attr_method)*)
+ ($($attr_use)*)
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+
+ // `unsafe(method_family)` attribute
+ {
+ (
+ #[unsafe(method_family = $($parsed:tt)+)]
+ $($rest:tt)*
+ )
+ ($($method:tt)*)
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__handle_duplicate!("method_family"; $($method_family)*);
+ $crate::__extract_method_attributes_inner! {
+ ($($rest)*)
+
+ ($($method)*)
+ // Add method_family attribute
+ ($($parsed)+)
+ ($($optional)*)
+ ($($attr_method)*)
+ ($($attr_use)*)
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+
+ // `method_family` attribute
+ {
+ (
+ #[method_family = $($parsed:tt)+]
+ $($rest:tt)*
+ )
+ ($($method:tt)*)
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__macro_helpers::compile_error!($crate::__macro_helpers::concat!(
+ "The #[method_family] attribute is unsafe, and must be used as #[unsafe(method_family = ",
+ $crate::__macro_helpers::stringify!($($parsed)*),
+ ")]",
+ ));
+
+ // Continue for better UI.
+ $crate::__handle_duplicate!("method_family"; $($method_family)*);
+ $crate::__extract_method_attributes_inner! {
+ ($($rest)*)
+
+ ($($method)*)
+ ($($parsed)+)
+ ($($optional)*)
+ ($($attr_method)*)
+ ($($attr_use)*)
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+
+ // `optional` attribute
+ {
+ (
+ #[optional]
+ $($rest:tt)*
+ )
+
+ ($($method:tt)*)
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__handle_duplicate!("optional"; $($optional)*);
+ $crate::__extract_method_attributes_inner! {
+ ($($rest)*)
+
+ ($($method)*)
+ ($($method_family)*)
+ // Add optional attribute
+ (#[optional])
+ ($($attr_method)*)
+ ($($attr_use)*)
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+
+ // Special-case `cfg(...)`
+ {
+ (
+ #[cfg $($parsed:tt)*]
+ $($rest:tt)*
+ )
+
+ ($($method:tt)*)
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__extract_method_attributes_inner! {
+ ($($rest)*)
+
+ ($($method)*)
+ ($($method_family)*)
+ ($($optional)*)
+ (
+ $($attr_method)*
+ #[cfg $($parsed)*]
+ )
+ // Add `cfg` attributes to use site as well.
+ (
+ $($attr_use)*
+ #[cfg $($parsed)*]
+ )
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+
+ // Special-case `allow(...)`
+ {
+ (
+ #[allow $($parsed:tt)*]
+ $($rest:tt)*
+ )
+
+ ($($method:tt)*)
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__extract_method_attributes_inner! {
+ ($($rest)*)
+
+ ($($method)*)
+ ($($method_family)*)
+ ($($optional)*)
+ (
+ $($attr_method)*
+ #[allow $($parsed)*]
+ )
+ // Add `allow` attributes to use site as well.
+ (
+ $($attr_use)*
+ #[allow $($parsed)*]
+ )
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+
+ // Other attributes.
+ // (`doc`, `deprecated`, `expect/warn/deny/forbid`, `cfg_attr`,
+ // `no_mangle`, `inline`, `cold`, `track_caller`, etc.)
+ {
+ (
+ #[$($parsed:tt)*]
+ $($rest:tt)*
+ )
+
+ ($($method:tt)*)
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__extract_method_attributes_inner! {
+ ($($rest)*)
+
+ ($($method)*)
+ ($($method_family)*)
+ ($($optional)*)
+ (
+ $($attr_method)*
+ // The attribute is appended to the current set, since we've
+ // been consuming the attributes from the front.
+ #[$($parsed)*]
+ )
+ ($($attr_use)*)
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+}
+
+/// Extract struct attributes, and send them to another macro.
+///
+/// Used by `define_class!` and `extern_class!`.
+///
+/// This will ensure that there is only one of our custom attributes present.
+///
+/// Custom attributes support both invocation forms `#[a = b]` and `#[a(b)]`,
+/// and if they are wrapped in `unsafe`, that token will be passed onwards.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extract_struct_attributes {
+ {
+ // The attributes to parse.
+ ($($attrs:tt)*)
+
+ // The output macro.
+ ($out_macro:path)
+
+ // Further arguments to passed to the output macro.
+ $($out_args:tt)*
+
+ // The following arguments will be appended to the output macro:
+ //
+ // The contents of the `super` attribute + optionally the token `unsafe`.
+ // (unsafe($($superclasses:path),*))
+ //
+ // The contents of the `thread_kind` attribute, if any.
+ // ($($thread_kind:path)?)
+ //
+ // The contents of the `name` attribute, if any.
+ // ($($name:expr)?)
+ //
+ // The contents of the `ivars` attribute, if any.
+ // ($($ivars:path)?)
+ //
+ // The list of paths in all `derive` attributes.
+ // ($($derives:path),*)
+ //
+ // Attributes that should be applied to the struct.
+ // ($($attr_struct:tt)*)
+ //
+ // Attributes that should be applied to struct implementations.
+ // ($($attr_impl:tt)*)
+ } => {
+ $crate::__extract_struct_attributes_inner! {
+ ($($attrs)*)
+
+ // No already parsed attributes
+ () // superclasses
+ () // thread_kind
+ () // name
+ () // ivars
+ () // derive
+ () // attr_struct
+ () // attr_impl
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extract_struct_attributes_inner {
+ // Base case
+ {
+ // No attributes left to process
+ ()
+
+ ($($superclasses:tt)*)
+ ($($thread_kind:tt)*)
+ ($($name:tt)*)
+ ($($ivars:tt)*)
+ ($($derives:tt)*)
+ ($($attr_struct:tt)*)
+ ($($attr_impl:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ // Output
+ $out_macro! {
+ $($out_args)*
+
+ // Append attributes to the end of the macro arguments
+ ($($superclasses)*)
+ ($($thread_kind)*)
+ ($($name)*)
+ ($($ivars)*)
+ ($($derives)*)
+ ($($attr_struct)*)
+ ($($attr_impl)*)
+ }
+ };
+
+ // `unsafe(super(...))`
+ {
+ (
+ #[unsafe(super($($parsed:tt)*))]
+ $($rest:tt)*
+ )
+
+ ($($superclasses:tt)*)
+ ($($thread_kind:tt)*)
+ ($($name:tt)*)
+ ($($ivars:tt)*)
+ ($($derives:tt)*)
+ ($($attr_struct:tt)*)
+ ($($attr_impl:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__handle_duplicate!("super"; $($superclasses)*);
+ $crate::__extract_struct_attributes_inner! {
+ ($($rest)*)
+
+ (unsafe $($parsed)*)
+ ($($thread_kind)*)
+ ($($name)*)
+ ($($ivars)*)
+ ($($derives)*)
+ ($($attr_struct)*)
+ ($($attr_impl)*)
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+ // `unsafe(super = ...)`
+ {
+ (
+ #[unsafe(super = $parsed:path)]
+ $($rest:tt)*
+ )
+
+ ($($superclasses:tt)*)
+ ($($thread_kind:tt)*)
+ ($($name:tt)*)
+ ($($ivars:tt)*)
+ ($($derives:tt)*)
+ ($($attr_struct:tt)*)
+ ($($attr_impl:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__handle_duplicate!("super"; $($superclasses)*);
+ $crate::__extract_struct_attributes_inner! {
+ ($($rest)*)
+
+ (unsafe $parsed)
+ ($($thread_kind)*)
+ ($($name)*)
+ ($($ivars)*)
+ ($($derives)*)
+ ($($attr_struct)*)
+ ($($attr_impl)*)
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+
+ // `super(...)`
+ {
+ (
+ #[super($($parsed:tt)*)]
+ $($rest:tt)*
+ )
+
+ ($($superclasses:tt)*)
+ ($($thread_kind:tt)*)
+ ($($name:tt)*)
+ ($($ivars:tt)*)
+ ($($derives:tt)*)
+ ($($attr_struct:tt)*)
+ ($($attr_impl:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__handle_duplicate!("super"; $($superclasses)*);
+ $crate::__extract_struct_attributes_inner! {
+ ($($rest)*)
+
+ (safe $($parsed)*)
+ ($($thread_kind)*)
+ ($($name)*)
+ ($($ivars)*)
+ ($($derives)*)
+ ($($attr_struct)*)
+ ($($attr_impl)*)
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+ // `super = ...`
+ {
+ (
+ #[super = $parsed:path]
+ $($rest:tt)*
+ )
+
+ ($($superclasses:tt)*)
+ ($($thread_kind:tt)*)
+ ($($name:tt)*)
+ ($($ivars:tt)*)
+ ($($derives:tt)*)
+ ($($attr_struct:tt)*)
+ ($($attr_impl:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__handle_duplicate!("super"; $($superclasses)*);
+ $crate::__extract_struct_attributes_inner! {
+ ($($rest)*)
+
+ (safe $parsed)
+ ($($thread_kind)*)
+ ($($name)*)
+ ($($ivars)*)
+ ($($derives)*)
+ ($($attr_struct)*)
+ ($($attr_impl)*)
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+
+ // `thread_kind = ...`
+ {
+ (
+ #[thread_kind = $($parsed:tt)+]
+ $($rest:tt)*
+ )
+
+ ($($superclasses:tt)*)
+ ($($thread_kind:tt)*)
+ ($($name:tt)*)
+ ($($ivars:tt)*)
+ ($($derives:tt)*)
+ ($($attr_struct:tt)*)
+ ($($attr_impl:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__handle_duplicate!("thread_kind"; $($thread_kind)*);
+ $crate::__extract_struct_attributes_inner! {
+ ($($rest)*)
+
+ ($($superclasses)*)
+ ($($parsed)+)
+ ($($name)*)
+ ($($ivars)*)
+ ($($derives)*)
+ ($($attr_struct)*)
+ ($($attr_impl)*)
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+
+ // `name = ...`
+ {
+ (
+ #[name = $($parsed:tt)+]
+ $($rest:tt)*
+ )
+
+ ($($superclasses:tt)*)
+ ($($thread_kind:tt)*)
+ ($($name:tt)*)
+ ($($ivars:tt)*)
+ ($($derives:tt)*)
+ ($($attr_struct:tt)*)
+ ($($attr_impl:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__handle_duplicate!("name"; $($name)*);
+ $crate::__extract_struct_attributes_inner! {
+ ($($rest)*)
+
+ ($($superclasses)*)
+ ($($thread_kind)*)
+ ($($parsed)+)
+ ($($ivars)*)
+ ($($derives)*)
+ ($($attr_struct)*)
+ ($($attr_impl)*)
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+
+ // `ivars = ...`
+ {
+ (
+ #[ivars = $($parsed:tt)+]
+ $($rest:tt)*
+ )
+
+ ($($superclasses:tt)*)
+ ($($thread_kind:tt)*)
+ ($($name:tt)*)
+ ($($ivars:tt)*)
+ ($($derives:tt)*)
+ ($($attr_struct:tt)*)
+ ($($attr_impl:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__handle_duplicate!("ivars"; $($ivars)*);
+ $crate::__extract_struct_attributes_inner! {
+ ($($rest)*)
+
+ ($($superclasses)*)
+ ($($thread_kind)*)
+ ($($name)*)
+ ($($parsed)+)
+ ($($derives)*)
+ ($($attr_struct)*)
+ ($($attr_impl)*)
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+
+ // Special-case `derive(...)`
+ {
+ (
+ #[derive($($parsed:tt)*)]
+ $($rest:tt)*
+ )
+
+ ($($superclasses:tt)*)
+ ($($thread_kind:tt)*)
+ ($($name:tt)*)
+ ($($ivars:tt)*)
+ ($($derives:tt)*)
+ ($($attr_struct:tt)*)
+ ($($attr_impl:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__extract_struct_attributes_inner! {
+ ($($rest)*)
+
+ ($($superclasses)*)
+ ($($thread_kind)*)
+ ($($name)*)
+ ($($ivars)*)
+ // Combine all #[derive(...)] into one list.
+ ($($derives)*, $($parsed)*)
+ ($($attr_struct)*)
+ ($($attr_impl)*)
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+
+ // Special-case `cfg(...)`
+ {
+ (
+ #[cfg $($parsed:tt)*]
+ $($rest:tt)*
+ )
+
+ ($($superclasses:tt)*)
+ ($($thread_kind:tt)*)
+ ($($name:tt)*)
+ ($($ivars:tt)*)
+ ($($derives:tt)*)
+ ($($attr_struct:tt)*)
+ ($($attr_impl:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__extract_struct_attributes_inner! {
+ ($($rest)*)
+
+ ($($superclasses)*)
+ ($($thread_kind)*)
+ ($($name)*)
+ ($($ivars)*)
+ ($($derives)*)
+ (
+ $($attr_struct)*
+ #[cfg $($parsed)*]
+ )
+ // Add `cfg` attributes to implementations as well.
+ (
+ $($attr_impl)*
+ #[cfg $($parsed)*]
+ )
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+
+ // Special-case `allow(...)`
+ {
+ (
+ #[allow $($parsed:tt)*]
+ $($rest:tt)*
+ )
+
+ ($($superclasses:tt)*)
+ ($($thread_kind:tt)*)
+ ($($name:tt)*)
+ ($($ivars:tt)*)
+ ($($derives:tt)*)
+ ($($attr_struct:tt)*)
+ ($($attr_impl:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__extract_struct_attributes_inner! {
+ ($($rest)*)
+
+ ($($superclasses)*)
+ ($($thread_kind)*)
+ ($($name)*)
+ ($($ivars)*)
+ ($($derives)*)
+ (
+ $($attr_struct)*
+ #[allow $($parsed)*]
+ )
+ // Add `allow` attributes to implementations as well.
+ (
+ $($attr_impl)*
+ #[allow $($parsed)*]
+ )
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+
+ // Other attributes.
+ // (`doc`, `deprecated`, `repr`, `non_exhaustive`, `expect/warn/deny/forbid`, `cfg_attr`, etc.)
+ {
+ (
+ #[$($parsed:tt)*]
+ $($rest:tt)*
+ )
+
+ ($($superclasses:tt)*)
+ ($($thread_kind:tt)*)
+ ($($name:tt)*)
+ ($($ivars:tt)*)
+ ($($derives:tt)*)
+ ($($attr_struct:tt)*)
+ ($($attr_impl:tt)*)
+
+ ($out_macro:path)
+ $($out_args:tt)*
+ } => {
+ $crate::__extract_struct_attributes_inner! {
+ ($($rest)*)
+
+ ($($superclasses)*)
+ ($($thread_kind)*)
+ ($($name)*)
+ ($($ivars)*)
+ ($($derives)*)
+ // Pass all other attributes onwards to the struct.
+ (
+ $($attr_struct)*
+ #[$($parsed)*]
+ )
+ ($($attr_impl)*)
+
+ ($out_macro)
+ $($out_args)*
+ }
+ };
+}
+
+/// Ensure that custom attributes do not appear twice.
+///
+/// NOTE: This intentionally only results in a `compile_error!`, to allow
+/// subsequent macros to still output something (better for rust-analyzer).
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __handle_duplicate {
+ (
+ $name:literal;
+ // No existing value
+ ) => {
+ // Success
+ };
+ (
+ $name:literal;
+ $($existing:tt)+ // Has existing value
+ ) => {
+ $crate::__macro_helpers::compile_error!($crate::__macro_helpers::concat!(
+ "cannot specify the `",
+ $name,
+ "` attribute twice",
+ ));
+ };
+}
diff --git a/third_party/rust/objc2/src/macros/__fallback.rs b/third_party/rust/objc2/src/macros/__fallback.rs
@@ -0,0 +1,16 @@
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __fallback_if_not_set {
+ (
+ ()
+ ($($fallback:tt)*)
+ ) => {
+ $($fallback)*
+ };
+ (
+ ($($actual:tt)+)
+ ($($_fallback:tt)*)
+ ) => {
+ $($actual)+
+ };
+}
diff --git a/third_party/rust/objc2/src/macros/__method_msg_send.rs b/third_party/rust/objc2/src/macros/__method_msg_send.rs
@@ -0,0 +1,165 @@
+/// Forward selector and arguments to `MsgSend::send_message[_error]`.
+///
+/// Note: We can't forward to `msg_send!` since that doesn't support selectors
+/// with space between.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __method_msg_send {
+ // Selector with no arguments
+ (
+ ($receiver:expr)
+ ($sel:ident)
+ ()
+
+ ()
+ ()
+ ($($method_family:tt)*)
+ ) => {
+ $crate::__msg_send_helper! {
+ ($receiver)
+ ($($method_family)*)
+ (MsgSend::send_message)
+ ($sel)
+ ()
+ }
+ };
+
+ // Skip using `MainThreadMarker` in the message send.
+ //
+ // This is a purely textual match, and using e.g. `objc2::MainThreadMarker`
+ // would fail - but that would just be detected as giving a wrong number
+ // of arguments, so it's fine for now.
+ (
+ ($receiver:expr)
+ ($($sel_rest:tt)*)
+ ($arg:ident: MainThreadMarker $(, $($params_rest:tt)*)?)
+
+ ($($sel_parsed:tt)*)
+ ($($arg_parsed:tt)*)
+ ($($method_family:tt)*)
+ ) => ({
+ let _ = $arg;
+ $crate::__method_msg_send! {
+ ($receiver)
+ ($($sel_rest)*)
+ ($($($params_rest)*)?)
+
+ ($($sel_parsed)*)
+ ($($arg_parsed)*)
+ ($($method_family)*)
+ }
+ });
+
+ // Parse each argument-selector pair
+ (
+ ($receiver:expr)
+ ($($sel:ident)? : $($sel_rest:tt)*)
+ ($arg:ident : $_arg_ty:ty $(, $($params_rest:tt)*)?)
+
+ ($($sel_parsed:tt)*)
+ ($($arg_parsed:tt)*)
+ ($($method_family:tt)*)
+ ) => {
+ $crate::__method_msg_send! {
+ ($receiver)
+ ($($sel_rest)*)
+ ($($($params_rest)*)?)
+
+ ($($sel_parsed)* $($sel)? :)
+ ($($arg_parsed)* $arg,)
+ ($($method_family)*)
+ }
+ };
+ // Handle path separator token
+ (
+ ($receiver:expr)
+ ($($sel:ident)? :: $($sel_rest:tt)*)
+ ($arg1:ident : $_arg_ty1:ty, $arg2:ident : $_arg_ty2:ty $(, $($params_rest:tt)*)?)
+
+ ($($sel_parsed:tt)*)
+ ($($arg_parsed:tt)*)
+ ($($method_family:tt)*)
+ ) => {
+ $crate::__method_msg_send! {
+ ($receiver)
+ ($($sel_rest)*)
+ ($($($params_rest)*)?)
+
+ ($($sel_parsed)* $($sel)? : :)
+ ($($arg_parsed)* $arg1, $arg2,)
+ ($($method_family)*)
+ }
+ };
+
+ // Normal return
+ (
+ ($receiver:expr)
+ ()
+ ()
+
+ // Notice the "+" here; we must make sure we actually _did_ parse
+ // a selector, and haven't just gotten an empty `#[unsafe(method())]`.
+ ($($sel_parsed:tt)+)
+ ($($arg_parsed:tt)*)
+ ($($method_family:tt)*)
+ ) => {
+ $crate::__msg_send_helper! {
+ ($receiver)
+ ($($method_family)*)
+ (MsgSend::send_message)
+ ($($sel_parsed)*)
+ ($($arg_parsed)*)
+ }
+ };
+
+ // Error return
+ (
+ ($receiver:expr)
+ // `sel:_` without a corresponding argument
+ ($sel:ident : _)
+ ()
+
+ ($($sel_parsed:tt)*)
+ ($($arg_parsed:tt)*)
+ ($($method_family:tt)*)
+ ) => {
+ $crate::__msg_send_helper! {
+ ($receiver)
+ ($($method_family)*)
+ // Use error method
+ (MsgSendError::send_message_error)
+ ($($sel_parsed)* $sel :)
+ ($($arg_parsed)*)
+ }
+ };
+
+ // Variadic method
+ (
+ ($receiver:expr)
+ ($($sel:ident : _)?)
+ ($($arg:ident :)? ...)
+
+ ($($sel_parsed:tt)*)
+ ($($arg_parsed:tt)*)
+ ($($method_family:tt)*)
+ ) => ({
+ $crate::__macro_helpers::compile_error!(
+ "variadic methods are not yet supported"
+ )
+ });
+
+ // Mismatched selector/argument
+ (
+ ($receiver:expr)
+ ($($sel_rest:tt)*)
+ ($($params_rest:tt)*)
+
+ ($($sel_parsed:tt)*)
+ ($($arg_parsed:tt)*)
+ ($($method_family:tt)*)
+ ) => ({
+ $crate::__macro_helpers::compile_error!(
+ "number of arguments in function and selector did not match"
+ )
+ });
+}
diff --git a/third_party/rust/objc2/src/macros/__msg_send_parse.rs b/third_party/rust/objc2/src/macros/__msg_send_parse.rs
@@ -0,0 +1,189 @@
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __msg_send_parse {
+ // No arguments
+ {
+ // Intentionally empty
+ ()
+ ()
+ ($selector:ident $(,)?)
+
+ ($($error_data:tt)*)
+ ($($data:tt)*)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => {
+ $crate::__msg_send_parse! {
+ ($selector)
+ ()
+ ()
+
+ ($($error_data)*)
+ ($($data)*)
+
+ ($out_macro)
+ $($macro_args)*
+ }
+ };
+
+ // tt-munch remaining `selector: argument` pairs, looking for a pattern
+ // that ends with `sel: _`.
+ {
+ ($($selector_output:tt)*)
+ ($($argument_output:tt)*)
+ ()
+
+ ($($error_data:tt)*)
+ ($($data:tt)*)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => ({
+ $out_macro! {
+ $($macro_args)*
+
+ ($($data)*)
+ ($($selector_output)*)
+ ($($argument_output)*)
+ }
+ });
+ {
+ ($($selector_output:tt)*)
+ ($($argument_output:tt)*)
+ ($selector:ident: _ $(,)?)
+
+ ($($error_data:tt)*)
+ ($($data:tt)*)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => {
+ $crate::__msg_send_parse! {
+ ($($selector_output)* $selector:)
+ // Don't pass an argument
+ ($($argument_output)*)
+ ()
+
+ // Instead, we change the data to the error data.
+ ($($error_data)*)
+ ($($error_data)*)
+
+ ($out_macro)
+ $($macro_args)*
+ }
+ };
+ {
+ ($($selector_output:tt)*)
+ ($($argument_output:tt)*)
+ ($selector:ident : $argument:expr $(, $($rest:tt)*)?)
+
+ ($($error_data:tt)*)
+ ($($data:tt)*)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => {
+ $crate::__msg_send_parse! {
+ ($($selector_output)* $selector:)
+ ($($argument_output)* $argument,)
+ ($($($rest)*)?)
+
+ ($($error_data)*)
+ ($($data)*)
+
+ ($out_macro)
+ $($macro_args)*
+ }
+ };
+
+ // Handle calls without comma between `selector: argument` pair.
+ {
+ // Intentionally empty
+ ()
+ ()
+ ($($selector:ident : $argument:expr)*)
+
+ ($($error_data:tt)*)
+ ($($data:tt)*)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => {{
+ $crate::__missing_comma_between_args!(
+ ($($data)*)
+
+ ($(
+ ", ",
+ $crate::__macro_helpers::stringify!($selector),
+ ": ",
+ $crate::__macro_helpers::stringify!($argument),
+ )+)
+
+ $($macro_args)*
+ );
+
+ $crate::__msg_send_parse! {
+ ()
+ ()
+ ($($selector : $argument),*)
+
+ ($($error_data)*)
+ ($($data)*)
+
+ ($out_macro)
+ $($macro_args)*
+ }
+ }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __missing_comma_between_args {
+ (
+ (MsgSendSuper::send_super_message_static)
+ ($($args:tt)*)
+ ($obj:expr)
+ ()
+ ) => {
+ $crate::__missing_comma_between_args_inner!(
+ "super", $crate::__macro_helpers::stringify!(($obj)), $($args)*
+ );
+ };
+ (
+ (MsgSendSuper::send_super_message)
+ ($($args:tt)*)
+ ($obj:expr, $superclass:expr)
+ ()
+ ) => {
+ $crate::__missing_comma_between_args_inner!(
+ "super", $crate::__macro_helpers::stringify!(($obj, $superclass)), $($args)*
+ );
+ };
+ (
+ (MsgSend::send_message)
+ ($($args:tt)*)
+ ($obj:expr)
+ ()
+ ) => {
+ $crate::__missing_comma_between_args_inner!(
+ $crate::__macro_helpers::stringify!($obj), $($args)*
+ );
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __missing_comma_between_args_inner {
+ ($($args:tt)*) => {{
+ #[deprecated = $crate::__macro_helpers::concat!(
+ "using msg_send! without a comma between arguments is ",
+ "technically not valid macro syntax, and may break in a future ",
+ "version of Rust. You should use the following instead:\n",
+ "msg_send![", $($args)* "]"
+ )]
+ #[inline]
+ fn __missing_comma() {}
+ __missing_comma();
+ }};
+}
diff --git a/third_party/rust/objc2/src/macros/__rewrite_self_param.rs b/third_party/rust/objc2/src/macros/__rewrite_self_param.rs
@@ -0,0 +1,222 @@
+/// Detect instance vs. class method.
+///
+/// Will add:
+/// ```ignore
+/// (builder_method:ident)
+/// (receiver:expr)
+/// (receiver_ty:ty)
+/// (params_prefix*)
+/// (params_rest*)
+/// ```
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __rewrite_self_param {
+ {
+ ($($params:tt)*)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => {
+ $crate::__rewrite_self_param_inner! {
+ // Duplicate params out so that we can match on `self`, while still
+ // using it as a function parameter
+ ($($params)*)
+ ($($params)*)
+
+ ($out_macro)
+ $($macro_args)*
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __rewrite_self_param_inner {
+ // Instance method
+ {
+ (&self $($__params_rest:tt)*)
+ (&$self:ident $(, $($params_rest:tt)*)?)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => {
+ $out_macro! {
+ $($macro_args)*
+
+ (add_method)
+ ($self)
+ (&Self)
+ (
+ &$self,
+ _: $crate::runtime::Sel,
+ )
+ ($($($params_rest)*)?)
+ }
+ };
+ {
+ (&mut self $($__params_rest:tt)*)
+ (&mut $self:ident $(, $($params_rest:tt)*)?)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => {
+ $out_macro! {
+ $($macro_args)*
+
+ (add_method)
+ ($self)
+ (&mut Self)
+ (
+ &mut $self,
+ _: $crate::runtime::Sel,
+ )
+ ($($($params_rest)*)?)
+ }
+ };
+ {
+ (self: $__self_ty:ty $(, $($__params_rest:tt)*)?)
+ ($self:ident: $self_ty:ty $(, $($params_rest:tt)*)?)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => {
+ $out_macro! {
+ $($macro_args)*
+
+ (add_method)
+ ($self)
+ ($self_ty)
+ (
+ $self: $self_ty,
+ _: $crate::runtime::Sel,
+ )
+ ($($($params_rest)*)?)
+ }
+ };
+ {
+ (mut self: $__self_ty:ty $(, $($__params_rest:tt)*)?)
+ ($mut:ident $self:ident: $self_ty:ty $(, $($params_rest:tt)*)?)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => {
+ $out_macro! {
+ $($macro_args)*
+
+ (add_method)
+ ($self)
+ ($self_ty)
+ (
+ $mut $self: $self_ty,
+ _: $crate::runtime::Sel,
+ )
+ ($($($params_rest)*)?)
+ }
+ };
+
+ // `this: Type` or `_this: Type` instance method
+ // Workaround for arbitrary self types being unstable
+ // https://doc.rust-lang.org/nightly/unstable-book/language-features/arbitrary-self-types.html
+ {
+ (mut this: $__self_ty:ty $(, $($__params_rest:tt)*)?)
+ ($mut:ident $this:ident: $this_ty:ty $(, $($params_rest:tt)*)?)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => {
+ $out_macro! {
+ $($macro_args)*
+
+ (add_method)
+ ($this)
+ ($this_ty)
+ (
+ $mut $this: $this_ty,
+ _: $crate::runtime::Sel,
+ )
+ ($($($params_rest)*)?)
+ }
+ };
+ {
+ (this: $__self_ty:ty $(, $($__params_rest:tt)*)?)
+ ($this:ident: $this_ty:ty $(, $($params_rest:tt)*)?)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => {
+ $out_macro! {
+ $($macro_args)*
+
+ (add_method)
+ ($this)
+ ($this_ty)
+ (
+ $this: $this_ty,
+ _: $crate::runtime::Sel,
+ )
+ ($($($params_rest)*)?)
+ }
+ };
+ {
+ (mut _this: $__self_ty:ty $(, $($__params_rest:tt)*)?)
+ ($mut:ident $this:ident: $this_ty:ty $(, $($params_rest:tt)*)?)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => {
+ $out_macro! {
+ $($macro_args)*
+
+ (add_method)
+ ($this)
+ ($this_ty)
+ (
+ $mut $this: $this_ty,
+ _: $crate::runtime::Sel,
+ )
+ ($($($params_rest)*)?)
+ }
+ };
+ {
+ (_this: $__self_ty:ty $(, $($__params_rest:tt)*)?)
+ ($this:ident: $this_ty:ty $(, $($params_rest:tt)*)?)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => {
+ $out_macro! {
+ $($macro_args)*
+
+ (add_method)
+ ($this)
+ ($this_ty)
+ (
+ $this: $this_ty,
+ _: $crate::runtime::Sel,
+ )
+ ($($($params_rest)*)?)
+ }
+ };
+
+ // Class method
+ {
+ ($($__params:tt)*)
+ ($($params_rest:tt)*)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => {
+ $out_macro! {
+ $($macro_args)*
+
+ (add_class_method)
+ (<Self as $crate::ClassType>::class())
+ (&$crate::runtime::AnyClass)
+ (
+ _: &$crate::runtime::AnyClass,
+ _: $crate::runtime::Sel,
+ )
+ ($($params_rest)*)
+ }
+ };
+}
diff --git a/third_party/rust/objc2/src/macros/available.rs b/third_party/rust/objc2/src/macros/available.rs
@@ -0,0 +1,207 @@
+/// Check if APIs from the given operating system versions are available.
+///
+/// Apple adds new APIs with new OS releases, and as a developer, you often
+/// want to use those to give your users the best behaviour, while still
+/// supporting older OS versions that don't have those APIs (instead of
+/// crashing e.g. because of an undefined selector).
+///
+/// This macro allows you to conditionally execute code depending on if the
+/// current OS version is higher than or equal to the version given in the
+/// macro.
+///
+/// If no version is specified for a certain OS, the API will be assumed to be
+/// unavailable there. This default can be changed by adding a trailing `..`
+/// to the macro invocation.
+///
+/// This is very similar to `@available` in Objective-C and `#available` in
+/// Swift, see [Apple's documentation][apple-doc]. Another great introduction
+/// to availability can be found in [here][epir-availability].
+///
+/// [apple-doc]: https://developer.apple.com/documentation/xcode/running-code-on-a-specific-version#Require-a-minimum-operating-system-version-for-a-feature
+/// [epir-availability]: https://epir.at/2019/10/30/api-availability-and-target-conditionals/
+///
+///
+/// # Operating systems
+///
+/// The operating system names this macro accepts, the standard environment
+/// variables that you use to raise the deployment target (the minimum
+/// supported OS version) and the current default versions are all summarized
+/// in the table below.
+///
+/// | OS Value | Name | Environment Variable | Default |
+/// | ---------- | ----------------------- | ---------------------------- | ------- |
+/// | `ios` | iOS/iPadOS/Mac Catalyst | `IPHONEOS_DEPLOYMENT_TARGET` | 10.0 |
+/// | `macos` | macOS | `MACOSX_DEPLOYMENT_TARGET` | 10.12 |
+/// | `tvos` | tvOS | `TVOS_DEPLOYMENT_TARGET` | 10.0 |
+/// | `visionos` | visionOS | `XROS_DEPLOYMENT_TARGET` | 1.0 |
+/// | `watchos` | watchOS | `WATCHOS_DEPLOYMENT_TARGET` | 5.0 |
+///
+/// The default version is the same as that of `rustc` itself.
+///
+///
+/// # Optimizations
+///
+/// This macro will statically be set to `true` when the deployment target is
+/// high enough.
+///
+/// If a runtime check is deemed necessary, the version lookup will be cached.
+///
+///
+/// # Alternatives
+///
+/// Instead of checking the version at runtime, you could do one of the
+/// following instead:
+///
+/// 1. Check statically that you're compiling for a version where the API is
+/// available, e.g. by checking the `*_DEPLOYMENT_TARGET` variables in a
+/// build script or at `const` time.
+///
+/// 2. Check at runtime that a class, method or symbol is available, using
+/// e.g. [`AnyClass::get`], [`respondsToSelector`] or [weak linking].
+///
+/// [`AnyClass::get`]: crate::runtime::AnyClass::get
+/// [`respondsToSelector`]: crate::runtime::NSObjectProtocol::respondsToSelector
+/// [weak linking]: https://github.com/rust-lang/rust/issues/29603
+///
+///
+/// # Examples
+///
+/// Use the [`effectiveAppearance`] API that was added in macOS 10.14.
+///
+/// ```
+/// # #[cfg(available_in_frameworks)]
+/// use objc2_app_kit::{NSApplication, NSAppearance, NSAppearanceNameAqua};
+/// use objc2::available;
+///
+/// let appearance = if available!(macos = 10.14) {
+/// // Dark mode and `effectiveAppearance` was added in macOS 10.14.
+/// # #[cfg(available_in_frameworks)]
+/// NSApplication::sharedApplication(mtm).effectiveAppearance()
+/// } else {
+/// // Fall back to `NSAppearanceNameAqua` on macOS 10.13 and below.
+/// # #[cfg(available_in_frameworks)]
+/// NSAppearance::appearanceNamed(NSAppearanceNameAqua).unwrap()
+/// };
+/// ```
+///
+/// Use an API added in Xcode 16.0 SDKs.
+///
+/// We use `..` here in case Apple adds a new operating system in the future,
+/// then we probably also want the branch to be taken there.
+///
+/// ```
+/// use objc2::available;
+///
+/// if available!(ios = 18.0, macos = 15.0, tvos = 18.0, visionos = 2.0, watchos = 11.0, ..) {
+/// // Use some recent API here.
+/// }
+/// ```
+///
+/// Set the [`wantsExtendedDynamicRangeContent`] property, which is available
+/// since iOS 16.0, macOS 10.11 and visionOS 1.0, but is not available on tvOS
+/// and watchOS.
+///
+/// ```
+/// use objc2::available;
+///
+/// if available!(ios = 16.0, macos = 10.11, visionos = 1.0) {
+/// # #[cfg(available_in_frameworks)]
+/// layer.setWantsExtendedDynamicRangeContent(true);
+/// }
+/// ```
+///
+/// Work around problems in a specific range of versions (an example of this
+/// in the real world can be seen in [#662]).
+///
+/// ```
+/// use objc2::available;
+///
+/// if available!(macos = 15.0) && !available!(macos = 15.1) {
+/// // Do something on macOS 15.0 and 15.0.1.
+/// } else {
+/// // Do something else on all other versions.
+/// }
+/// ```
+///
+/// [`effectiveAppearance`]: https://developer.apple.com/documentation/appkit/nsapplication/2967171-effectiveappearance?language=objc
+/// [`wantsExtendedDynamicRangeContent`]: https://developer.apple.com/documentation/quartzcore/cametallayer/1478161-wantsextendeddynamicrangecontent
+/// [#662]: https://github.com/madsmtm/objc2/issues/662
+#[doc(alias = "@available")] // Objective-C
+#[doc(alias = "#available")] // Swift
+#[macro_export]
+macro_rules! available {
+ (
+ // Returns `false` on unspecified platforms.
+ $(
+ $os:ident $(= $major:literal $(. $minor:literal $(. $patch:literal)?)?)?
+ ),* $(,)?
+ ) => {
+ $crate::__macro_helpers::is_available({
+ // TODO: Use inline const once in MSRV
+ #[allow(clippy::needless_update)]
+ const VERSION: $crate::__macro_helpers::AvailableVersion = $crate::__macro_helpers::AvailableVersion {
+ $(
+ // Doesn't actually parse versions this way, but is
+ // helpful to write it like this for documentation.
+ //
+ // We use optionality for the version here, to allow
+ // rust-analyzer to work with partially filled macros.
+ $os: $($crate::__available_version!($major $(. $minor $(. $patch)?)?))?,
+ )*
+ // A version this high will never be lower than the deployment
+ // target, and hence will always return `false` from
+ // `is_available`.
+ .. $crate::__macro_helpers::AvailableVersion::MAX
+ };
+ VERSION
+ })
+ };
+ (
+ // Returns `true` on unspecified platforms because of the trailing `..`.
+ $(
+ $os:ident $(= $major:literal $(. $minor:literal $(. $patch:literal)?)?)?,
+ )*
+ ..
+ ) => {
+ $crate::__macro_helpers::is_available({
+ #[allow(clippy::needless_update)]
+ const VERSION: $crate::__macro_helpers::AvailableVersion = $crate::__macro_helpers::AvailableVersion {
+ $(
+ $os: $($crate::__available_version!($major $(. $minor $(. $patch)?)?))?,
+ )*
+ // A version of 0.0.0 will always be lower than the deployment
+ // target, and hence will always return `true` from
+ // `is_available`.
+ //
+ // We do this when `..` is specified.
+ .. $crate::__macro_helpers::AvailableVersion::MIN
+ };
+ VERSION
+ })
+ };
+}
+
+/// Both `tt` and `literal` matches either `$major` as an integer, or
+/// `$major.$minor` as a float.
+///
+/// As such, we cannot just take `$major:tt . $minor:tt . $patch:tt` and
+/// convert that to `OSVersion` directly, we must convert it to a string
+/// first, and then parse that.
+///
+/// We also _have_ to do string parsing, floating point parsing wouldn't be
+/// enough (because e.g. `10.10` would result in the float `10.1` and parse
+/// wrongly).
+///
+/// Note that we intentionally `stringify!` before passing to `concat!`, as
+/// that seems to properly preserve all zeros in the literal.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __available_version {
+ // Just in case rustc's parsing changes in the future, let's handle this
+ // generically, instead of trying to split each part into separate `tt`.
+ ($($version_part_or_period:tt)*) => {
+ $crate::__macro_helpers::OSVersion::from_str($crate::__macro_helpers::concat!($(
+ $crate::__macro_helpers::stringify!($version_part_or_period),
+ )*))
+ };
+}
diff --git a/third_party/rust/objc2/src/macros/cf_objc2_type.rs b/third_party/rust/objc2/src/macros/cf_objc2_type.rs
@@ -0,0 +1,47 @@
+/// Helper macro for implementing [`objc2`][crate] traits for
+/// CoreFoundation-like types.
+#[doc(hidden)] // For now, though still a breaking change to modify
+#[macro_export]
+macro_rules! cf_objc2_type {
+ (unsafe impl $(<$($generic:ident : ?$sized:ident),* $(,)?>)? RefEncode<$encoding_name:literal> for $ty:ty {}) => {
+ // SAFETY: Caller upholds that the struct is a ZST type, and
+ // represents a C struct with the given encoding.
+ unsafe impl $(<$($generic : ?$sized),*>)? $crate::encode::RefEncode for $ty {
+ const ENCODING_REF: $crate::encode::Encoding = $crate::encode::Encoding::Pointer(
+ &$crate::encode::Encoding::Struct($encoding_name, &[]),
+ );
+ }
+
+ // SAFETY: CF types are message-able in the Objective-C runtime.
+ //
+ // (Yes, even e.g. `CFArray<u32>`, though the return type from methods
+ // might not be what's expected).
+ unsafe impl $(<$($generic : ?$sized),*>)? $crate::Message for $ty {}
+
+ // Allow converting to AnyObject.
+ // Similar to __extern_class_impl_as_ref_borrow!
+ impl $(<$($generic : ?$sized),*>)? $crate::__macro_helpers::AsRef<$crate::runtime::AnyObject> for $ty {
+ #[inline]
+ fn as_ref(&self) -> &$crate::runtime::AnyObject {
+ // SAFETY: CF types are valid to re-interpret as AnyObject.
+ unsafe { $crate::__macro_helpers::transmute(self) }
+ }
+ }
+
+ impl $(<$($generic : ?$sized),*>)? $crate::__macro_helpers::Borrow<$crate::runtime::AnyObject> for $ty {
+ #[inline]
+ fn borrow(&self) -> &$crate::runtime::AnyObject {
+ <Self as $crate::__macro_helpers::AsRef<$crate::runtime::AnyObject>>::as_ref(self)
+ }
+ }
+
+ // Do not implement `ClassType`, CoreFoundation objects are root
+ // objects, and all inherit from the same (hidden) __NSCFType class.
+ //
+ // This also means that casting etc. must be implemented differently
+ // for CoreFoundation objects (compare).
+
+ // NOTE: Make sure to keep objc2-core-foundation/src/base.rs up to
+ // date with changes in here.
+ };
+}
diff --git a/third_party/rust/objc2/src/macros/define_class.rs b/third_party/rust/objc2/src/macros/define_class.rs
@@ -0,0 +1,1517 @@
+/// Create a new Objective-C class.
+///
+/// This is useful in many cases since Objective-C frameworks tend to favour a
+/// design pattern using "delegates", where to hook into a piece of
+/// functionality in a class, you implement that class' delegate protocol in
+/// a custom class.
+///
+/// This macro is the declarative way of creating classes, in contrast with
+/// [`ClassBuilder`], which allows creating classes in an imperative fashion.
+/// It is highly recommended that you use this macro though, since it contains
+/// a lot of extra debug assertions and niceties that help ensure the
+/// soundness of your code.
+///
+/// The class is guaranteed to have been created and registered with the
+/// Objective-C runtime after the [`ClassType::class`] function has been
+/// called.
+///
+/// See [Apple's documentation] on defining classes for a more in-depth
+/// introduction.
+///
+/// [Apple's documentation]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/DefiningClasses/DefiningClasses.html
+/// [`ClassBuilder`]: crate::runtime::ClassBuilder
+/// [`ClassType::class`]: crate::ClassType::class
+///
+///
+/// # Specification
+///
+/// This macro consists of the following parts:
+/// - The type definition, along with special attributes.
+/// - Any number of inherent implementations.
+/// - Any number of protocol implementations.
+///
+/// With the syntax generally resembling a combination of that in
+/// [`extern_class!`] and [`extern_methods!`].
+///
+/// This macro creates an opaque struct with implementations in [a similar
+/// manner as the `extern_class!` macro][ec_spec]. Additionally, it implements
+/// the [`DefinedClass`] trait, as well as any protocols specified in the
+/// protocol implementations.
+///
+/// If the type implements [`Drop`], the macro will generate a `dealloc`
+/// method for you, which will call `drop` automatically.
+///
+/// The macro does not support generic types.
+///
+/// [`extern_class!`]: crate::extern_class
+/// [`extern_methods!`]: crate::extern_methods
+/// [ec_spec]: crate::extern_class#specification
+/// [`DefinedClass`]: crate::DefinedClass
+///
+///
+/// ## Attributes
+///
+/// You can add most normal attributes to the class, including `#[cfg(...)]`,
+/// `#[allow(...)]` and doc comments.
+///
+/// Exceptions and special attributes are noted below.
+///
+///
+/// ### `#[unsafe(super(...))]` (required)
+///
+/// Same [as in `extern_class!`](crate::extern_class#unsafesuper-required).
+///
+///
+/// ### `#[thread_kind = ...]` (optional)
+///
+/// Same [as in `extern_class!`](crate::extern_class#thread_kind---optional).
+///
+///
+/// ### `#[name = "..."]` (optional)
+///
+/// Specify the runtime-name for the class. Must be unique across the entire
+/// application. This is useful if the name of a class is used elsewhere, such
+/// as when defining a delegate that needs to be named in e.g. `Info.plist`.
+///
+/// If not set, this will default to:
+/// ```ignore
+/// concat!(module_path!(), "::", $class, env!("CARGO_PKG_VERSION"));
+/// ```
+///
+/// E.g. for example `"my_crate::my_module::MyClass0.1.0"`.
+///
+/// If you're developing a library, it is recommended that you do not set
+/// this, and instead rely on the default naming, since that usually works
+/// better with users having multiple SemVer-incompatible versions of your
+/// library in the same binary.
+///
+///
+/// ### `#[ivars = ...]` (optional)
+///
+/// Controls [the instance variables] of the class; this is the intended way
+/// to specify the data your class stores. If you don't set this attribute,
+/// the macro will default to [`()`][unit].
+///
+/// It is recommended that you wrap your instance variables in [`Cell`],
+/// [`RefCell`], atomics or other similar interior mutability abstractions to
+/// allow mutating your instance variables. See [the docs on interior
+/// mutability][interior_mutability] for further details.
+///
+/// Beware that if you want to use the class' inherited initializers (such as
+/// `init`), you must override the subclass' designated initializers, and
+/// initialize your ivars properly in there.
+///
+/// [the instance variables]: crate::DefinedClass::Ivars
+/// [`Cell`]: core::cell::Cell
+/// [`RefCell`]: core::cell::RefCell
+/// [interior_mutability]: crate::topics::interior_mutability
+///
+///
+/// ### `#[derive(...)]`
+///
+/// This is overridden, and only works with [`PartialEq`], [`Eq`], [`Hash`]
+/// and [`Debug`].
+///
+/// The implementations delegate to the superclass' implementation, so if you
+/// want to change how they work, you should override the [`isEqual:`] and
+/// [`hash`] methods instead.
+///
+/// The `Debug` implementation currently also debug print your ivars, but that
+/// may change in the future. Prefer to override [`description`] (and
+/// potentially [`debugDescription`]) instead.
+///
+/// [`Hash`]: std::hash::Hash
+/// [`Debug`]: std::fmt::Debug
+/// [`isEqual:`]: crate::runtime::NSObjectProtocol::isEqual
+/// [`hash`]: crate::runtime::NSObjectProtocol::hash
+/// [`description`]: crate::runtime::NSObjectProtocol::description
+/// [`debugDescription`]: crate::runtime::NSObjectProtocol::debugDescription
+///
+///
+/// ### `#[cfg_attr(..., ...)]`
+///
+/// Same [as in `extern_class!`](crate::extern_class#cfg_attr-).
+///
+///
+/// ### `#[repr(...)]`
+///
+/// Same [as in `extern_class!`](crate::extern_class#repr).
+///
+///
+/// ## Inherent method definitions
+///
+/// Within the `impl` block you can define two types of functions;
+/// ["associated functions"] and ["methods"]. These are then mapped to the
+/// Objective-C equivalents "class methods" and "instance methods". In
+/// particular, if you use `self` or the special name `this` (or `_this`),
+/// your method will be registered as an instance method, and if you don't it
+/// will be registered as a class method.
+///
+/// On instance methods, you can freely choose between different types of
+/// receivers, e.g. `&self`, `self: *const Self`, `this: *const Self`, and so
+/// on. Note that using `&mut self` is not possible, if you need mutation of
+/// your class' instance variables, consider using [`Cell`] or similar
+/// instead.
+///
+/// The desired selector can be specified using the
+/// `#[unsafe(method(my:selector:))]` or `#[unsafe(method_id(my:selector:))]`
+/// attributes, similar to the [`extern_methods!`] macro.
+///
+/// If the `#[unsafe(method_id(...))]` attribute is used, the return type must
+/// be `Option<Retained<T>>` or `Retained<T>`. Additionally, if the selector
+/// is in the "init"-family, the `self`/`this` parameter must be
+/// `Allocated<Self>`.
+///
+/// Putting other attributes on the method such as `cfg`, `allow`, `doc`,
+/// `deprecated` and so on is supported. However, note that `cfg_attr` may not
+/// work correctly, due to implementation difficulty - if you have a concrete
+/// use-case, please [open an issue], then we can discuss it.
+///
+/// A transformation step is performed on the functions (to make them have the
+/// correct ABI) and hence they shouldn't really be called manually. (You
+/// can't mark them as `pub` for the same reason). Instead, use the
+/// [`extern_methods!`] macro to create a Rust interface to the methods.
+///
+/// If the parameter or return type is [`bool`], a conversion is performed to
+/// make it behave similarly to the Objective-C `BOOL`. Use [`runtime::Bool`]
+/// if you want to control this manually.
+///
+/// Note that `&mut Retained<_>` and other such out parameters are not yet
+/// supported, and may generate a panic at runtime.
+///
+/// ["associated functions"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods
+/// ["methods"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods
+/// [`Cell`]: core::cell::Cell
+/// [open an issue]: https://github.com/madsmtm/objc2/issues/new
+/// [`msg_send!`]: crate::msg_send
+/// [`runtime::Bool`]: crate::runtime::Bool
+///
+///
+/// ## Protocol implementations
+///
+/// You can specify protocols that the class should implement, along with any
+/// required/optional methods for said protocols.
+///
+/// The protocol must have been previously defined with [`extern_protocol!`].
+///
+/// The methods work exactly as normal, they're only put "under" the protocol
+/// definition to make things easier to read.
+///
+/// Putting attributes on the `impl` item such as `cfg`, `allow`, `doc`,
+/// `deprecated` and so on is supported.
+///
+/// [`extern_protocol!`]: crate::extern_protocol
+///
+///
+/// # Panics
+///
+/// The implemented `ClassType::class` method may panic in a few cases, such
+/// as if:
+/// - A class with the specified name already exists.
+/// - Debug assertions are enabled, and an overridden method's signature is not
+/// equal to the one on the superclass.
+/// - Debug assertions are enabled, and the protocol's required methods are not
+/// implemented.
+///
+/// And possibly more similar cases in the future.
+///
+///
+/// # Safety
+///
+/// Using this macro requires writing a lot of `unsafe` markers:
+///
+/// When writing `#[unsafe(super(...))]`, you must ensure that:
+/// - Any invariants that the superclass [`ClassType::Super`] may have must be
+/// upheld.
+/// - If your type implements `Drop`, the implementation must abide by the
+/// following rules:
+/// - It must not call any overridden methods.
+/// - It must not `retain` the object past the lifetime of the drop.
+/// - It must not `retain` in the same scope that `&mut self` is active.
+/// - TODO: And probably a few more. [Open an issue] if you would like
+/// guidance on whether your implementation is correct.
+///
+/// `#[unsafe(method(...))]` asserts that the types match those that are
+/// expected when the method is invoked from Objective-C. Note that unlike
+/// with [`extern_methods!`], there are no safe-guards here; you can write
+/// `i8`, but if Objective-C thinks it's an `u32`, it will cause UB when
+/// called!
+///
+/// `unsafe impl P for T { ... }` requires that all required methods of the
+/// specified protocol is implemented, and that any extra requirements
+/// (implicit or explicit) that the protocol has are upheld.
+///
+/// [`ClassType::Super`]: crate::ClassType::Super
+/// [Open an issue]: https://github.com/madsmtm/objc2/issues/new
+///
+///
+/// # Thread safety
+///
+/// The Objective-C runtime is thread-safe, so any classes you create yourself
+/// will automatically be [`Send`] and [`Sync`] (via auto traits) provided
+/// that:
+/// 1. The superclass is thread-safe, or is [`NSObject`].
+/// 2. The ivars are thread-safe.
+/// 3. The thread kind is not [`MainThreadOnly`].
+///
+/// Note though that in many cases, [the frameworks] you will be interacting
+/// with will not be thread-safe, and so in many cases it will make sense to
+/// [use interior mutability] in your custom classes.
+///
+/// [`NSObject`]: crate::runtime::NSObject
+/// [`MainThreadOnly`]: crate::MainThreadOnly
+/// [the frameworks]: crate::topics::about_generated
+/// [use interior mutability]: crate::topics::interior_mutability
+///
+///
+/// # Examples
+///
+/// Define a class `MyCustomObject` that inherits `NSObject`, has a few
+/// instance variables and methods, and implements the `NSCopying` protocol.
+///
+/// ```
+/// use std::ffi::c_int;
+///
+/// use objc2_foundation::{CopyingHelper, NSCopying, NSObject, NSObjectProtocol, NSZone};
+/// use objc2::rc::{Allocated, Retained};
+/// use objc2::{
+/// define_class, extern_methods, extern_protocol, msg_send, AnyThread,
+/// ClassType, DefinedClass, ProtocolType,
+/// };
+///
+/// #[derive(Clone)]
+/// struct Ivars {
+/// foo: u8,
+/// bar: c_int,
+/// object: Retained<NSObject>,
+/// }
+///
+/// define_class!(
+/// // SAFETY:
+/// // - The superclass NSObject does not have any subclassing requirements.
+/// // - `MyCustomObject` does not implement `Drop`.
+/// #[unsafe(super(NSObject))]
+///
+/// // If we were implementing delegate methods like `NSApplicationDelegate`,
+/// // we would specify the object to only be usable on the main thread:
+/// // #[thread_kind = MainThreadOnly]
+///
+/// // If we needed to refer to the class from elsewhere, we'd give it a
+/// // name here explicitly.
+/// // #[name = "MyCustomObject"]
+///
+/// // Specify the instance variables this class has.
+/// #[ivars = Ivars]
+/// struct MyCustomObject;
+///
+/// impl MyCustomObject {
+/// #[unsafe(method(foo))]
+/// fn __get_foo(&self) -> u8 {
+/// self.ivars().foo
+/// }
+///
+/// #[unsafe(method_id(object))]
+/// fn __get_object(&self) -> Retained<NSObject> {
+/// self.ivars().object.clone()
+/// }
+///
+/// #[unsafe(method(myClassMethod))]
+/// fn __my_class_method() -> bool {
+/// true
+/// }
+/// }
+///
+/// unsafe impl NSObjectProtocol for MyCustomObject {}
+///
+/// unsafe impl NSCopying for MyCustomObject {
+/// #[unsafe(method_id(copyWithZone:))]
+/// fn copyWithZone(&self, _zone: *const NSZone) -> Retained<Self> {
+/// let new = Self::alloc().set_ivars(self.ivars().clone());
+/// unsafe { msg_send![super(new), init] }
+/// }
+///
+/// // If we have tried to add other methods here, or had forgotten
+/// // to implement the method, we would have gotten an error.
+/// }
+/// );
+///
+/// // Specially required for `NSCopying`, but otherwise not needed.
+/// unsafe impl CopyingHelper for MyCustomObject {
+/// type Result = Self;
+/// }
+///
+/// // Add creation method.
+/// impl MyCustomObject {
+/// fn new(foo: u8) -> Retained<Self> {
+/// // Initialize instance variables.
+/// let this = Self::alloc().set_ivars(Ivars {
+/// foo,
+/// bar: 42,
+/// object: NSObject::new(),
+/// });
+/// // Call `NSObject`'s `init` method.
+/// unsafe { msg_send![super(this), init] }
+/// }
+/// }
+///
+/// // Make an interface to the methods we defined.
+/// impl MyCustomObject {
+/// extern_methods!(
+/// #[unsafe(method(foo))]
+/// pub fn get_foo(&self) -> u8;
+///
+/// #[unsafe(method(object))]
+/// pub fn get_object(&self) -> Retained<NSObject>;
+///
+/// #[unsafe(method(myClassMethod))]
+/// pub fn my_class_method() -> bool;
+/// );
+/// }
+///
+/// # // Intentionally use `fn main` for clarity
+/// fn main() {
+/// let obj = MyCustomObject::new(3);
+/// assert_eq!(obj.ivars().foo, 3);
+/// assert_eq!(obj.ivars().bar, 42);
+/// assert!(obj.ivars().object.isKindOfClass(NSObject::class()));
+///
+/// let obj = obj.copy();
+///
+/// assert_eq!(obj.get_foo(), 3);
+/// assert!(obj.get_object().isKindOfClass(NSObject::class()));
+///
+/// assert!(MyCustomObject::my_class_method());
+/// }
+/// ```
+///
+/// Approximately equivalent to the following ARC-enabled Objective-C code.
+///
+/// ```text
+/// #import <Foundation/Foundation.h>
+///
+/// @interface MyCustomObject: NSObject <NSCopying>
+/// - (instancetype)initWithFoo:(uint8_t)foo;
+/// - (uint8_t)foo;
+/// - (NSObject*)object;
+/// + (BOOL)myClassMethod;
+/// @end
+///
+///
+/// @implementation MyCustomObject {
+/// // Instance variables
+/// uint8_t foo;
+/// int bar;
+/// NSObject* _Nonnull object;
+/// }
+///
+/// - (instancetype)initWithFoo:(uint8_t)foo_arg {
+/// self = [super init];
+/// if (self) {
+/// self->foo = foo_arg;
+/// self->bar = 42;
+/// self->object = [NSObject new];
+/// }
+/// return self;
+/// }
+///
+/// - (uint8_t)foo {
+/// return self->foo;
+/// }
+///
+/// - (NSObject*)object {
+/// return self->object;
+/// }
+///
+/// + (BOOL)myClassMethod {
+/// return YES;
+/// }
+///
+/// // NSCopying
+///
+/// - (id)copyWithZone:(NSZone *)_zone {
+/// MyCustomObject* new = [[MyCustomObject alloc] initWithFoo: self->foo];
+/// new->bar = self->bar;
+/// new->obj = self->obj;
+/// return new;
+/// }
+///
+/// @end
+/// ```
+#[doc(alias = "@interface")]
+#[doc(alias = "@implementation")]
+#[macro_export]
+macro_rules! define_class {
+ {
+ // The following special attributes are supported:
+ // - #[unsafe(super($($superclasses:path),*))]
+ // - #[unsafe(super = $superclass:path)]
+ // - #[thread_kind = $thread_kind:path]
+ // - #[name = $name:literal]
+ // - #[ivars = $ivars:path]
+ $(#[$($attrs:tt)*])*
+ $v:vis struct $class:ident;
+
+ // unsafe impl Protocol for $class { ... }
+ // impl $class { ... }
+ $($impls:tt)*
+ } => {
+ // Struct and various impls.
+ $crate::__extract_struct_attributes! {
+ ($(#[$($attrs)*])*)
+
+ ($crate::__define_class_inner)
+ ($v)
+ ($class)
+ ($($impls)*)
+ }
+
+ // Methods.
+ $crate::__define_class_output_impls! {
+ $($impls)*
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! declare_class {
+ {
+ $(#[$m:meta])*
+ $v:vis struct $name:ident;
+
+ unsafe impl ClassType for $for_class:ty {
+ $(#[inherits($($inheritance_rest:ty),+)])?
+ type Super = $superclass:ty;
+
+ type Mutability = $mutability:ty;
+
+ const NAME: &'static str = $name_const:expr;
+ }
+
+ impl DefinedClass for $for_defined:ty {
+ $(type Ivars = $ivars:ty;)?
+ }
+
+ $($impls:tt)*
+ } => {
+ // For slightly better diagnostics
+ $(#[$m])*
+ $v struct $name;
+
+ $crate::__macro_helpers::compile_error!("declare_class! has been renamed to define_class!, and the syntax has changed")
+ }
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __define_class_inner {
+ (
+ ($v:vis)
+ ($class:ident)
+ ($($impls:tt)*)
+
+ ($($safety:tt $superclass:path $(, $superclasses:path)* $(,)?)?)
+ ($($($thread_kind:tt)+)?)
+ ($($name:tt)*)
+ ($($ivars:tt)*)
+ ($($derives:tt)*)
+ ($($attr_struct:tt)*)
+ ($($attr_impl:tt)*)
+ ) => {
+ // Ensure that the type has the same layout as the superclass.
+ // #[repr(transparent)] doesn't work because the superclass is a ZST.
+ #[repr(C)]
+ $($attr_struct)*
+ $v struct $class {
+ // Superclasses are deallocated by calling `[super dealloc]`.
+ //
+ // Auto traits are taken from `__SubclassingType` (which is
+ // usually the super class).
+ __superclass: $crate::__macro_helpers::ManuallyDrop<$crate::__fallback_if_not_set! {
+ ($(<$superclass as $crate::ClassType>::__SubclassingType)?)
+ // For better diagnostics, see also __extern_class_inner!
+ ($crate::runtime::NSObject)
+ }>,
+ __phantom: $crate::__macro_helpers::PhantomData<(
+ // Include ivars for auto traits.
+ <Self as $crate::DefinedClass>::Ivars,
+ // Translate thread kind to appropriate auto traits.
+ $crate::__macro_helpers::ThreadKindAutoTraits<<Self as $crate::ClassType>::ThreadKind>,
+ )>,
+ }
+
+ $crate::__extern_class_impl_traits! {
+ ($($attr_impl)*)
+ (unsafe impl)
+ ($class)
+ ($($superclass, $($superclasses,)*)? $crate::runtime::AnyObject)
+ }
+
+ $crate::__define_class_derives! {
+ ($($attr_impl)*)
+ ($class)
+ ($($derives)*)
+ }
+
+ // Anonymous block to hide the shared statics
+ $($attr_impl)*
+ const _: () = {
+ static __OBJC2_CLASS: $crate::__macro_helpers::SyncUnsafeCell<
+ $crate::__macro_helpers::MaybeUninit<&'static $crate::runtime::AnyClass>
+ > = $crate::__macro_helpers::SyncUnsafeCell::new($crate::__macro_helpers::MaybeUninit::uninit());
+ static __OBJC2_IVAR_OFFSET: $crate::__macro_helpers::SyncUnsafeCell<
+ $crate::__macro_helpers::MaybeUninit<$crate::__macro_helpers::isize>
+ > = $crate::__macro_helpers::SyncUnsafeCell::new($crate::__macro_helpers::MaybeUninit::uninit());
+ static __OBJC2_DROP_FLAG_OFFSET: $crate::__macro_helpers::SyncUnsafeCell<
+ $crate::__macro_helpers::MaybeUninit<$crate::__macro_helpers::isize>
+ > = $crate::__macro_helpers::SyncUnsafeCell::new($crate::__macro_helpers::MaybeUninit::uninit());
+
+ // Creation
+ unsafe impl $crate::ClassType for $class {
+ type Super = $crate::__fallback_if_not_set! {
+ ($($superclass)?)
+ // For better diagnostics, see also __extern_class_inner!
+ ($crate::runtime::NSObject)
+ };
+
+ type ThreadKind = $crate::__fallback_if_not_set! {
+ ($(dyn ($($thread_kind)+))?)
+ // Default to the super class' thread kind
+ (<<Self as $crate::ClassType>::Super as $crate::ClassType>::ThreadKind)
+ };
+
+ const NAME: &'static $crate::__macro_helpers::str = $crate::__fallback_if_not_set! {
+ ($($name)*)
+ (
+ $crate::__macro_helpers::concat!(
+ // Module path includes crate name when in library.
+ $crate::__macro_helpers::module_path!(),
+ "::",
+ $crate::__macro_helpers::stringify!($class),
+ $crate::__macro_helpers::env!("CARGO_PKG_VERSION"),
+ )
+ )
+ };
+
+ fn class() -> &'static $crate::runtime::AnyClass {
+ let _ = <Self as $crate::__macro_helpers::ValidThreadKind<Self::ThreadKind>>::check;
+ let _ = <Self as $crate::__macro_helpers::MainThreadOnlyDoesNotImplSendSync<_>>::check;
+
+ // TODO: Use `std::sync::OnceLock`
+ static REGISTER_CLASS: $crate::__macro_helpers::Once = $crate::__macro_helpers::Once::new();
+
+ REGISTER_CLASS.call_once(|| {
+ let mut __objc2_builder = $crate::__macro_helpers::ClassBuilderHelper::<Self>::new();
+
+ // Implement protocols and methods
+ $crate::__define_class_register_impls! {
+ (__objc2_builder)
+ $($impls)*
+ }
+
+ let (__objc2_cls, __objc2_ivar_offset, __objc2_drop_flag_offset) = __objc2_builder.register();
+
+ // SAFETY: Modification is ensured by `Once` to happen
+ // before any access to the variables.
+ unsafe {
+ __OBJC2_CLASS.get().write($crate::__macro_helpers::MaybeUninit::new(__objc2_cls));
+ if <Self as $crate::__macro_helpers::DefinedIvarsHelper>::HAS_IVARS {
+ __OBJC2_IVAR_OFFSET.get().write($crate::__macro_helpers::MaybeUninit::new(__objc2_ivar_offset));
+ }
+ if <Self as $crate::__macro_helpers::DefinedIvarsHelper>::HAS_DROP_FLAG {
+ __OBJC2_DROP_FLAG_OFFSET.get().write($crate::__macro_helpers::MaybeUninit::new(__objc2_drop_flag_offset));
+ }
+ }
+ });
+
+ // SAFETY: We just registered the class, so is now available
+ unsafe { __OBJC2_CLASS.get().read().assume_init() }
+ }
+
+ #[inline]
+ fn as_super(&self) -> &Self::Super {
+ &*self.__superclass
+ }
+
+ const __INNER: () = ();
+
+ type __SubclassingType = Self;
+ }
+
+ impl $crate::DefinedClass for $class {
+ type Ivars = $crate::__select_ivars!($($ivars)?);
+
+ #[inline]
+ fn __ivars_offset() -> $crate::__macro_helpers::isize {
+ // Only access ivar offset if we have an ivar.
+ //
+ // This makes the offset not be included in the final
+ // executable if it's not needed.
+ if <Self as $crate::__macro_helpers::DefinedIvarsHelper>::HAS_IVARS {
+ // SAFETY: Accessing the offset is guaranteed to only be
+ // done after the class has been initialized.
+ unsafe { __OBJC2_IVAR_OFFSET.get().read().assume_init() }
+ } else {
+ // Fall back to an offset of zero.
+ //
+ // This is fine, since any reads here will only be via zero-sized
+ // ivars, where the actual pointer doesn't matter.
+ 0
+ }
+ }
+
+ #[inline]
+ fn __drop_flag_offset() -> $crate::__macro_helpers::isize {
+ if <Self as $crate::__macro_helpers::DefinedIvarsHelper>::HAS_DROP_FLAG {
+ // SAFETY: Same as above.
+ unsafe { __OBJC2_DROP_FLAG_OFFSET.get().read().assume_init() }
+ } else {
+ // Fall back to an offset of zero.
+ //
+ // This is fine, since the drop flag is never actually used in the
+ // cases where it was not added.
+ 0
+ }
+ }
+
+ // SAFETY: The offsets are implemented correctly
+ const __UNSAFE_OFFSETS_CORRECT: () = ();
+ }
+ };
+
+ // SAFETY: This macro only allows non-generic classes and non-generic
+ // classes are always valid downcast targets.
+ $($attr_impl)*
+ unsafe impl $crate::DowncastTarget for $class {}
+
+ $($attr_impl)*
+ $crate::__extern_class_check_super_unsafe!($($safety $superclass)?);
+ };
+}
+
+/// Mirror of [`crate::__extern_class_derives`].
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __define_class_derives {
+ // Base case
+ (
+ ($($attr_impl:tt)*)
+ ($for:path)
+ ($(,)*)
+ ) => {};
+
+ // Debug
+ (
+ ($($attr_impl:tt)*)
+ ($for:path)
+ (
+ $(,)*
+ Debug
+ $($rest:tt)*
+ )
+ ) => {
+ $($attr_impl)*
+ #[automatically_derived]
+ impl $crate::__macro_helpers::fmt::Debug for $for {
+ fn fmt(&self, f: &mut $crate::__macro_helpers::fmt::Formatter<'_>) -> $crate::__macro_helpers::fmt::Result {
+ f.debug_struct($crate::__macro_helpers::stringify!($for))
+ .field("super", &**self.__superclass)
+ .field("ivars", <Self as $crate::DefinedClass>::ivars(self))
+ .finish()
+ }
+ }
+
+ $crate::__define_class_derives! {
+ ($($attr_impl)*)
+ ($for)
+ ($($rest)*)
+ }
+ };
+
+ // PartialEq
+ (
+ ($($attr_impl:tt)*)
+ ($for:path)
+ (
+ $(,)*
+ PartialEq
+ $($rest:tt)*
+ )
+ ) => {
+ $($attr_impl)*
+ #[automatically_derived]
+ impl $crate::__macro_helpers::PartialEq for $for {
+ #[inline]
+ fn eq(&self, other: &Self) -> $crate::__macro_helpers::bool {
+ // Delegate to the superclass (referential equality)
+ $crate::__macro_helpers::PartialEq::eq(&self.__superclass, &other.__superclass)
+ }
+ }
+
+ $crate::__define_class_derives! {
+ ($($attr_impl)*)
+ ($for)
+ ($($rest)*)
+ }
+ };
+
+ // Eq
+ (
+ ($($attr_impl:tt)*)
+ ($for:path)
+ (
+ $(,)*
+ Eq
+ $($rest:tt)*
+ )
+ ) => {
+ $($attr_impl)*
+ #[automatically_derived]
+ impl $crate::__macro_helpers::Eq for $for {}
+
+ $crate::__define_class_derives! {
+ ($($attr_impl)*)
+ ($for)
+ ($($rest)*)
+ }
+ };
+
+ // Hash
+ (
+ ($($attr_impl:tt)*)
+ ($for:path)
+ (
+ $(,)*
+ Hash
+ $($rest:tt)*
+ )
+ ) => {
+ $($attr_impl)*
+ #[automatically_derived]
+ impl $crate::__macro_helpers::Hash for $for {
+ #[inline]
+ fn hash<H: $crate::__macro_helpers::Hasher>(&self, state: &mut H) {
+ // Delegate to the superclass (which hashes the reference)
+ $crate::__macro_helpers::Hash::hash(&self.__superclass, state)
+ }
+ }
+
+ $crate::__define_class_derives! {
+ ($($attr_impl)*)
+ ($for)
+ ($($rest)*)
+ }
+ };
+
+ // Unhandled derive
+ (
+ ($($attr_impl:tt)*)
+ ($for:path)
+ (
+ $(,)*
+ $derive:path
+ $(, $($rest:tt)*)?
+ )
+ ) => {
+ const _: () = {
+ // For better diagnostics.
+ #[derive($derive)]
+ struct Derive;
+ };
+ $crate::__macro_helpers::compile_error!($crate::__macro_helpers::stringify!(
+ #[derive($derive)] is not supported in define_class!
+ ));
+
+ $crate::__define_class_derives! {
+ ($($attr_impl)*)
+ ($for)
+ ($($($rest)*)?)
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __select_ivars {
+ ($ivars:ty) => {
+ $ivars
+ };
+ () => {
+ // Default ivars to unit
+ ()
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __define_class_output_impls {
+ // Base-case
+ () => {};
+
+ // With protocol
+ (
+ $(#[$m:meta])*
+ unsafe impl $protocol:ident for $for:ty {
+ $($methods:tt)*
+ }
+
+ $($rest:tt)*
+ ) => {
+ // SAFETY: Upheld by caller
+ $(#[$m])*
+ unsafe impl $protocol for $for {}
+
+ $(#[$m])*
+ impl $for {
+ $crate::__define_class_output_methods! {
+ $($methods)*
+ }
+ }
+
+ $crate::__define_class_output_impls!{
+ $($rest)*
+ }
+ };
+
+ // Without protocol
+ (
+ $(#[$m:meta])*
+ impl $for:ty {
+ $($methods:tt)*
+ }
+
+ $($rest:tt)*
+ ) => {
+ $(#[$m])*
+ impl $for {
+ $crate::__define_class_output_methods! {
+ $($methods)*
+ }
+ }
+
+ $crate::__define_class_output_impls! {
+ $($rest)*
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __define_class_output_methods {
+ // Base case
+ {} => {};
+
+ // Unsafe variant
+ {
+ $(#[$($m:tt)*])*
+ unsafe fn $name:ident($($params:tt)*) $(-> $ret:ty)? $body:block
+
+ $($rest:tt)*
+ } => {
+ $crate::__rewrite_self_param! {
+ ($($params)*)
+
+ ($crate::__extract_method_attributes)
+ ($(#[$($m)*])*)
+
+ ($crate::__define_class_method_out)
+ (unsafe)
+ ($name)
+ ($($ret)?)
+ ($body)
+ }
+
+ $crate::__define_class_output_methods! {
+ $($rest)*
+ }
+ };
+
+ // Safe variant
+ {
+ $(#[$($m:tt)*])*
+ fn $name:ident($($params:tt)*) $(-> $ret:ty)? $body:block
+
+ $($rest:tt)*
+ } => {
+ $crate::__rewrite_self_param! {
+ ($($params)*)
+
+ ($crate::__extract_method_attributes)
+ ($(#[$($m)*])*)
+
+ ($crate::__define_class_method_out)
+ ()
+ ($name)
+ ($($ret)?)
+ ($body)
+ }
+
+ $crate::__define_class_output_methods! {
+ $($rest)*
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __define_class_register_impls {
+ // Base-case
+ (
+ ($builder:ident)
+ ) => {};
+
+ // With protocol
+ (
+ ($builder:ident)
+
+ $(#[$($m:tt)*])*
+ unsafe impl $protocol:ident for $for:ty {
+ $($methods:tt)*
+ }
+
+ $($rest:tt)*
+ ) => {
+ $crate::__extract_and_apply_cfg_attributes! {
+ ($(#[$($m)*])*)
+
+ // Implement protocol
+ #[allow(unused_mut)]
+ let mut __objc2_protocol_builder = $builder.add_protocol_methods::<dyn $protocol>();
+
+ // In case the user's function is marked `deprecated`
+ #[allow(deprecated)]
+ // In case the user did not specify any methods
+ #[allow(unused_unsafe)]
+ // SAFETY: Upheld by caller
+ unsafe {
+ $crate::__define_class_register_methods! {
+ (__objc2_protocol_builder)
+
+ $($methods)*
+ }
+ }
+
+ // Finished creating protocol; get error message if any
+ __objc2_protocol_builder.finish();
+ }
+
+ $crate::__define_class_register_impls! {
+ ($builder)
+ $($rest)*
+ }
+ };
+
+ // Without protocol
+ (
+ ($builder:ident)
+
+ $(#[$($m:tt)*])*
+ impl $for:ty {
+ $($methods:tt)*
+ }
+
+ $($rest:tt)*
+ ) => {
+ $crate::__extract_and_apply_cfg_attributes! {
+ ($(#[$($m)*])*)
+
+ // In case the user's function is marked `deprecated`
+ #[allow(deprecated)]
+ // In case the user did not specify any methods
+ #[allow(unused_unsafe)]
+ // SAFETY: Upheld by caller
+ unsafe {
+ $crate::__define_class_register_methods! {
+ ($builder)
+
+ $($methods)*
+ }
+ }
+ }
+
+ $crate::__define_class_register_impls! {
+ ($builder)
+ $($rest)*
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __define_class_register_methods {
+ // Base case
+ {
+ ($builder:ident)
+ } => {};
+
+ // Unsafe variant
+ {
+ ($builder:ident)
+
+ $(#[$($m:tt)*])*
+ unsafe fn $name:ident($($params:tt)*) $(-> $ret:ty)? $body:block
+
+ $($rest:tt)*
+ } => {
+ $crate::__rewrite_self_param! {
+ ($($params)*)
+
+ ($crate::__extract_method_attributes)
+ ($(#[$($m)*])*)
+
+ ($crate::__define_class_register_out)
+ ($builder)
+ (unsafe)
+ ($name)
+ ($($ret)?)
+ ($body)
+ }
+
+ $crate::__define_class_register_methods! {
+ ($builder)
+
+ $($rest)*
+ }
+ };
+
+ // Safe variant
+ {
+ ($builder:ident)
+
+ $(#[$($m:tt)*])*
+ fn $name:ident($($params:tt)*) $(-> $ret:ty)? $body:block
+
+ $($rest:tt)*
+ } => {
+ $crate::__rewrite_self_param! {
+ ($($params)*)
+
+ ($crate::__extract_method_attributes)
+ ($(#[$($m)*])*)
+
+ ($crate::__define_class_register_out)
+ ($builder)
+ ()
+ ($name)
+ ($($ret)?)
+ ($body)
+ }
+
+ $crate::__define_class_register_methods! {
+ ($builder)
+
+ $($rest)*
+ }
+ };
+
+ // Consume associated items for better UI.
+ //
+ // This will still fail inside __define_class_output_methods!
+ {
+ ($builder:ident)
+
+ $_associated_item:item
+
+ $($rest:tt)*
+ } => {
+ $crate::__define_class_output_methods! {
+ ($builder)
+
+ $($rest)*
+ }
+ }
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __define_class_method_out {
+ {
+ ($($qualifiers:tt)*)
+ ($name:ident)
+ ($($ret:ty)?)
+ ($body:block)
+
+ ($builder_method:ident)
+ ($receiver:expr)
+ ($receiver_ty:ty)
+ ($($params_prefix:tt)*)
+ ($($params_rest:tt)*)
+
+ ($($m_method:tt)*)
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+ } => {
+ $crate::__define_class_rewrite_params! {
+ ($($params_rest)*)
+ ()
+ ()
+
+ ($crate::__define_class_method_out_inner)
+
+ ($($qualifiers)*)
+ ($name)
+ ($($ret)?)
+ ($body)
+
+ ($builder_method)
+ ($receiver)
+ ($receiver_ty)
+ ($($params_prefix)*)
+
+ ($($m_method)*)
+ ($($method_family)*)
+ ($($optional)*)
+ ($($attr_method)*)
+ ($($attr_use)*)
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __define_class_rewrite_params {
+ // Convert _
+ {
+ (_ : $param_ty:ty $(, $($params_rest:tt)*)?)
+ ($($params_converted:tt)*)
+ ($($body_prefix:tt)*)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => {
+ $crate::__define_class_rewrite_params! {
+ ($($($params_rest)*)?)
+ ($($params_converted)* _ : <$param_ty as $crate::__macro_helpers::ConvertArgument>::__Inner,)
+ ($($body_prefix)*)
+
+ ($out_macro)
+ $($macro_args)*
+ }
+ };
+ // Convert mut
+ {
+ (mut $param:ident : $param_ty:ty $(, $($params_rest:tt)*)?)
+ ($($params_converted:tt)*)
+ ($($body_prefix:tt)*)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => {
+ $crate::__define_class_rewrite_params! {
+ ($($($params_rest)*)?)
+ ($($params_converted)* $param : <$param_ty as $crate::__macro_helpers::ConvertArgument>::__Inner,)
+ (
+ $($body_prefix)*
+ let mut $param = <$param_ty as $crate::__macro_helpers::ConvertArgument>::__from_defined_param($param);
+ )
+
+ ($out_macro)
+ $($macro_args)*
+ }
+ };
+ // Convert
+ {
+ ($param:ident : $param_ty:ty $(, $($params_rest:tt)*)?)
+ ($($params_converted:tt)*)
+ ($($body_prefix:tt)*)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => {
+ $crate::__define_class_rewrite_params! {
+ ($($($params_rest)*)?)
+ ($($params_converted)* $param : <$param_ty as $crate::__macro_helpers::ConvertArgument>::__Inner,)
+ (
+ $($body_prefix)*
+ let $param = <$param_ty as $crate::__macro_helpers::ConvertArgument>::__from_defined_param($param);
+ )
+
+ ($out_macro)
+ $($macro_args)*
+ }
+ };
+ // Output result
+ {
+ ()
+ ($($params_converted:tt)*)
+ ($($body_prefix:tt)*)
+
+ ($out_macro:path)
+ $($macro_args:tt)*
+ } => {
+ $out_macro! {
+ $($macro_args)*
+
+ ($($params_converted)*)
+ ($($body_prefix)*)
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __define_class_method_out_inner {
+ // #[unsafe(method(...))]
+ {
+ ($($qualifiers:tt)*)
+ ($name:ident)
+ ($($ret:ty)?)
+ ($body:block)
+
+ ($__builder_method:ident)
+ ($__receiver:expr)
+ ($__receiver_ty:ty)
+ ($($params_prefix:tt)*)
+
+ (method($($__sel:tt)*))
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+
+ ($($params_converted:tt)*)
+ ($($body_prefix:tt)*)
+ } => {
+ $($attr_method)*
+ #[allow(clippy::diverging_sub_expression)]
+ $($qualifiers)* extern "C-unwind" fn $name(
+ $($params_prefix)*
+ $($params_converted)*
+ ) $(-> <$ret as $crate::__macro_helpers::ConvertReturn<()>>::Inner)? {
+ $crate::__define_class_no_method_family!($($method_family)*);
+ $($body_prefix)*
+ $crate::__convert_result! {
+ $body $(; $ret)?
+ }
+ }
+ };
+
+ // #[unsafe(method_id(...))]
+ {
+ ($($qualifiers:tt)*)
+ ($name:ident)
+ ($ret:ty)
+ ($body:block)
+
+ ($__builder_method:ident)
+ ($__receiver:expr)
+ ($receiver_ty:ty)
+ ($($params_prefix:tt)*)
+
+ (method_id($($sel:tt)*))
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+
+ ($($params_converted:tt)*)
+ ($($body_prefix:tt)*)
+ } => {
+ $($attr_method)*
+ #[allow(clippy::diverging_sub_expression)]
+ $($qualifiers)* extern "C-unwind" fn $name(
+ $($params_prefix)*
+ $($params_converted)*
+ ) -> $crate::__macro_helpers::RetainedReturnValue {
+ // TODO: Somehow tell the compiler that `this: Allocated<Self>` is non-null.
+
+ $($body_prefix)*
+
+ let __objc2_result = $body;
+
+ #[allow(unreachable_code)]
+ <$crate::__method_family!(($($method_family)*) ($($sel)*)) as $crate::__macro_helpers::MessageReceiveRetained<
+ $receiver_ty,
+ $ret,
+ >>::into_return(__objc2_result)
+ }
+ };
+
+ {
+ ($($qualifiers:tt)*)
+ ($name:ident)
+ ()
+ ($body:block)
+
+ ($__builder_method:ident)
+ ($__receiver:expr)
+ ($__receiver_ty:ty)
+ ($($params_prefix:tt)*)
+
+ (method_id($($sel:tt)*))
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+
+ ($($params_converted:tt)*)
+ ($($body_prefix:tt)*)
+ } => {
+ $($attr_method)*
+ $($qualifiers)* extern "C-unwind" fn $name() {
+ $crate::__macro_helpers::compile_error!("`#[unsafe(method_id(...))]` must have a return type")
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __convert_result {
+ ($body:block) => {
+ $body
+ };
+ ($body:block; $ret:ty) => {
+ let __objc2_result = $body;
+ #[allow(unreachable_code)]
+ <$ret as $crate::__macro_helpers::ConvertReturn<()>>::convert_defined_return(__objc2_result)
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __define_class_register_out {
+ {
+ ($builder:ident)
+ ($($qualifiers:tt)*)
+ ($name:ident)
+ ($($__ret:ty)?)
+ ($__body:block)
+
+ ($builder_method:ident)
+ ($__receiver:expr)
+ ($__receiver_ty:ty)
+ ($($__params_prefix:tt)*)
+ ($($params_rest:tt)*)
+
+ ($method_or_method_id:ident($($sel:tt)*))
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+ } => {
+ $($attr_use)*
+ {
+ $crate::__define_class_invalid_selectors!($method_or_method_id($($sel)*));
+ $crate::__define_class_no_optional!($($optional)*);
+
+ $builder.$builder_method(
+ $crate::sel!($($sel)*),
+ Self::$name as $crate::__fn_ptr! {
+ ($($qualifiers)*)
+ (_, _,)
+ $($params_rest)*
+ },
+ );
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __define_class_invalid_selectors {
+ (method(dealloc)) => {
+ $crate::__macro_helpers::compile_error!(
+ "`#[unsafe(method(dealloc))]` is not supported. Implement `Drop` for the type instead"
+ )
+ };
+ (method_id(dealloc)) => {
+ $crate::__macro_helpers::compile_error!(
+ "`#[unsafe(method_id(dealloc))]` is not supported. Implement `Drop` for the type instead"
+ )
+ };
+ (method_id(alloc)) => {
+ $crate::__macro_helpers::compile_error!($crate::__macro_helpers::concat!(
+ "`#[unsafe(method_id(alloc))]` is not supported. ",
+ "Use `#[unsafe(method(alloc))]` and do the memory management yourself",
+ ))
+ };
+ (method_id(retain)) => {
+ $crate::__macro_helpers::compile_error!($crate::__macro_helpers::concat!(
+ "`#[unsafe(method_id(retain))]` is not supported. ",
+ "Use `#[unsafe(method(retain))]` and do the memory management yourself",
+ ))
+ };
+ (method_id(release)) => {
+ $crate::__macro_helpers::compile_error!($crate::__macro_helpers::concat!(
+ "`#[unsafe(method_id(release))]` is not supported. ",
+ "Use `#[unsafe(method(release))]` and do the memory management yourself",
+ ))
+ };
+ (method_id(autorelease)) => {
+ $crate::__macro_helpers::compile_error!($crate::__macro_helpers::concat!(
+ "`#[unsafe(method_id(autorelease))]` is not supported. ",
+ "Use `#[unsafe(method(autorelease))]` and do the memory management yourself",
+ ))
+ };
+ ($method_or_method_id:ident($($sel:tt)*)) => {};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __define_class_no_method_family {
+ () => {};
+ ($($t:tt)+) => {
+ $crate::__macro_helpers::compile_error!(
+ "`#[unsafe(method_family = ...)]` is not yet supported in `define_class!` together with `#[unsafe(method(...))]`"
+ )
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __define_class_no_optional {
+ () => {};
+ (#[optional]) => {
+ $crate::__macro_helpers::compile_error!(
+ "`#[optional]` is only supported in `extern_protocol!`"
+ )
+ };
+}
+
+/// Create function pointer type with inferred parameters.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __fn_ptr {
+ (
+ ($($qualifiers:tt)*)
+ ($($output:tt)*)
+ $(,)?
+ ) => {
+ $($qualifiers)* extern "C-unwind" fn($($output)*) -> _
+ };
+ (
+ ($($qualifiers:tt)*)
+ ($($output:tt)*)
+ _ : $param_ty:ty $(, $($rest:tt)*)?
+ ) => {
+ $crate::__fn_ptr! {
+ ($($qualifiers)*)
+ ($($output)* _,)
+ $($($rest)*)?
+ }
+ };
+ (
+ ($($qualifiers:tt)*)
+ ($($output:tt)*)
+ mut $param:ident : $param_ty:ty $(, $($rest:tt)*)?
+ ) => {
+ $crate::__fn_ptr! {
+ ($($qualifiers)*)
+ ($($output)* _,)
+ $($($rest)*)?
+ }
+ };
+ (
+ ($($qualifiers:tt)*)
+ ($($output:tt)*)
+ $param:ident : $param_ty:ty $(, $($rest:tt)*)?
+ ) => {
+ $crate::__fn_ptr! {
+ ($($qualifiers)*)
+ ($($output)* _,)
+ $($($rest)*)?
+ }
+ };
+}
diff --git a/third_party/rust/objc2/src/macros/extern_class.rs b/third_party/rust/objc2/src/macros/extern_class.rs
@@ -0,0 +1,669 @@
+/// Create a new type to represent a class.
+///
+/// This is similar to an `@interface` declaration in Objective-C.
+///
+/// It is useful for things like `objc2-foundation`, which needs to create
+/// interfaces to existing, externally defined classes like `NSString`,
+/// `NSURL` and so on, but can also be useful for users that have custom
+/// classes written in Objective-C that they want to access from Rust.
+///
+///
+/// # Specification
+///
+/// The syntax is similar enough to Rust syntax that if you invoke the macro
+/// with parentheses (as opposed to curly brackets), [`rustfmt` will be able to
+/// format the contents][rustfmt-macros] (so e.g. as `extern_class!( ... );`).
+///
+/// The macro creates an opaque struct containing the superclass (which means
+/// that auto traits are inherited from the superclass), and implements the
+/// following traits for it to allow easier usage as an Objective-C object:
+///
+/// - [`RefEncode`][crate::RefEncode]
+/// - [`Message`][crate::Message]
+/// - [`Deref<Target = $superclass>`][core::ops::Deref]
+/// - [`ClassType`][crate::ClassType]
+/// - [`DowncastTarget`][$crate::DowncastTarget]
+/// - [`AsRef<$inheritance_chain>`][AsRef]
+/// - [`Borrow<$inheritance_chain>`][core::borrow::Borrow]
+///
+/// If generics are specified, these will be placed in a [`PhantomData`].
+///
+/// [rustfmt-macros]: https://github.com/rust-lang/rustfmt/discussions/5437
+/// [`PhantomData`]: core::marker::PhantomData
+///
+///
+/// ## Attributes
+///
+/// You can add most normal attributes to the class, including `#[cfg(...)]`,
+/// `#[allow(...)]` and doc comments.
+///
+/// Exceptions and special attributes are noted below.
+///
+///
+/// ### `#[unsafe(super(...))]` (required)
+///
+/// Controls the [superclass][crate::ClassType::Super] and the rest of the
+/// inheritance chain. This attribute is required.
+///
+/// Due to Rust trait limitations, specifying e.g. the superclass `NSData`
+/// would not give you the ability to convert via `AsRef` to `NSObject`.
+/// Therefore, you can optionally specify additional parts of the inheritance
+/// in this attribute.
+///
+///
+/// ### `#[thread_kind = ...]` (optional)
+///
+/// Controls the [thread kind][crate::ClassType::ThreadKind], i.e. it can be
+/// set to [`MainThreadOnly`] if the object is only usable on the main thread.
+///
+/// [`MainThreadOnly`]: crate::MainThreadOnly
+///
+///
+/// ### `#[name = "..."]` (optional)
+///
+/// Controls the [name][crate::ClassType::NAME] of the class.
+///
+/// If not specified, this will default to the struct name.
+///
+///
+/// ### `#[derive(...)]`
+///
+/// This is overridden, and only works with [`PartialEq`], [`Eq`], [`Hash`]
+/// and [`Debug`].
+///
+/// [`Hash`]: std::hash::Hash
+/// [`Debug`]: std::fmt::Debug
+///
+///
+/// ### `#[cfg_attr(..., ...)]`
+///
+/// This is only supported for attributes that apply to the struct itself
+/// (i.e. not supported for attributes that apply to implementations, or any
+/// of the custom attributes).
+///
+///
+/// ### `#[repr(...)]`
+///
+/// Not allowed (the macro uses this attribute internally).
+///
+///
+/// # Safety
+///
+/// When writing `#[unsafe(super(...))]`, you must ensure that:
+/// 1. The first superclass is correct.
+/// 2. The thread kind is set to `MainThreadOnly` if the class can only be
+/// used from the main thread.
+///
+///
+/// # Examples
+///
+/// Create a new type to represent the `NSFormatter` class (for demonstration,
+/// `objc2_foundation::NSFormatter` exist for exactly this purpose).
+///
+/// ```
+/// # #[cfg(not_available)]
+/// use objc2_foundation::{NSCoding, NSCopying, NSObjectProtocol};
+/// # use objc2::runtime::NSObjectProtocol;
+/// use objc2::rc::Retained;
+/// use objc2::runtime::NSObject;
+/// use objc2::{extern_class, extern_conformance, msg_send, ClassType};
+///
+/// extern_class!(
+/// /// An example description, to show that doc comments work.
+/// // Specify the superclass, in this case `NSObject`
+/// #[unsafe(super(NSObject))]
+/// // We could specify that the class is only usable on the main thread.
+/// // #[thread_kind = MainThreadOnly];
+/// // And specify the name of the class, if it differed from the struct.
+/// // #[name = "NSFormatter"];
+/// // These derives use the superclass' implementation.
+/// #[derive(PartialEq, Eq, Hash, Debug)]
+/// pub struct NSFormatter;
+/// );
+///
+/// // Note: We have to specify the protocols for the superclasses as well,
+/// // since Rust doesn't do inheritance.
+/// extern_conformance!(unsafe impl NSObjectProtocol for NSFormatter {});
+/// # #[cfg(not_available)]
+/// extern_conformance!(unsafe impl NSCopying for NSFormatter {});
+/// # #[cfg(not_available)]
+/// extern_conformance!(unsafe impl NSCoding for NSFormatter {});
+///
+/// fn main() {
+/// // Provided by the implementation of `ClassType`
+/// let cls = NSFormatter::class();
+///
+/// // `NSFormatter` implements `Message`:
+/// let obj: Retained<NSFormatter> = unsafe { msg_send![cls, new] };
+/// }
+/// ```
+///
+/// Represent the `NSDateFormatter` class, using the `NSFormatter` type we
+/// declared previously to specify as its superclass.
+///
+/// ```
+/// # #[cfg(not_available)]
+/// use objc2_foundation::{NSCoding, NSCopying, NSObjectProtocol};
+/// # use objc2::runtime::NSObjectProtocol;
+/// use objc2::runtime::NSObject;
+/// use objc2::{extern_class, extern_conformance, ClassType};
+/// #
+/// # extern_class!(
+/// # #[unsafe(super(NSObject))]
+/// # #[derive(PartialEq, Eq, Hash, Debug)]
+/// # pub struct NSFormatter;
+/// # );
+///
+/// extern_class!(
+/// // Specify the correct inheritance chain
+/// #[unsafe(super(NSFormatter, NSObject))]
+/// #[derive(PartialEq, Eq, Hash, Debug)]
+/// pub struct NSDateFormatter;
+/// );
+///
+/// // Similarly, we can specify the protocols that this implements here:
+/// extern_conformance!(unsafe impl NSObjectProtocol for NSDateFormatter {});
+/// # #[cfg(not_available)]
+/// extern_conformance!(unsafe impl NSCopying for NSDateFormatter {});
+/// # #[cfg(not_available)]
+/// extern_conformance!(unsafe impl NSCoding for NSDateFormatter {});
+/// ```
+///
+/// See the source code of `objc2-foundation` for many more examples.
+#[doc(alias = "@interface")]
+#[macro_export]
+macro_rules! extern_class {
+ (
+ // The following special attributes are supported:
+ // - #[unsafe(super($($superclasses:path),*))]
+ // - #[unsafe(super = $superclass:path)]
+ // - #[thread_kind = $thread_kind:path]
+ // - #[name = $name:literal]
+ //
+ // As well as the following standard attributes:
+ // - #[derive(Eq, PartialEq, Hash, Debug)] (only those four are supported)
+ // - #[cfg(...)]
+ // - #[cfg_attr(..., ...)] (only for standard attributes)
+ // - #[doc(...)]
+ // - #[deprecated(...)]
+ // - #[allow/expect/warn/deny/forbid]
+ //
+ // Note that `#[repr(...)]` and `#[non_exhaustive]` are intentionally not supported.
+ $(#[$($attrs:tt)*])*
+ $v:vis struct $class:ident;
+ ) => {
+ $crate::__extract_struct_attributes! {
+ ($(#[$($attrs)*])*)
+
+ ($crate::__extern_class_inner)
+ ($v)
+ ($class)
+ () // No generics
+ }
+ };
+ (
+ // Generic version. Currently pretty ill supported.
+ $(#[$($attrs:tt)*])*
+ $v:vis struct $class:ident<
+ $($generic:ident $(: $(?$bound_sized:ident)? $($bound:ident)?)? $(= $default:ty)?),*
+ $(,)?
+ >;
+ ) => {
+ $crate::__extract_struct_attributes! {
+ ($(#[$($attrs)*])*)
+
+ ($crate::__extern_class_inner)
+ ($v)
+ ($class)
+ ($(
+ ($generic)
+ ($($(?$bound_sized)? $($bound)?)?)
+ ($($default)?)
+ )*)
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extern_class_inner {
+ (
+ ($v:vis)
+ ($class:ident)
+ ($($(
+ ($generic:ident)
+ ($($($bounds:tt)+)?)
+ ($($default:ty)?)
+ )+)?)
+
+ ($($safety:tt $superclass:path $(, $superclasses:path)* $(,)?)?)
+ ($($($thread_kind:tt)+)?)
+ ($($name:tt)*)
+ ($($ivars:tt)*)
+ ($($derives:tt)*)
+ ($($attr_struct:tt)*)
+ ($($attr_impl:tt)*)
+ ) => {
+ // Ensure that the type has the same layout as the superclass.
+ // #[repr(transparent)] doesn't work because the superclass is a ZST.
+ #[repr(C)]
+ $($attr_struct)*
+ $v struct $class $(<$($generic $(: $($bounds)+)? $(= $default)?),*>)? {
+ __superclass: $crate::__fallback_if_not_set! {
+ ($($superclass)?)
+ // For diagnostics / rust-analyzer's sake, we choose a default
+ // superclass so that we can continue compiling, even though
+ // we're going to error if the super class is not set in
+ // `__extern_class_check_super_unsafe` below.
+ ($crate::runtime::NSObject)
+ },
+ // Bind generics (and make them invariant).
+ $(__generics: $crate::__macro_helpers::PhantomData<($(*mut $generic),+)>,)?
+ }
+
+ $crate::__extern_class_impl_traits! {
+ ($($attr_impl)*)
+ (unsafe impl $(<$($generic: $($($bounds)+ +)? $crate::Message),+>)?)
+ ($class $(<$($generic),*>)?)
+ ($($superclass, $($superclasses,)*)? $crate::runtime::AnyObject)
+ }
+
+ $crate::__extern_class_derives! {
+ ($($attr_impl)*)
+ (impl $(<$($generic: $($($bounds)+)?),+>)?)
+ ($class $(<$($generic),*>)?)
+ ($($derives)*)
+ }
+
+ // SAFETY: This maps `SomeClass<T, ...>` to a single `SomeClass<AnyObject, ...>` type and
+ // implements `DowncastTarget` on that type. This is safe because the "base container" class
+ // is the same and each generic argument is replaced with `AnyObject`, which can represent
+ // any Objective-C class instance.
+ $($attr_impl)*
+ unsafe impl $crate::DowncastTarget for $class $(<$($crate::__extern_class_map_anyobject!($generic)),+>)? {}
+
+ $($attr_impl)*
+ unsafe impl $(<$($generic $(: $($bounds)+ + $crate::Message)?),*>)? $crate::ClassType for $class $(<$($generic),*>)? {
+ type Super = $crate::__fallback_if_not_set! {
+ ($($superclass)?)
+ // See __superclass, this is still just for better diagnostics.
+ ($crate::runtime::NSObject)
+ };
+
+ type ThreadKind = $crate::__fallback_if_not_set! {
+ ($(dyn ($($thread_kind)+))?)
+ // Default to the super class' thread kind
+ (<<Self as $crate::ClassType>::Super as $crate::ClassType>::ThreadKind)
+ };
+
+ const NAME: &'static $crate::__macro_helpers::str = $crate::__fallback_if_not_set! {
+ ($($name)*)
+ ($crate::__macro_helpers::stringify!($class))
+ };
+
+ #[inline]
+ fn class() -> &'static $crate::runtime::AnyClass {
+ let _ = <Self as $crate::__macro_helpers::ValidThreadKind<<Self as $crate::ClassType>::ThreadKind>>::check;
+ let _ = <Self as $crate::__macro_helpers::MainThreadOnlyDoesNotImplSendSync<_>>::check;
+ let _ = <Self as $crate::__macro_helpers::DoesNotImplDrop<_>>::check;
+
+ $crate::__class_inner!($crate::__fallback_if_not_set! {
+ ($($name)*)
+ ($crate::__macro_helpers::stringify!($class))
+ }, $crate::__hash_idents!($class))
+ }
+
+ #[inline]
+ fn as_super(&self) -> &Self::Super {
+ &self.__superclass
+ }
+
+ const __INNER: () = ();
+
+ type __SubclassingType = Self;
+ }
+
+ $($attr_impl)*
+ $crate::__extern_class_check_super_unsafe!($($safety $superclass)?);
+
+ $($attr_impl)*
+ $crate::__extern_class_check_no_ivars!($($ivars)*);
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extern_class_check_super_unsafe {
+ (unsafe $($superclass:tt)+) => {};
+ (safe $($superclass:tt)+) => {
+ $crate::__macro_helpers::compile_error!(
+ "#[super(...)] must be wrapped in `unsafe`, as in #[unsafe(super(...))]"
+ );
+ };
+ () => {
+ $crate::__macro_helpers::compile_error!(
+ "must specify the superclass with #[unsafe(super(...))]"
+ );
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extern_class_map_anyobject {
+ ($t:ident) => {
+ $crate::runtime::AnyObject
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extern_class_check_no_ivars {
+ () => {};
+ ($($ivars:tt)*) => {
+ $crate::__macro_helpers::compile_error!("#[ivars] is not supported in extern_class!");
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extern_class_impl_traits {
+ (
+ ($($attr_impl:tt)*)
+ (unsafe impl $($after_impl:tt)*)
+ ($($for:tt)*)
+ ($superclass:path $(, $remaining_superclasses:path)*)
+ ) => {
+ // SAFETY:
+ // - The item is FFI-safe with `#[repr(C)]`.
+ // - The encoding is taken from the inner item, and caller verifies
+ // that it actually inherits said object.
+ // - The rest of the struct's fields are ZSTs, so they don't influence
+ // the layout.
+ //
+ // Be aware that very rarely, this implementation is wrong because the
+ // class' instances do not have the encoding `Encoding::Object`.
+ //
+ // A known case is that `NSAutoreleasePool` has a different encoding.
+ // This should be fairly problem-free though, since that is still
+ // valid in Objective-C to represent that class' instances as
+ // `NSObject*`.
+ $($attr_impl)*
+ unsafe impl $($after_impl)* $crate::RefEncode for $($for)* {
+ const ENCODING_REF: $crate::Encoding
+ = <$superclass as $crate::RefEncode>::ENCODING_REF;
+ }
+
+ // SAFETY: This is a newtype wrapper over `AnyObject` (we even ensure
+ // that `AnyObject` is always last in our inheritance tree), so it is
+ // always safe to reinterpret as that.
+ //
+ // That the object must work with standard memory management is
+ // properly upheld by the fact that the superclass is required by
+ // `ValidThreadKind` to implement `ClassType`, and hence must also be
+ // a subclass of one of `NSObject`, `NSProxy` or some other class that
+ // ensures this (e.g. the object itself is not a root class).
+ $($attr_impl)*
+ unsafe impl $($after_impl)* $crate::Message for $($for)* {}
+
+ // SAFETY: An instance can always be _used_ in exactly the same way as
+ // its superclasses (though not necessarily _constructed_ in the same
+ // way, but `Deref` doesn't allow this).
+ //
+ // Remember; while we (the Rust side) may intentionally be forgetting
+ // which instance we're holding, the Objective-C side will remember,
+ // and will always dispatch to the correct method implementations.
+ //
+ // TODO: If the object has a lifetime, we must keep that lifetime
+ // information, since all objects can be retained using
+ // `Message::retain`, and that could possibly make it unsound to allow
+ // non-`'static` here.
+ //
+ // `&NSMutableArray<T>` -> `&NSArray<T>` -> `Retained<NSArray<T>>` is
+ // fine, but `&UserClass<'a>` -> `&NSObject` -> `Retained<NSObject>`
+ // is not, and hence `&NSArray<UserClass<'a>>` -> `&NSObject` ->
+ // `Retained<NSObject>` isn't either.
+ $($attr_impl)*
+ impl $($after_impl)* $crate::__macro_helpers::Deref for $($for)* {
+ type Target = $superclass;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ &self.__superclass
+ }
+ }
+
+ $($attr_impl)*
+ impl $($after_impl)* $crate::__macro_helpers::AsRef<Self> for $($for)* {
+ #[inline]
+ fn as_ref(&self) -> &Self {
+ self
+ }
+ }
+
+ $crate::__extern_class_impl_as_ref_borrow! {
+ ($superclass $(, $remaining_superclasses)*)
+
+ ($($attr_impl)*)
+ (impl $($after_impl)*)
+ ($($for)*)
+ fn as_ref(&self) {
+ // Triggers Deref coercion depending on return type
+ &*self
+ }
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extern_class_impl_as_ref_borrow {
+ // Base case
+ {
+ ()
+
+ ($($attr_impl:tt)*)
+ (impl $($after_impl:tt)*)
+ ($($for:tt)*)
+ fn as_ref($($self:tt)*) $as_ref:block
+ } => {};
+
+ // For each superclass
+ {
+ ($superclass:path $(, $remaining_superclasses:path)*)
+
+ ($($attr_impl:tt)*)
+ (impl $($after_impl:tt)*)
+ ($($for:tt)*)
+ fn as_ref($($self:tt)*) $as_ref:block
+ } => {
+ $($attr_impl)*
+ impl $($after_impl)* $crate::__macro_helpers::AsRef<$superclass> for $($for)* {
+ #[inline]
+ fn as_ref($($self)*) -> &$superclass $as_ref
+ }
+
+ // Borrow is correct, since subclasses behaves identical to the class
+ // they inherit (message sending doesn't care).
+ //
+ // In particular, `Eq`, `Ord` and `Hash` all give the same results
+ // after borrow.
+
+ $($attr_impl)*
+ impl $($after_impl)* $crate::__macro_helpers::Borrow<$superclass> for $($for)* {
+ #[inline]
+ fn borrow($($self)*) -> &$superclass $as_ref
+ }
+
+ $crate::__extern_class_impl_as_ref_borrow! {
+ ($($remaining_superclasses),*)
+
+ ($($attr_impl)*)
+ (impl $($after_impl)*)
+ ($($for)*)
+ fn as_ref($($self)*) $as_ref
+ }
+ };
+}
+
+/// Note: We intentionally don't add e.g. `T: PartialEq`, as generic objects
+/// are always comparable, hashable and debuggable, regardless of their
+/// generic parameters.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extern_class_derives {
+ // Base case
+ (
+ ($($attr_impl:tt)*)
+ (impl $($after_impl:tt)*)
+ ($($for:tt)*)
+ ($(,)*)
+ ) => {};
+
+ // Debug
+ (
+ ($($attr_impl:tt)*)
+ (impl $($after_impl:tt)*)
+ ($($for:tt)*)
+ (
+ $(,)*
+ Debug
+ $($rest:tt)*
+ )
+ ) => {
+ $($attr_impl)*
+ #[automatically_derived]
+ impl $($after_impl)* $crate::__macro_helpers::fmt::Debug for $($for)* {
+ fn fmt(&self, f: &mut $crate::__macro_helpers::fmt::Formatter<'_>) -> $crate::__macro_helpers::fmt::Result {
+ // Delegate to the superclass
+ $crate::__macro_helpers::fmt::Debug::fmt(&self.__superclass, f)
+ }
+ }
+
+ $crate::__extern_class_derives! {
+ ($($attr_impl)*)
+ (impl $($after_impl)*)
+ ($($for)*)
+ ($($rest)*)
+ }
+ };
+
+ // PartialEq
+ (
+ ($($attr_impl:tt)*)
+ (impl $($after_impl:tt)*)
+ ($($for:tt)*)
+ (
+ $(,)*
+ PartialEq
+ $($rest:tt)*
+ )
+ ) => {
+ $($attr_impl)*
+ #[automatically_derived]
+ impl $($after_impl)* $crate::__macro_helpers::PartialEq for $($for)* {
+ #[inline]
+ fn eq(&self, other: &Self) -> $crate::__macro_helpers::bool {
+ // Delegate to the superclass
+ $crate::__macro_helpers::PartialEq::eq(&self.__superclass, &other.__superclass)
+ }
+ }
+
+ $crate::__extern_class_derives! {
+ ($($attr_impl)*)
+ (impl $($after_impl)*)
+ ($($for)*)
+ ($($rest)*)
+ }
+ };
+
+ // Eq
+ (
+ ($($attr_impl:tt)*)
+ (impl $($after_impl:tt)*)
+ ($($for:tt)*)
+ (
+ $(,)*
+ Eq
+ $($rest:tt)*
+ )
+ ) => {
+ $($attr_impl)*
+ #[automatically_derived]
+ impl $($after_impl)* $crate::__macro_helpers::Eq for $($for)* {}
+
+ $crate::__extern_class_derives! {
+ ($($attr_impl)*)
+ (impl $($after_impl)*)
+ ($($for)*)
+ ($($rest)*)
+ }
+ };
+
+ // Hash
+ (
+ ($($attr_impl:tt)*)
+ (impl $($after_impl:tt)*)
+ ($($for:tt)*)
+ (
+ $(,)*
+ Hash
+ $($rest:tt)*
+ )
+ ) => {
+ $($attr_impl)*
+ #[automatically_derived]
+ impl $($after_impl)* $crate::__macro_helpers::Hash for $($for)* {
+ #[inline]
+ fn hash<H: $crate::__macro_helpers::Hasher>(&self, state: &mut H) {
+ // Delegate to the superclass
+ $crate::__macro_helpers::Hash::hash(&self.__superclass, state)
+ }
+ }
+
+ $crate::__extern_class_derives! {
+ ($($attr_impl)*)
+ (impl $($after_impl)*)
+ ($($for)*)
+ ($($rest)*)
+ }
+ };
+
+ // Unhandled derive
+ (
+ ($($attr_impl:tt)*)
+ (impl $($after_impl:tt)*)
+ ($($for:tt)*)
+ (
+ $(,)*
+ $derive:path
+ $(, $($rest:tt)*)?
+ )
+ ) => {
+ const _: () = {
+ // For better diagnostics.
+ #[derive($derive)]
+ struct Derive;
+ };
+ $crate::__macro_helpers::compile_error!($crate::__macro_helpers::stringify!(
+ #[derive($derive)] is not supported in extern_class!
+ ));
+
+ $crate::__extern_class_derives! {
+ ($($attr_impl)*)
+ (impl $($after_impl)*)
+ ($($for)*)
+ ($($($rest)*)?)
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __select_name {
+ ($_name:ident; $name_const:expr) => {
+ $name_const
+ };
+ ($name:ident;) => {
+ $crate::__macro_helpers::stringify!($name)
+ };
+}
diff --git a/third_party/rust/objc2/src/macros/extern_conformance.rs b/third_party/rust/objc2/src/macros/extern_conformance.rs
@@ -0,0 +1,62 @@
+/// Implement a protocol for an external class.
+///
+/// This creates a given protocol implementation with regards to the
+/// type-system. Useful on types created with [`extern_class!`], not use this
+/// on custom classes created with [`define_class!`], instead, implement the
+/// protocol within that macro.
+///
+/// [`define_class!`]: crate::define_class!
+///
+/// # Examples
+///
+/// ```
+/// # #[cfg(not_available)]
+/// use objc2_foundation::NSObjectProtocol;
+/// # use objc2::runtime::NSObjectProtocol;
+/// use objc2::runtime::NSObject;
+/// use objc2::{extern_class, extern_conformance};
+///
+/// extern_class!(
+/// #[unsafe(super(NSObject))]
+/// #[derive(PartialEq, Eq, Hash, Debug)]
+/// pub struct MyClass;
+/// );
+///
+/// // SAFETY: The class `MyClass` conforms to the `NSObject` protocol (since
+/// // its superclass `NSObject` does).
+/// extern_conformance!(unsafe impl NSObjectProtocol for MyClass {});
+/// ```
+///
+/// See the [`extern_class!`] macro for more examples.
+///
+/// [`extern_class!`]: crate::extern_class!
+#[doc(alias = "@interface")]
+#[macro_export]
+macro_rules! extern_conformance {
+ (unsafe impl $(<$($t:ident : $($bound:ident)? $(?$sized:ident)? $(+ $b:path)*),* $(,)?>)? $ty:ident for $protocol:ty {}) => {
+ unsafe impl $(<$($t : $($bound)? $(?$sized)? $(+ $b)*),*>)? $ty for $protocol {
+ // TODO(breaking): Add private marker here.
+ }
+ };
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::runtime::NSObject;
+ use crate::{extern_class, extern_protocol};
+
+ extern_class!(
+ #[unsafe(super(NSObject))]
+ #[name = "NSObject"]
+ struct OldSyntax;
+ );
+
+ extern_protocol!(
+ #[name = "NSObjectProtocol"]
+ #[allow(clippy::missing_safety_doc)]
+ unsafe trait Protocol {}
+ );
+
+ // Old syntax, must continue to work until next breaking version.
+ unsafe impl Protocol for OldSyntax {}
+}
diff --git a/third_party/rust/objc2/src/macros/extern_methods.rs b/third_party/rust/objc2/src/macros/extern_methods.rs
@@ -0,0 +1,385 @@
+/// Define methods on an external class.
+///
+/// This is a convenience macro to generate associated functions and methods
+/// that delegate to [`msg_send!`].
+///
+/// [`msg_send!`]: crate::msg_send
+///
+///
+/// # Specification
+///
+/// Within the `impl` block you can define two types of functions without
+/// bodies; ["associated functions"] and ["methods"]. These are then mapped to
+/// the Objective-C equivalents "class methods" and "instance methods", and an
+/// appropriate body is created for you. In particular, if you use `self` or
+/// the special name `this` (or `_this`), your method will assumed to be an
+/// instance method, and if you don't it will be assumed to be a class method.
+///
+/// If you specify a function/method with a body, the macro will output it
+/// unchanged.
+///
+/// The name of the function will be used for the resulting function that the
+/// user will use to access the functionality, but is otherwise not used by
+/// the macro.
+///
+/// If you use `objc2::MainThreadMarker` as a parameter type, the macro will
+/// ignore it, allowing you to neatly specify "this method must be run on the
+/// main thread". Note that due to type-system limitations, this is currently
+/// a textual match on `MainThreadMarker`; so you must use that exact
+/// identifier.
+///
+/// ["associated functions"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods
+/// ["methods"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods
+///
+///
+/// ## Attributes
+///
+/// You can add most normal attributes to the methods, including
+/// `#[cfg(...)]`, `#[allow(...)]`, `#[deprecated = ...]` and doc comments.
+///
+/// Exceptions and special attributes are noted below.
+///
+///
+/// ### `#[unsafe(method(...))]` (required)
+///
+/// Specify the desired selector using this attribute.
+///
+/// If the selector ends with "_", as in `#[unsafe(method(my:error:_))]`, the
+/// method is assumed to take an implicit `NSError**` parameter, which is
+/// automatically converted to a [`Result`]. See the error section in
+/// [`msg_send!`] for details.
+///
+///
+/// ### `#[unsafe(method_family = ...)]` (optional)
+///
+/// The Cocoa memory management convention is figured out automatically based
+/// on the name of the selector, but it can be overwritten with this `unsafe`
+/// attribute.
+///
+/// This is commonly done in framework crates to improve compile-time
+/// performance, as the logic to determine the family automatically can be
+/// quite taxing at scale. That said, you should rarely need to use this
+/// yourself.
+///
+/// The valid family names are:
+/// - `alloc`.
+/// - `new`.
+/// - `init`.
+/// - `copy`.
+/// - `mutableCopy`.
+///
+/// As well as the special `none` family that opts-out of being in a family.
+///
+/// This corresponds to the `__attribute__((objc_method_family(family)))` C
+/// attribute, see [Clang's documentation][clang-method-families].
+///
+/// [clang-method-families]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#method-families
+///
+///
+/// #### Safety
+///
+/// You must ensure that the specified method family is correct.
+///
+///
+/// ### `#[cfg_attr(..., ...)]`
+///
+/// This is only supported for attributes that apply to the method itself
+/// (i.e. not supported for attributes that apply to any of the custom
+/// attributes, due to implementation difficulty).
+///
+///
+/// # Safety
+///
+/// You must ensure that any methods you declare with the
+/// `#[unsafe(method(...))]` attribute upholds the safety guarantees described
+/// in the [`msg_send!`] macro, _or_ are marked `unsafe`.
+///
+///
+/// # Examples
+///
+/// Let's create a quick custom class:
+///
+/// ```
+/// use objc2::encode::{Encode, Encoding};
+/// use objc2::ffi::NSUInteger;
+/// use objc2::rc::{Allocated, Retained};
+/// use objc2::runtime::NSObject;
+/// use objc2::{define_class, extern_methods};
+///
+/// // Shim
+/// type NSError = NSObject;
+///
+/// define_class!(
+/// // SAFETY:
+/// // - The superclass NSObject does not have any subclassing requirements.
+/// // - `MyObject` does not implement `Drop`.
+/// #[unsafe(super(NSObject))]
+/// pub struct MyObject;
+///
+/// impl MyObject {
+/// // ... Assume we've implemented all the methods used below
+/// }
+/// );
+///
+/// /// Creation methods.
+/// impl MyObject {
+/// extern_methods!(
+/// // SAFETY: The method is correctly specified.
+/// #[unsafe(method(new))]
+/// pub fn new() -> Retained<Self>;
+///
+/// // SAFETY: The method is correctly specified.
+/// #[unsafe(method(initWithVal:))]
+/// // arbitrary self types are not stable, but we can work around it
+/// // with the special name `this`.
+/// pub fn init(this: Allocated<Self>, val: usize) -> Retained<Self>;
+/// );
+/// }
+///
+/// /// Instance accessor methods.
+/// impl MyObject {
+/// extern_methods!(
+/// // SAFETY: The method is correctly specified.
+/// #[unsafe(method(foo))]
+/// pub fn foo(&self) -> NSUInteger;
+///
+/// // SAFETY: The method is correctly specified.
+/// #[unsafe(method(fooObject))]
+/// pub fn foo_object(&self) -> Retained<NSObject>;
+///
+/// // SAFETY: The method is correctly specified.
+/// #[unsafe(method(withError:_))]
+/// // Since the selector specifies "_", the return type is assumed to
+/// // be `Result`.
+/// pub fn with_error(&self) -> Result<(), Retained<NSError>>;
+/// );
+/// }
+/// ```
+///
+/// The `extern_methods!` then expands to (roughly):
+///
+/// ```
+/// # use objc2::encode::{Encode, Encoding};
+/// # use objc2::ffi::NSUInteger;
+/// # use objc2::rc::{Allocated, Retained};
+/// # use objc2::runtime::NSObject;
+/// # use objc2::{define_class, extern_methods, ClassType};
+/// #
+/// # // Shim
+/// # type NSError = NSObject;
+/// #
+/// # define_class!(
+/// # #[unsafe(super(NSObject))]
+/// # pub struct MyObject;
+/// #
+/// # impl MyObject {
+/// # // ... Assume we've implemented all the methods used below
+/// # }
+/// # );
+/// #
+/// use objc2::msg_send;
+///
+/// /// Creation methods.
+/// impl MyObject {
+/// pub fn new() -> Retained<Self> {
+/// unsafe { msg_send![Self::class(), new] }
+/// }
+///
+/// pub fn init(this: Allocated<Self>, val: usize) -> Retained<Self> {
+/// unsafe { msg_send![this, initWithVal: val] }
+/// }
+/// }
+///
+/// /// Instance accessor methods.
+/// impl MyObject {
+/// pub fn foo(&self) -> NSUInteger {
+/// unsafe { msg_send![self, foo] }
+/// }
+///
+/// pub fn foo_object(&self) -> Retained<NSObject> {
+/// unsafe { msg_send![self, fooObject] }
+/// }
+///
+/// // Since the selector specifies one more argument than we
+/// // have, the return type is assumed to be `Result`.
+/// pub fn with_error(&self) -> Result<(), Retained<NSError>> {
+/// unsafe { msg_send![self, withError: _] }
+/// }
+/// }
+/// ```
+///
+/// See the source code of `objc2-foundation` for many more examples.
+#[macro_export]
+macro_rules! extern_methods {
+ (
+ // Base case of the tt-muncher.
+ ) => {};
+
+ (
+ // Unsafe method.
+ //
+ // Special attributes:
+ // #[unsafe(method($($selector:tt)+))]
+ // #[unsafe(method_family = $family:ident)]
+ $(#[$($m:tt)*])*
+ $v:vis unsafe fn $fn_name:ident($($params:tt)*) $(-> $ret:ty)?
+ // Optionally, a single `where` bound.
+ // TODO: Handle this better.
+ $(where $($where:ty : $bound:path),+ $(,)?)?;
+
+ $($rest:tt)*
+ ) => {
+ $crate::__rewrite_self_param! {
+ ($($params)*)
+
+ ($crate::__extract_method_attributes)
+ ($(#[$($m)*])*)
+
+ ($crate::__extern_methods_method_out)
+ ($v unsafe fn $fn_name($($params)*) $(-> $ret)?)
+ ($($($where : $bound ,)+)?)
+ }
+
+ $crate::extern_methods!($($rest)*);
+ };
+
+ (
+ // Safe method.
+ //
+ // Special attributes:
+ // #[unsafe(method($($selector:tt)+))]
+ // #[unsafe(method_family = $family:ident)]
+ $(#[$($m:tt)*])*
+ $v:vis fn $fn_name:ident($($params:tt)*) $(-> $ret:ty)?
+ // Optionally, a single `where` bound.
+ // TODO: Handle this better.
+ $(where $($where:ty : $bound:path),+ $(,)?)?;
+
+ $($rest:tt)*
+ ) => {
+ $crate::__rewrite_self_param! {
+ ($($params)*)
+
+ ($crate::__extract_method_attributes)
+ ($(#[$($m)*])*)
+
+ ($crate::__extern_methods_method_out)
+ ($v fn $fn_name($($params)*) $(-> $ret)?)
+ ($($($where : $bound ,)+)?)
+ }
+
+ $crate::extern_methods!($($rest)*);
+ };
+
+ (
+ // Deprecated syntax.
+ $(#[$m:meta])*
+ unsafe impl $type:ty {
+ $($methods:tt)*
+ }
+
+ $($rest:tt)*
+ ) => {
+ const _: () = $crate::__macro_helpers::extern_methods_unsafe_impl();
+
+ $(#[$m])*
+ impl<$($t $(: $b $(+ $rest)*)?),*> $type {
+ $crate::extern_methods! {
+ $($methods)*
+ }
+ }
+
+ $crate::extern_methods!($($rest)*);
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extern_methods_method_out {
+ {
+ ($($function_start:tt)*)
+ ($($where:ty : $bound:path ,)*)
+
+ ($__builder_method:ident)
+ ($receiver:expr)
+ ($__receiver_ty:ty)
+ ($($__params_prefix:tt)*)
+ ($($params_rest:tt)*)
+
+ ($method_or_method_id:ident($($sel:tt)*))
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+ } => {
+ $($attr_method)*
+ $($function_start)*
+ where
+ $($where : $bound,)*
+ {
+ $crate::__extern_methods_method_id_deprecated!($method_or_method_id($($sel)*));
+ $crate::__extern_methods_no_optional!($($optional)*);
+
+ #[allow(unused_unsafe)]
+ unsafe {
+ $crate::__method_msg_send! {
+ ($receiver)
+ ($($sel)*)
+ ($($params_rest)*)
+
+ ()
+ ()
+ ($($method_family)*)
+ }
+ }
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extern_methods_no_optional {
+ () => {};
+ (#[optional]) => {
+ $crate::__macro_helpers::compile_error!(
+ "`#[optional]` is only supported in `extern_protocol!`"
+ )
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extern_methods_method_id_deprecated {
+ (method($($sel:tt)*)) => {};
+ (method_id($($sel:tt)*)) => {{
+ #[deprecated = $crate::__macro_helpers::concat!(
+ "using #[unsafe(method_id(",
+ $crate::__macro_helpers::stringify!($($sel)*),
+ "))] inside extern_methods! is deprecated.\nUse #[unsafe(method(",
+ $crate::__macro_helpers::stringify!($($sel)*),
+ "))] instead",
+ )]
+ #[inline]
+ fn method_id() {}
+ method_id();
+ }};
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::extern_methods;
+ use crate::runtime::{NSObject, NSObjectProtocol};
+
+ #[test]
+ fn outside_impl_using_this() {
+ // The fact that this works outside `impl` is an implementation detail
+ // that will get resolved once we have arbitrary self types.
+ extern_methods!(
+ #[unsafe(method(hash))]
+ fn obj_hash(this: &NSObject) -> usize;
+ );
+
+ let obj = NSObject::new();
+ assert_eq!(obj_hash(&obj), obj.hash())
+ }
+}
diff --git a/third_party/rust/objc2/src/macros/extern_protocol.rs b/third_party/rust/objc2/src/macros/extern_protocol.rs
@@ -0,0 +1,452 @@
+/// Create a new trait to represent a protocol.
+///
+/// This is similar to a `@protocol` declaration in Objective-C.
+///
+/// See [Protocols - The Objective-C Programming Language][protocols] and
+/// [Working with Protocols - Programming with Objective-C][working-with] for
+/// general information about protocols in Objective-C.
+///
+/// This macro will create an `unsafe` trait with methods that provide access
+/// to the functionality exposed by the protocol.
+///
+/// Conforming to the protocol can be done in two ways:
+/// - For external classes, use the [`extern_conformance!`] macro.
+/// - For custom classes created with the [`define_class!`] macro, implement
+/// the trait inside the macro.
+///
+/// Objective-C has a smart feature where you can write `id<MyProtocol>`, and
+/// then work with the protocol as-if it was an object; this is very similar
+/// to `dyn` traits in Rust, and we implement it in a similar way, see
+/// [`ProtocolObject`] for details.
+///
+/// [protocols]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProtocols.html
+/// [working-with]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithProtocols/WorkingwithProtocols.html
+/// [`extern_conformance!`]: crate::extern_conformance
+/// [`ProtocolObject`]: crate::runtime::ProtocolObject
+///
+///
+/// # Specification
+///
+/// The syntax is similar enough to Rust syntax that if you invoke the macro
+/// with parentheses (as opposed to curly brackets), `rustfmt` will be able to
+/// format the contents.
+///
+/// This macro creates an `unsafe` trait with the specified methods. A default
+/// implementation of the method is generated based on the selector specified
+/// with `#[unsafe(method(a:selector:))]`. Similar to [`extern_methods!`], you
+/// can use the `#[unsafe(method_family = ...)]` attribute to override the
+/// inferred method family.
+///
+/// Other protocols that this protocol conforms to / inherits can be specified
+/// as supertraits.
+///
+/// The new trait `T` is automatically implemented for
+/// [`ProtocolObject<dyn T>`], which also means that [`ProtocolType`] is
+/// implemented for `dyn T`.
+///
+/// Finally, you can use the `#[optional]` attribute to mark optional methods.
+/// This currently doesn't have any effect, but probably will have one in the
+/// future when implementing protocols in [`define_class!`].
+///
+/// This macro otherwise shares similarities with [`extern_class!`] and
+/// [`extern_methods!`].
+///
+/// [`ProtocolObject<dyn T>`]: crate::runtime::ProtocolObject
+/// [`ProtocolType`]: crate::ProtocolType
+/// [`define_class!`]: crate::define_class
+/// [`extern_class!`]: crate::extern_class
+/// [`extern_methods!`]: crate::extern_methods
+///
+///
+/// # Safety
+///
+/// The following are required for using the macro itself:
+/// - The specified name must be an existing Objective-C protocol.
+/// - The protocol must actually inherit/conform to the protocols specified
+/// as supertraits.
+///
+/// Each method is annotated with `#[unsafe(method(...))]`, where you are
+/// responsible for ensuring that the declaration is correct.
+///
+/// While the following are required when implementing the `unsafe` trait for
+/// a new type:
+/// - The type must represent an object that implements the protocol.
+///
+///
+/// # Examples
+///
+/// Create a trait to represent the `NSItemProviderWriting` protocol (in
+/// practice, you would import this from `objc2-foundation`, this is just for
+/// demonstration purposes).
+///
+/// ```
+/// use std::ffi::c_void;
+/// use objc2::ffi::NSInteger;
+/// use objc2::rc::Retained;
+/// use objc2::runtime::{NSObject, NSObjectProtocol};
+/// use objc2::extern_protocol;
+/// # type NSArray<T> = T;
+/// # type NSString = NSObject;
+/// # type NSProgress = NSObject;
+/// # type NSItemProviderRepresentationVisibility = NSInteger;
+/// # #[cfg(defined_in_foundation)]
+/// use objc2_foundation::{NSArray, NSString, NSProgress, NSItemProviderRepresentationVisibility};
+///
+/// extern_protocol!(
+/// /// This comment will appear on the trait as expected.
+/// //
+/// // We could have named the trait something else on the Rust-side, and
+/// // then used this to keep it correct from Objective-C.
+/// // #[name = "NSItemProviderWriting"]
+/// //
+/// // SAFETY:
+/// // - The name is correct.
+/// // - The protocol does inherit from `NSObjectProtocol`.
+/// pub unsafe trait NSItemProviderWriting: NSObjectProtocol {
+/// // ^^^^^^^^^^^^^^^^
+/// // This protocol inherits from the `NSObject` protocol
+///
+/// // This method we mark as `unsafe`, since we aren't using the
+/// // correct type for the completion handler.
+/// #[unsafe(method(loadDataWithTypeIdentifier:forItemProviderCompletionHandler:))]
+/// unsafe fn loadData(
+/// &self,
+/// type_identifier: &NSString,
+/// completion_handler: *mut c_void,
+/// ) -> Option<Retained<NSProgress>>;
+///
+/// // SAFETY: The method is correctly specified.
+/// #[unsafe(method(writableTypeIdentifiersForItemProvider))]
+/// fn writableTypeIdentifiersForItemProvider_class()
+/// -> Retained<NSArray<NSString>>;
+///
+/// // The rest of these are optional, which means that a user of
+/// // `define_class!` would not need to implement them.
+///
+/// // SAFETY: The method is correctly specified.
+/// #[unsafe(method(writableTypeIdentifiersForItemProvider))]
+/// #[optional]
+/// fn writableTypeIdentifiersForItemProvider(&self)
+/// -> Retained<NSArray<NSString>>;
+///
+/// // SAFETY: The method is correctly specified.
+/// #[unsafe(method(itemProviderVisibilityForRepresentationWithTypeIdentifier:))]
+/// #[optional]
+/// fn itemProviderVisibilityForRepresentation_class(
+/// type_identifier: &NSString,
+/// ) -> NSItemProviderRepresentationVisibility;
+///
+/// // SAFETY: The method is correctly specified.
+/// #[unsafe(method(itemProviderVisibilityForRepresentationWithTypeIdentifier:))]
+/// #[optional]
+/// fn itemProviderVisibilityForRepresentation(
+/// &self,
+/// type_identifier: &NSString,
+/// ) -> NSItemProviderRepresentationVisibility;
+/// }
+/// );
+///
+/// // Types can now implement `NSItemProviderWriting`, and use the methods
+/// // from it as we specified.
+/// ```
+///
+/// See the source code of `objc2-foundation` for many more examples.
+#[doc(alias = "@protocol")]
+#[macro_export]
+macro_rules! extern_protocol {
+ (
+ // The special #[name = $name:literal] attribute is supported here.
+ $(#[$($attrs:tt)*])*
+ $v:vis unsafe trait $protocol:ident $(: $conforms_to:ident $(+ $conforms_to_rest:ident)*)? {
+ $($methods:tt)*
+ }
+ ) => {
+ $crate::__extract_struct_attributes! {
+ ($(#[$($attrs)*])*)
+
+ ($crate::__inner_extern_protocol)
+ ($protocol)
+ ($v unsafe trait $protocol $(: $conforms_to $(+ $conforms_to_rest)*)? {
+ $crate::__extern_protocol_rewrite_methods! {
+ $($methods)*
+ }
+ })
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __inner_extern_protocol {
+ (
+ ($protocol:ident)
+ ($protocol_definition:item)
+
+ ($($superclasses:tt)*)
+ ($($thread_kind:tt)*)
+ ($($name:tt)*)
+ ($($ivars:tt)*)
+ ($($derives:tt)*)
+ ($($attr_protocol:tt)*)
+ ($($attr_impl:tt)*)
+ ) => {
+ $($attr_protocol)*
+ $protocol_definition
+
+ $($attr_impl)*
+ unsafe impl<T> $protocol for $crate::runtime::ProtocolObject<T>
+ where
+ T: ?$crate::__macro_helpers::Sized + $protocol
+ {}
+
+ // SAFETY: The specified name is ensured by caller to be a protocol,
+ // and is correctly defined.
+ $($attr_impl)*
+ unsafe impl $crate::ProtocolType for dyn $protocol {
+ const NAME: &'static $crate::__macro_helpers::str = $crate::__fallback_if_not_set! {
+ ($($name)*)
+ ($crate::__macro_helpers::stringify!($protocol))
+ };
+ const __INNER: () = ();
+ }
+
+ // SAFETY: Anything that implements the protocol is valid to convert
+ // to `ProtocolObject<dyn [PROTO]>`.
+ $($attr_impl)*
+ unsafe impl<T> $crate::runtime::ImplementedBy<T> for dyn $protocol
+ where
+ T: ?$crate::__macro_helpers::Sized + $crate::Message + $protocol
+ {
+ const __INNER: () = ();
+ }
+
+ // TODO: Should we also implement `ImplementedBy` for `Send + Sync`
+ // types, as is done for `NSObjectProtocol`?
+
+ $crate::__extern_protocol_check_no_super!($($superclasses)*);
+
+ $crate::__extern_protocol_check_no_thread_kind!($($thread_kind)*);
+
+ $crate::__extern_protocol_check_no_ivars!($($ivars)*);
+
+ $crate::__extern_protocol_check_no_derives!($($derives)*);
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extern_protocol_check_no_super {
+ () => {};
+ ($($ivars:tt)*) => {
+ $crate::__macro_helpers::compile_error!("#[super] is not supported in extern_protocol!");
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extern_protocol_check_no_thread_kind {
+ () => {};
+ ($($ivars:tt)*) => {
+ $crate::__macro_helpers::compile_error!(
+ "#[thread_kind = ...] is not supported in extern_protocol!. Add MainThreadOnly or AnyThread bound instead"
+ );
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extern_protocol_check_no_ivars {
+ () => {};
+ ($($ivars:tt)*) => {
+ $crate::__macro_helpers::compile_error!("#[ivars] is not supported in extern_protocol!");
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extern_protocol_check_no_derives {
+ () => {};
+ ($($ivars:tt)*) => {
+ $crate::__macro_helpers::compile_error!(
+ "#[derive(...)] is not supported in extern_protocol!"
+ );
+ };
+}
+
+/// tt-munch each protocol method.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extern_protocol_rewrite_methods {
+ // Base case
+ {} => {};
+
+ // Unsafe variant
+ {
+ $(#[$($m:tt)*])*
+ $v:vis unsafe fn $name:ident($($params:tt)*) $(-> $ret:ty)?
+ // TODO: Handle where bounds better
+ $(where $($where:ty : $bound:path),+ $(,)?)?;
+
+ $($rest:tt)*
+ } => {
+ $crate::__rewrite_self_param! {
+ ($($params)*)
+
+ ($crate::__extract_method_attributes)
+ ($(#[$($m)*])*)
+
+ ($crate::__extern_protocol_method_out)
+ ($v unsafe fn $name($($params)*) $(-> $ret)?)
+ ($($($where : $bound ,)+)?)
+ }
+
+ $crate::__extern_protocol_rewrite_methods! {
+ $($rest)*
+ }
+ };
+
+ // Safe variant
+ {
+ $(#[$($m:tt)*])*
+ $v:vis fn $name:ident($($params:tt)*) $(-> $ret:ty)?
+ // TODO: Handle where bounds better
+ $(where $($where:ty : $bound:path),+ $(,)?)?;
+
+ $($rest:tt)*
+ } => {
+ $crate::__rewrite_self_param! {
+ ($($params)*)
+
+ ($crate::__extract_method_attributes)
+ ($(#[$($m)*])*)
+
+ ($crate::__extern_protocol_method_out)
+ ($v fn $name($($params)*) $(-> $ret)?)
+ ($($($where : $bound ,)+)?)
+ }
+
+ $crate::__extern_protocol_rewrite_methods! {
+ $($rest)*
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extern_protocol_method_out {
+ // Instance method
+ {
+ ($($function_start:tt)*)
+ ($($where:ty : $bound:path ,)*)
+
+ (add_method)
+ ($receiver:expr)
+ ($__receiver_ty:ty)
+ ($($__params_prefix:tt)*)
+ ($($params_rest:tt)*)
+
+ ($method_or_method_id:ident($($sel:tt)*))
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+ } => {
+ $($attr_method)*
+ $($function_start)*
+ where
+ Self: $crate::__macro_helpers::Sized + $crate::Message
+ $(, $where : $bound)*
+ {
+ $crate::__extern_methods_method_id_deprecated!($method_or_method_id($($sel)*));
+
+ #[allow(unused_unsafe)]
+ unsafe {
+ $crate::__method_msg_send! {
+ ($receiver)
+ ($($sel)*)
+ ($($params_rest)*)
+
+ ()
+ ()
+ ($($method_family)*)
+ }
+ }
+ }
+ };
+
+ // Class method
+ {
+ ($($function_start:tt)*)
+ ($($where:ty : $bound:path ,)*)
+
+ (add_class_method)
+ ($receiver:expr)
+ ($__receiver_ty:ty)
+ ($($__params_prefix:tt)*)
+ ($($params_rest:tt)*)
+
+ ($method_or_method_id:ident($($sel:tt)*))
+ ($($method_family:tt)*)
+ ($($optional:tt)*)
+ ($($attr_method:tt)*)
+ ($($attr_use:tt)*)
+ } => {
+ $($attr_method)*
+ $($function_start)*
+ where
+ Self: $crate::__macro_helpers::Sized + $crate::ClassType
+ $(, $where : $bound)*
+ {
+ $crate::__extern_methods_method_id_deprecated!($method_or_method_id($($sel)*));
+
+ #[allow(unused_unsafe)]
+ unsafe {
+ $crate::__method_msg_send! {
+ ($receiver)
+ ($($sel)*)
+ ($($params_rest)*)
+
+ ()
+ ()
+ ($($method_family)*)
+ }
+ }
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __extern_protocol_method_id_deprecated {
+ (method($($sel:tt)*)) => {};
+ (method_id($($sel:tt)*)) => {{
+ #[deprecated = $crate::__macro_helpers::concat!(
+ "using #[unsafe(method_id(",
+ $crate::__macro_helpers::stringify!($($sel)*),
+ "))] inside extern_protocol! is deprecated.\nUse #[unsafe(method(",
+ $crate::__macro_helpers::stringify!($($sel)*),
+ "))] instead",
+ )]
+ #[inline]
+ fn method_id() {}
+ method_id();
+ }};
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::{extern_protocol, ProtocolType};
+
+ #[test]
+ fn explicit_name() {
+ extern_protocol!(
+ #[allow(clippy::missing_safety_doc)]
+ #[name = "NSObject"]
+ unsafe trait Foo {}
+ );
+
+ let proto = <dyn Foo>::protocol().unwrap();
+ assert_eq!(proto.name().to_str().unwrap(), "NSObject");
+ assert_eq!(<dyn Foo>::NAME, "NSObject");
+ }
+}
diff --git a/third_party/rust/objc2/src/macros/mod.rs b/third_party/rust/objc2/src/macros/mod.rs
@@ -0,0 +1,1290 @@
+mod __attribute_helpers;
+mod __fallback;
+mod __method_msg_send;
+mod __msg_send_parse;
+mod __rewrite_self_param;
+mod available;
+mod cf_objc2_type;
+mod define_class;
+mod extern_class;
+mod extern_conformance;
+mod extern_methods;
+mod extern_protocol;
+
+/// Gets a reference to an [`AnyClass`] from the given name.
+///
+/// If you have an object that implements [`ClassType`], consider using the
+/// [`ClassType::class`] method instead.
+///
+/// [`AnyClass`]: crate::runtime::AnyClass
+/// [`ClassType`]: crate::ClassType
+/// [`ClassType::class`]: crate::ClassType::class
+///
+///
+/// # Panics
+///
+/// Panics if no class with the given name can be found.
+///
+/// To dynamically check for a class that may not exist, use [`AnyClass::get`].
+///
+/// [`AnyClass::get`]: crate::runtime::AnyClass::get
+///
+///
+/// # Features
+///
+/// If the experimental `"unstable-static-class"` feature is enabled, this
+/// will emit special statics that will be replaced by dyld when the program
+/// starts up.
+///
+/// Errors that were previously runtime panics may now turn into linker errors
+/// if you try to use a class which is not available. Additionally, you may
+/// have to call `msg_send![cls, class]` on the result if you want to use it
+/// in a dynamic context (e.g. when dynamically creating classes).
+///
+/// See the [corresponding section][sel#features] in the [`sel!`] macro for
+/// more details on the limitations of this. The
+/// `"unstable-static-class-inlined"` corresponds to the
+/// `"unstable-static-sel-inlined"` feature here.
+///
+/// [sel#features]: crate::sel#features
+/// [`sel!`]: crate::sel
+///
+///
+/// # Examples
+///
+/// Get and compare the class with one returned from [`ClassType::class`].
+///
+/// ```
+/// use objc2::runtime::NSObject;
+/// use objc2::{class, ClassType};
+///
+/// let cls1 = class!(NSObject);
+/// let cls2 = NSObject::class();
+/// assert_eq!(cls1, cls2);
+/// ```
+///
+/// Try to get a non-existing class (this will panic, or fail to link).
+///
+#[cfg_attr(not(feature = "unstable-static-class"), doc = "```should_panic")]
+#[cfg_attr(feature = "unstable-static-class", doc = "```ignore")]
+/// use objc2::class;
+///
+/// let _ = class!(NonExistentClass);
+/// ```
+#[macro_export]
+macro_rules! class {
+ ($name:ident) => {{
+ $crate::__class_inner!(
+ $crate::__macro_helpers::stringify!($name),
+ $crate::__hash_idents!($name)
+ )
+ }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+#[cfg(not(feature = "unstable-static-class"))]
+macro_rules! __class_inner {
+ ($name:expr, $_hash:expr) => {{
+ static CACHED_CLASS: $crate::__macro_helpers::CachedClass =
+ $crate::__macro_helpers::CachedClass::new();
+ #[allow(unused_unsafe)]
+ unsafe {
+ CACHED_CLASS.get($crate::__macro_helpers::concat!($name, '\0'))
+ }
+ }};
+}
+
+/// Register a selector with the Objective-C runtime.
+///
+/// Returns the [`Sel`] corresponding to the specified selector.
+///
+/// [`Sel`]: crate::runtime::Sel
+///
+///
+/// # Panics
+///
+/// Panics if the runtime failed allocating space for the selector.
+///
+///
+/// # Specification
+///
+/// This has similar syntax and functionality as the `@selector` directive in
+/// Objective-C.
+///
+/// This calls [`Sel::register`] internally. The result is cached for
+/// efficiency. The cache for certain common selectors (`alloc`, `init` and
+/// `new`) is deduplicated to reduce code-size.
+///
+/// Non-ascii identifiers are ill-tested, if supported at all.
+///
+/// [`Sel::register`]: crate::runtime::Sel::register
+///
+///
+/// # Features
+///
+/// If the experimental `"unstable-static-sel"` feature is enabled, this will
+/// emit special statics that will be replaced by the dynamic linker (dyld)
+/// when the program starts up - in exactly the same manner as normal
+/// Objective-C code does.
+/// This should be significantly faster (and allow better native debugging),
+/// however due to the Rust compilation model, and since we don't have
+/// low-level control over it, it is currently unlikely that this will work
+/// correctly in all cases.
+/// See the source code and [rust-lang/rust#53929] for more info.
+///
+/// Concretely, this may fail at:
+/// - link-time (likely)
+/// - dynamic link-time/just before the program is run (fairly likely)
+/// - runtime, causing UB (unlikely)
+///
+/// The `"unstable-static-sel-inlined"` feature is the even more extreme
+/// version - it yields the best performance and is closest to real
+/// Objective-C code, but probably won't work unless your code and its
+/// inlining is written in a very certain way.
+///
+/// Enabling LTO greatly increases the chance that these features work.
+///
+/// [rust-lang/rust#53929]: https://github.com/rust-lang/rust/issues/53929
+///
+///
+/// # Examples
+///
+/// Get a few different selectors:
+///
+/// ```rust
+/// use objc2::sel;
+/// let sel = sel!(alloc);
+/// let sel = sel!(description);
+/// let sel = sel!(_privateMethod);
+/// let sel = sel!(storyboardWithName:bundle:);
+/// let sel = sel!(
+/// otherEventWithType:
+/// location:
+/// modifierFlags:
+/// timestamp:
+/// windowNumber:
+/// context:
+/// subtype:
+/// data1:
+/// data2:
+/// );
+/// ```
+///
+/// Whitespace is ignored:
+///
+/// ```
+/// # use objc2::sel;
+/// let sel1 = sel!(setObject:forKey:);
+/// let sel2 = sel!( setObject :
+///
+/// forKey : );
+/// assert_eq!(sel1, sel2);
+/// ```
+///
+/// Invalid selector:
+///
+/// ```compile_fail
+/// # use objc2::sel;
+/// let sel = sel!(aSelector:withoutTrailingColon);
+/// ```
+///
+/// A selector with internal colons:
+///
+/// ```
+/// # use objc2::sel;
+/// let sel = sel!(sel::with:::multiple:internal::::colons:::);
+///
+/// // Yes, that is possible! The following Objective-C would work:
+/// //
+/// // @interface MyThing: NSObject
+/// // + (void)test:(int)a :(int)b arg:(int)c :(int)d;
+/// // @end
+/// ```
+///
+/// Unsupported usage that you may run into when using macros - fails to
+/// compile when the `"unstable-static-sel"` feature is enabled.
+///
+/// Instead, define a wrapper function that retrieves the selector.
+///
+#[cfg_attr(not(feature = "unstable-static-sel"), doc = "```no_run")]
+#[cfg_attr(feature = "unstable-static-sel", doc = "```compile_fail")]
+/// use objc2::sel;
+/// macro_rules! x {
+/// ($x:ident) => {
+/// // One of these is fine
+/// sel!($x);
+/// // But using the identifier again in the same way is not!
+/// sel!($x);
+/// };
+/// }
+/// // Identifier `abc`
+/// x!(abc);
+/// ```
+#[macro_export]
+macro_rules! sel {
+ (new) => ({
+ $crate::__macro_helpers::new_sel()
+ });
+ (init) => ({
+ $crate::__macro_helpers::init_sel()
+ });
+ (alloc) => ({
+ $crate::__macro_helpers::alloc_sel()
+ });
+ (dealloc) => ({
+ $crate::__macro_helpers::dealloc_sel()
+ });
+ ($sel:ident) => ({
+ $crate::__sel_inner!(
+ $crate::__sel_data!($sel),
+ $crate::__hash_idents!($sel)
+ )
+ });
+ ($($sel:ident :)*) => ({
+ $crate::__sel_inner!(
+ $crate::__sel_data!($($sel :)*),
+ $crate::__hash_idents!($($sel :)*)
+ )
+ });
+ ($($sel:tt)*) => {
+ $crate::__sel_inner!(
+ $crate::__sel_helper! {
+ ()
+ $($sel)*
+ },
+ $crate::__hash_idents!($($sel)*)
+ )
+ };
+}
+
+/// Handle selectors with internal colons.
+///
+/// Required since `::` is a different token than `:`.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __sel_helper {
+ // Base-case
+ {
+ ($($parsed_sel:tt)*)
+ } => ({
+ $crate::__sel_data!($($parsed_sel)*)
+ });
+ // Single identifier
+ {
+ ()
+ $ident:ident
+ } => {
+ $crate::__sel_helper! {
+ ($ident)
+ }
+ };
+ // Parse identitifer + colon token
+ {
+ ($($parsed_sel:tt)*)
+ $($ident:ident)? : $($rest:tt)*
+ } => {
+ $crate::__sel_helper! {
+ ($($parsed_sel)* $($ident)? :)
+ $($rest)*
+ }
+ };
+ // Parse identitifer + path separator token
+ {
+ ($($parsed_sel:tt)*)
+ $($ident:ident)? :: $($rest:tt)*
+ } => {
+ $crate::__sel_helper! {
+ // Notice space between these
+ ($($parsed_sel)* $($ident)? : :)
+ $($rest)*
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __sel_data {
+ ($first:ident $(: $($($rest:ident)? :)*)?) => {
+ $crate::__macro_helpers::concat!(
+ $crate::__macro_helpers::stringify!($first),
+ $(':', $($($crate::__macro_helpers::stringify!($rest),)? ':',)*)?
+ '\0',
+ )
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+#[cfg(not(feature = "unstable-static-sel"))]
+macro_rules! __sel_inner {
+ ($data:expr, $_hash:expr) => {{
+ static CACHED_SEL: $crate::__macro_helpers::CachedSel =
+ $crate::__macro_helpers::CachedSel::new();
+ #[allow(unused_unsafe)]
+ unsafe {
+ CACHED_SEL.get($data)
+ }
+ }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __statics_string_to_known_length_bytes {
+ ($inp:ident) => {{
+ // Convert the `&[u8]` slice to an array with known length, so
+ // that we can place that directly in a static.
+ let mut res: [$crate::__macro_helpers::u8; $inp.len()] = [0; $inp.len()];
+ let mut i = 0;
+ while i < $inp.len() {
+ res[i] = $inp[i];
+ i += 1;
+ }
+ res
+ }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+#[cfg(target_vendor = "apple")]
+macro_rules! __statics_image_info {
+ ($hash:expr) => {
+ /// We always emit the image info tag, since we need it to:
+ /// - End up in the same codegen unit as the other statics below.
+ /// - End up in the final binary so it can be read by dyld.
+ ///
+ /// If it's not present in the codegen unit, then `ld64` won't set
+ /// `hasObjC` for that specific object file, and in turn it might
+ /// disable processing of the special Objective-C sections (currently
+ /// a category merging pass, in the future who knows what).
+ ///
+ /// Unfortunately however, this leads to duplicated tags - the linker
+ /// reports `__DATA/__objc_imageinfo has unexpectedly large size XXX`,
+ /// but things still seems to work.
+ #[cfg_attr(
+ not(all(target_os = "macos", target_arch = "x86")),
+ link_section = "__DATA,__objc_imageinfo,regular,no_dead_strip"
+ )]
+ #[cfg_attr(
+ all(target_os = "macos", target_arch = "x86"),
+ link_section = "__OBJC,__image_info,regular"
+ )]
+ #[export_name = $crate::__macro_helpers::concat!("\x01L_OBJC_IMAGE_INFO_", $hash)]
+ #[used] // Make sure this reaches the linker
+ static _IMAGE_INFO: $crate::__macro_helpers::ImageInfo =
+ $crate::__macro_helpers::ImageInfo::system();
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+#[cfg(target_vendor = "apple")]
+macro_rules! __statics_module_info {
+ ($hash:expr) => {
+ #[link_section = "__TEXT,__cstring,cstring_literals"]
+ #[export_name = $crate::__macro_helpers::concat!("\x01L_OBJC_CLASS_NAME_", $hash, "_MODULE_INFO")]
+ static MODULE_INFO_NAME: [$crate::__macro_helpers::u8; 1] = [0];
+
+ /// Emit module info.
+ ///
+ /// This is similar to image info, and must be present in the final
+ /// binary on macOS 32-bit.
+ #[link_section = "__OBJC,__module_info,regular,no_dead_strip"]
+ #[export_name = $crate::__macro_helpers::concat!("\x01L_OBJC_MODULES_", $hash)]
+ #[used] // Make sure this reaches the linker
+ static _MODULE_INFO: $crate::__macro_helpers::ModuleInfo =
+ $crate::__macro_helpers::ModuleInfo::new(MODULE_INFO_NAME.as_ptr());
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+#[cfg(target_vendor = "apple")]
+macro_rules! __statics_sel {
+ {
+ ($data:expr)
+ ($hash:expr)
+ } => {
+ const X: &[$crate::__macro_helpers::u8] = $data.as_bytes();
+
+ /// Clang marks this with LLVM's `unnamed_addr`.
+ /// See rust-lang/rust#18297
+ /// Should only be an optimization (?)
+ #[cfg_attr(
+ not(all(target_os = "macos", target_arch = "x86")),
+ link_section = "__TEXT,__objc_methname,cstring_literals",
+ )]
+ #[cfg_attr(
+ all(target_os = "macos", target_arch = "x86"),
+ link_section = "__TEXT,__cstring,cstring_literals",
+ )]
+ #[export_name = $crate::__macro_helpers::concat!(
+ "\x01L_OBJC_METH_VAR_NAME_",
+ $hash,
+ )]
+ static NAME_DATA: [$crate::__macro_helpers::u8; X.len()] = $crate::__statics_string_to_known_length_bytes!(X);
+
+ /// Place the constant value in the correct section.
+ ///
+ /// We use `UnsafeCell` because this somewhat resembles internal
+ /// mutation - this pointer will be changed by dyld at startup, so we
+ /// _must_ prevent Rust/LLVM from trying to "peek inside" it and just
+ /// use a pointer to `NAME_DATA` directly.
+ ///
+ /// Clang does this by marking `REF` with LLVM's
+ /// `externally_initialized`.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// I'm quite uncertain of how safe this is, since the Rust abstract
+ /// machine has no concept of a static that is initialized outside of
+ /// it - perhaps it would be better to use `read_volatile` instead of
+ /// relying on `UnsafeCell`? Or perhaps `MaybeUninit` would help?
+ ///
+ /// See the [`ctor`](https://docs.rs/ctor) crate for more info on
+ /// "life before main".
+ #[cfg_attr(
+ not(all(target_os = "macos", target_arch = "x86")),
+ // Clang uses `no_dead_strip` in the link section for some unknown reason,
+ // but it makes LTO fail to trim the unused symbols.
+ // https://github.com/madsmtm/objc2/issues/667
+ // https://github.com/llvm/llvm-project/issues/114111
+ link_section = "__DATA,__objc_selrefs,literal_pointers",
+ )]
+ #[cfg_attr(
+ all(target_os = "macos", target_arch = "x86"),
+ link_section = "__OBJC,__message_refs,literal_pointers",
+ )]
+ #[export_name = $crate::__macro_helpers::concat!("\x01L_OBJC_SELECTOR_REFERENCES_", $hash)]
+ static REF: $crate::__macro_helpers::SyncUnsafeCell<$crate::runtime::Sel> = unsafe {
+ $crate::__macro_helpers::SyncUnsafeCell::new($crate::runtime::Sel::__internal_from_ptr(NAME_DATA.as_ptr()))
+ };
+
+ $crate::__statics_image_info!($hash);
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+#[cfg(not(target_vendor = "apple"))]
+macro_rules! __statics_sel {
+ ($($args:tt)*) => {
+ // TODO
+ $crate::__macro_helpers::compile_error!(
+ "The `\"unstable-static-sel\"` feature is not yet supported on GNUStep!"
+ )
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+#[cfg(all(
+ target_vendor = "apple",
+ not(all(target_os = "macos", target_arch = "x86"))
+))]
+macro_rules! __statics_class {
+ {
+ ($name:expr)
+ ($hash:expr)
+ } => {
+ extern "C" {
+ /// Link to the Objective-C class static.
+ ///
+ /// This uses the special symbol that static and dynamic linkers
+ /// knows about.
+ ///
+ /// Failure modes:
+ /// - Unknown class: Static linker error.
+ /// - OS version < Class introduced version: Dynamic linker error
+ /// on program startup.
+ /// - Deployment target > Class introduced version: No error,
+ /// though _should_ be a static linker error.
+ ///
+ /// Ideally, we'd have some way of allowing this to be weakly
+ /// linked, and return `Option<&AnyClass>` in that case, but Rust
+ /// doesn't have the capability to do so yet!
+ /// <https://github.com/rust-lang/rust/issues/29603>
+ /// <https://stackoverflow.com/a/16936512>
+ /// <http://sealiesoftware.com/blog/archive/2010/4/8/Do-it-yourself_Objective-C_weak_import.html>
+ #[link_name = $crate::__macro_helpers::concat!("OBJC_CLASS_$_", $name)]
+ static CLASS: $crate::runtime::AnyClass;
+ }
+
+ /// SAFETY: Same as `REF` above in `__statics_sel!`.
+ #[link_section = "__DATA,__objc_classrefs,regular"]
+ #[export_name = $crate::__macro_helpers::concat!(
+ "\x01L_OBJC_CLASSLIST_REFERENCES_$_",
+ $hash,
+ )]
+ static REF: $crate::__macro_helpers::SyncUnsafeCell<&$crate::runtime::AnyClass> = unsafe {
+ $crate::__macro_helpers::SyncUnsafeCell::new(&CLASS)
+ };
+
+ $crate::__statics_image_info!($hash);
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+#[cfg(all(target_vendor = "apple", all(target_os = "macos", target_arch = "x86")))]
+macro_rules! __statics_class {
+ {
+ ($name:expr)
+ ($hash:expr)
+ } => {
+ const X: &[$crate::__macro_helpers::u8] = $name.as_bytes();
+
+ /// Similar to NAME_DATA above in `__statics_sel!`.
+ #[link_section = "__TEXT,__cstring,cstring_literals"]
+ #[export_name = $crate::__macro_helpers::concat!(
+ "\x01L_OBJC_CLASS_NAME_",
+ $hash,
+ )]
+ static NAME_DATA: [$crate::__macro_helpers::u8; X.len()] = $crate::__statics_string_to_known_length_bytes!(X);
+
+ /// SAFETY: Same as `REF` above in `__statics_sel!`.
+ #[link_section = "__OBJC,__cls_refs,literal_pointers"]
+ #[export_name = $crate::__macro_helpers::concat!(
+ "\x01L_OBJC_CLASS_REFERENCES_",
+ $hash,
+ )]
+ static REF: $crate::__macro_helpers::SyncUnsafeCell<&$crate::runtime::AnyClass> = unsafe {
+ let ptr: *const $crate::runtime::AnyClass = NAME_DATA.as_ptr().cast();
+ $crate::__macro_helpers::SyncUnsafeCell::new(&*ptr)
+ };
+
+ $crate::__statics_image_info!($hash);
+ $crate::__statics_module_info!($hash);
+ }
+}
+
+#[doc(hidden)]
+#[macro_export]
+#[cfg(all(
+ feature = "unstable-static-sel",
+ not(feature = "unstable-static-sel-inlined")
+))]
+macro_rules! __sel_inner {
+ ($data:expr, $hash:expr) => {{
+ $crate::__statics_sel! {
+ ($data)
+ ($hash)
+ }
+
+ /// HACK: Wrap the access in a non-generic, `#[inline(never)]`
+ /// function to make the compiler group it into the same codegen unit
+ /// as the statics.
+ ///
+ /// See the following link for details on how the compiler decides
+ /// to partition code into codegen units:
+ /// <https://doc.rust-lang.org/1.61.0/nightly-rustc/rustc_monomorphize/partitioning/index.html>
+ #[inline(never)]
+ fn objc_static_workaround() -> $crate::runtime::Sel {
+ // SAFETY: The actual selector is replaced by dyld when the
+ // program is loaded.
+ //
+ // This is similar to a volatile read, except it can be stripped
+ // if unused.
+ unsafe { *REF.get() }
+ }
+
+ objc_static_workaround()
+ }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+#[cfg(feature = "unstable-static-sel-inlined")]
+macro_rules! __sel_inner {
+ ($data:expr, $hash:expr) => {{
+ $crate::__statics_sel! {
+ ($data)
+ ($hash)
+ }
+
+ #[allow(unused_unsafe)]
+ // SAFETY: See above
+ unsafe {
+ *REF.get()
+ }
+ }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+#[cfg(all(
+ feature = "unstable-static-class",
+ not(feature = "gnustep-1-7"),
+ not(feature = "unstable-static-class-inlined")
+))]
+macro_rules! __class_inner {
+ ($name:expr, $hash:expr) => {{
+ $crate::__statics_class! {
+ ($name)
+ ($hash)
+ }
+
+ #[inline(never)]
+ fn objc_static_workaround() -> &'static $crate::runtime::AnyClass {
+ // SAFETY: Same as __sel_inner
+ unsafe { *REF.get() }
+ }
+
+ objc_static_workaround()
+ }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+#[cfg(all(
+ feature = "unstable-static-class",
+ not(feature = "gnustep-1-7"),
+ feature = "unstable-static-class-inlined"
+))]
+macro_rules! __class_inner {
+ ($name:expr, $hash:expr) => {{
+ $crate::__statics_class! {
+ ($name)
+ ($hash)
+ }
+
+ #[allow(unused_unsafe)]
+ // SAFETY: See above
+ unsafe {
+ *REF.get()
+ }
+ }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+// The linking changed in libobjc2 v2.0
+#[cfg(all(feature = "gnustep-1-7", not(feature = "gnustep-2-0"),))]
+macro_rules! __class_static_name {
+ ($name:expr) => {
+ $crate::__macro_helpers::concat!("_OBJC_CLASS_", $name)
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+#[cfg(feature = "gnustep-2-0")]
+macro_rules! __class_static_name {
+ ($name:expr) => {
+ $crate::__macro_helpers::concat!("._OBJC_CLASS_", $name)
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+#[cfg(all(feature = "unstable-static-class", feature = "gnustep-1-7"))]
+macro_rules! __class_inner {
+ ($name:expr, $_hash:expr) => {{
+ // NOTE: This is not verified for correctness in any way whatsoever.
+ extern "C" {
+ #[link_name = $crate::__class_static_name!($name)]
+ static CLASS: $crate::runtime::AnyClass;
+
+ // Others:
+ // __objc_class_name_
+ // _OBJC_CLASS_REF_
+ }
+
+ #[allow(unused_unsafe)]
+ unsafe {
+ $crate::__macro_helpers::disallow_in_static(&CLASS)
+ }
+ }};
+}
+
+/// Send a message to an object or class.
+///
+/// This is wildly `unsafe`, even more so than sending messages in
+/// Objective-C, because this macro can't inspect header files to see the
+/// expected types, and because Rust has more safety invariants to uphold.
+/// Make sure to review the safety section below!
+///
+/// The recommended way of using this macro is by defining a wrapper function:
+///
+/// ```
+/// # use std::ffi::{c_int, c_char};
+/// # use objc2::msg_send;
+/// # use objc2::runtime::NSObject;
+/// unsafe fn do_something(obj: &NSObject, arg: c_int) -> *const c_char {
+/// msg_send![obj, doSomething: arg]
+/// }
+/// ```
+///
+/// This way we are clearly communicating to Rust that: The method
+/// `doSomething:` works with a shared reference to the object. It takes a
+/// C-style signed integer, and returns a pointer to what is probably a
+/// C-compatible string. Now it's much, _much_ easier to make a safe
+/// abstraction around this!
+///
+/// The [`extern_methods!`] macro can help with coding this pattern.
+///
+/// [`extern_methods!`]: crate::extern_methods
+///
+///
+/// # Memory management
+///
+/// If an Objective-C method returns `id`, `NSObject*`, or similar object
+/// pointers, you should use [`Retained<T>`] on the Rust side, or
+/// `Option<Retained<T>>` if the pointer is nullable.
+///
+/// This is necessary because object pointers in Objective-C have certain
+/// rules for when they should be retained and released across function calls.
+///
+/// [`Retained<T>`]: crate::rc::Retained
+///
+///
+/// ## A little history
+///
+/// Objective-C's type system is... limited, so you can't tell without
+/// consulting the documentation who is responsible for releasing an object.
+/// To remedy this problem, Apple/Cocoa introduced (approximately) the
+/// following rule:
+///
+/// The caller is responsible for releasing objects return from methods that
+/// begin with `new`, `alloc`, `copy`, `mutableCopy` or `init`, and method
+/// that begins with `init` takes ownership of the receiver. See [Cocoa's
+/// Memory Management Policy][mmRules] for a user-friendly introduction to
+/// this concept.
+///
+/// In the past, users had to do `retain` and `release` calls themselves to
+/// properly follow these rules. To avoid the memory management problems
+/// associated with manual stuff like that, they [introduced "ARC"][arc-rel],
+/// which codifies the rules as part of the language, and inserts the required
+/// `retain` and `release` calls automatically.
+///
+/// Returning a `*const T` pointer is similar to pre-ARC; you have to know
+/// when to retain and when to release an object. Returning `Retained` is
+/// similar to ARC; the rules are simple enough that we can do them
+/// automatically!
+///
+/// [mmRules]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-SW1
+/// [arc-rel]: https://developer.apple.com/library/archive/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011226
+///
+///
+/// # Specification
+///
+/// The syntax is somewhat similar to the message syntax in Objective-C,
+/// except with a comma between arguments. Eliding the comma is possible, but
+/// deprecated, and may be removed in a future version of `objc2`.
+///
+/// The first expression, know as the "receiver", can be any type that
+/// implements [`MessageReceiver`], like a reference or a pointer to an
+/// object. Additionally, it can even be a reference to an [`Retained`]
+/// containing an object.
+///
+/// The expression can be wrapped in `super`, with an optional superclass
+/// as the second argument. If no specific superclass is specified, the
+/// direct superclass is retrieved from [`ClassType`].
+///
+/// All arguments, as well as the return type, must implement [`Encode`] (bar
+/// the exceptions below).
+///
+/// If the last argument is the special marker `_`, the macro will return a
+/// `Result<_, Retained<E>>`, see below.
+///
+/// This macro roughly translates into a call to [`sel!`], and afterwards a
+/// fully qualified call to [`MessageReceiver::send_message`]. Note that this
+/// means that auto-dereferencing of the receiver is not supported, and that
+/// the receiver is consumed. You may encounter a little trouble with `&mut`
+/// references, try refactoring into a separate method or reborrowing the
+/// reference.
+///
+/// Variadic arguments are currently not supported.
+///
+/// [`MessageReceiver`]: crate::runtime::MessageReceiver
+/// [`Retained`]: crate::rc::Retained
+/// [`ClassType`]: crate::ClassType
+/// [`Encode`]: crate::Encode
+/// [`sel!`]: crate::sel
+/// [`MessageReceiver::send_message`]: crate::runtime::MessageReceiver::send_message
+///
+///
+/// ## Memory management details
+///
+/// The accepted receiver and return types, and how we handle them, differ
+/// depending on which, if any, of the [recognized selector
+/// families][sel-families] the selector belongs to:
+///
+/// - The `new` family: The receiver may be anything that implements
+/// [`MessageReceiver`] (though often you'll want to use `&AnyClass`). The
+/// return type is a generic `Retained<T>` or `Option<Retained<T>>`.
+///
+/// - The `alloc` family: The receiver must be `&AnyClass`, and the return
+/// type is a generic `Allocated<T>`.
+///
+/// - The `init` family: The receiver must be `Allocated<T>` as returned from
+/// `alloc`, or if sending messages to the superclass, it must be
+/// `PartialInit<T>`.
+///
+/// The receiver is consumed, and a the now-initialized `Retained<T>` or
+/// `Option<Retained<T>>` (with the same `T`) is returned.
+///
+/// - The `copy` family: The receiver may be anything that implements
+/// [`MessageReceiver`] and the return type is a generic `Retained<T>` or
+/// `Option<Retained<T>>`.
+///
+/// - The `mutableCopy` family: Same as the `copy` family.
+///
+/// - No family: The receiver may be anything that implements
+/// [`MessageReceiver`]. The result is retained using
+/// [`Retained::retain_autoreleased`], and a generic `Retained<T>` or
+/// `Option<Retained<T>>` is returned. This retain is in most cases faster
+/// than using autorelease pools!
+///
+/// See [the clang documentation][arc-retainable] for the precise
+/// specification of Objective-C's ownership rules.
+///
+/// As you may have noticed, the return type is usually either `Retained` or
+/// `Option<Retained>`. Internally, the return type is always
+/// `Option<Retained>` (for example: almost all `new` methods can fail if the
+/// allocation failed), but for convenience, if the return type is
+/// `Retained<T>`, this macro will automatically unwrap the object, or panic
+/// with an error message if it couldn't be retrieved.
+///
+/// As a special case, if the last argument is the marker `_`, the macro will
+/// return a `Result<Retained<T>, Retained<E>>`, see below.
+///
+/// The `retain`, `release` and `autorelease` selectors are not supported, use
+/// [`Retained::retain`], [`Retained::drop`] and [`Retained::autorelease_ptr`]
+/// for that.
+///
+/// [sel-families]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-method-families
+/// [`MessageReceiver`]: crate::runtime::MessageReceiver
+/// [`Retained::retain_autoreleased`]: crate::rc::Retained::retain_autoreleased
+/// [arc-retainable]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#retainable-object-pointers-as-operands-and-arguments
+/// [`Retained::retain`]: crate::rc::Retained::retain
+/// [`Retained::drop`]: crate::rc::Retained::drop
+/// [`Retained::autorelease_ptr`]: crate::rc::Retained::autorelease_ptr
+///
+///
+/// # `bool` handling
+///
+/// Objective-C's `BOOL` is slightly different from Rust's [`bool`], and hence
+/// a conversion step must be performed before using it. This is _very_ easy
+/// to forget (because it'll happen to work in _most_ cases), so this macro
+/// does the conversion step automatically whenever an argument or the return
+/// type is `bool`.
+///
+/// That means that any Objective-C method that take or return `BOOL` can be
+/// translated to use `bool` on the Rust side.
+///
+/// If you want to handle the conversion explicitly, or the Objective-C method
+/// expects e.g. a pointer to a `BOOL`, use [`runtime::Bool`] instead.
+///
+/// [`runtime::Bool`]: crate::runtime::Bool
+///
+///
+/// # Out-parameters
+///
+/// Parameters like `NSString**` in Objective-C are passed by "writeback",
+/// which means that the callee autoreleases any value that they may write
+/// into the parameter.
+///
+/// This macro has support for passing such parameters using the following
+/// types:
+/// - `&mut Retained<_>`
+/// - `Option<&mut Retained<_>>`
+/// - `&mut Option<Retained<_>>`,
+/// - `Option<&mut Option<Retained<_>>>`
+///
+/// Beware with the first two, since they will cause undefined behaviour if
+/// the method overwrites the value with `nil`.
+///
+/// See [clang's documentation][clang-out-params] for more details.
+///
+/// [clang-out-params]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#passing-to-an-out-parameter-by-writeback
+///
+///
+/// # Errors
+///
+/// The most common place you'll see out-parameters is as `NSError**` the last
+/// parameter, which is used to communicate errors to the caller, see [Error
+/// Handling Programming Guide For Cocoa][cocoa-error].
+///
+/// Similar to Swift's [importing of error parameters][swift-error], this
+/// macro supports an even more convenient version than the out-parameter
+/// support, which transforms methods whose last parameter is `NSError**` into
+/// the Rust equivalent, the [`Result`] type.
+///
+/// In particular, if you make the last argument the special marker `_`, then
+/// the macro will return a `Result<R, Retained<E>>`. The error type `E` must
+/// be either [`NSObject`] or `objc2_foundation::NSError`.
+///
+/// The success type `R` must be either `()` or `Retained<T>`.
+///
+/// At runtime, we create the temporary error variable for you on the stack
+/// and send it as the out-parameter to the method. If the method then returns
+/// `NO`/`false`, or in the case of an object pointer, `NULL`, the error
+/// variable is loaded and returned in [`Err`].
+///
+/// [cocoa-error]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ErrorHandlingCocoa/ErrorHandling/ErrorHandling.html
+/// [swift-error]: https://developer.apple.com/documentation/swift/about-imported-cocoa-error-parameters
+/// [`NSObject`]: crate::runtime::NSObject
+///
+///
+/// # Panics
+///
+/// Unwinds if the underlying method throws and exception. If the
+/// `"catch-all"` Cargo feature is enabled, the Objective-C exception is
+/// converted into a Rust panic, with potentially a bit better stack trace.
+///
+/// Finally, panics if the return type is specified as `Retained<_>`, but the
+/// method actually returned NULL. If this happens, you should change the
+/// signature to instead return `Option<Retained<_>>` to handle the error
+/// yourself.
+///
+///
+/// ## Type verification
+///
+/// To make message sending safer, all arguments and return values for
+/// messages must implement [`encode::Encode`]. This allows the Rust compiler
+/// to prevent you from passing e.g. a [`Vec`] into Objective-C, which would
+/// both be UB and leak the vector.
+///
+/// When `debug_assertions` are enabled, this macro will check the encoding of
+/// the given arguments and return every time you send a message, and will
+/// panic if they are not equivalent.
+///
+/// This is not a perfect solution for ensuring safety (some Rust types have
+/// the same Objective-C encoding, but are not equivalent, such as `&T` and
+/// `*const T`), but it gets us much closer to it!
+///
+/// This behaviour can be tweaked with the `"relax-void-encoding"`,
+/// `"relax-sign-encoding"` or `"disable-encoding-assertions"` Cargo feature
+/// flags if it is causing you trouble.
+///
+/// [`encode::Encode`]: crate::encode::Encode
+/// [`Vec`]: std::vec::Vec
+///
+///
+/// # Safety
+///
+/// Similar to defining and calling an `extern` function in a foreign function
+/// interface. In particular, you must uphold the following requirements:
+///
+/// 1. The selector corresponds to a valid method that is available on the
+/// receiver.
+///
+/// 2. The argument types match what the receiver excepts for this selector.
+///
+/// 3. The return type match what the receiver returns for this selector.
+///
+/// 4. The call must not violate Rust's mutability rules, for example if
+/// passing an `&T`, the Objective-C method must not mutate the variable
+/// (except if the variable is inside [`std::cell::UnsafeCell`] or
+/// derivatives).
+///
+/// 5. If the receiver is a raw pointer it must be valid (aligned,
+/// dereferenceable, initialized and so on). Messages to `null` pointers
+/// are allowed (though heavily discouraged), but _only_ if the return type
+/// itself is a pointer.
+///
+/// 6. You must uphold any additional safety requirements (explicit and
+/// implicit) that the method has. For example:
+/// - Methods that take pointers usually require that the pointer is valid,
+/// and sometimes non-null.
+/// - Sometimes, a method may only be called on the main thread.
+/// - The lifetime of returned pointers usually follows certain rules, and
+/// may not be valid outside of an [`autoreleasepool`] (returning
+/// `Retained` usually helps with these cases).
+///
+/// 7. Each out-parameter must have the correct nullability, and the method
+/// must not have any attributes that changes the how it handles memory
+/// management for these.
+///
+/// 8. If using the automatic memory management facilities of this macro, the
+/// method must not have any attributes such as `objc_method_family`,
+/// `ns_returns_retained`, `ns_consumed` that changes the how it handles
+/// memory management.
+///
+/// 8. TODO: Maybe more?
+///
+/// [`autoreleasepool`]: crate::rc::autoreleasepool
+///
+///
+/// # Examples
+///
+/// Interacting with [`NSURLComponents`], [`NSString`] and [`NSNumber`].
+///
+/// [`NSURLComponents`]: https://developer.apple.com/documentation/foundation/nsurlcomponents?language=objc
+/// [`NSString`]: https://developer.apple.com/documentation/foundation/nsstring?language=objc
+/// [`NSNumber`]: https://developer.apple.com/documentation/foundation/nsnumber?language=objc
+///
+/// ```
+/// use objc2::rc::Retained;
+/// use objc2::{msg_send, ClassType};
+/// use objc2_foundation::{NSNumber, NSString, NSURLComponents};
+///
+///
+/// // Create an empty `NSURLComponents` by calling the class method `new`.
+/// let components: Retained<NSURLComponents> = unsafe {
+/// // ^^^^^^^^^^^^^^^^^^^^^^^^^ the return type, a memory-managed
+/// // `NSURLComponents` instance
+/// //
+/// msg_send![NSURLComponents::class(), new]
+/// // ------------------------ ^^^ the selector `new`
+/// // |
+/// // the receiver, in this case the class itself
+/// };
+///
+///
+/// // Create a new `NSNumber` from an integer.
+/// let port: Retained<NSNumber> = unsafe {
+/// msg_send![NSNumber::class(), numberWithInt: 8080i32]
+/// // -------------- ^^^^^^^ the argument to the method
+/// // |
+/// // the selector `numberWithInt:`
+/// //
+/// // Note how we must fully specify the argument as `8080i32` instead of just `8080`.
+/// };
+///
+///
+/// // Set the port property of the URL.
+/// let _: () = unsafe { msg_send![&components, setPort: &*port] };
+/// // -- -------- ^^^^^^ the port is deref'd to
+/// // | | become the correct type
+/// // | |
+/// // | the selector `setPort:` is derived
+/// // | from the property name `port`.
+/// // |
+/// // return type (i.e. nothing / void)
+/// //
+/// // Note that even return types of `void` must be explicitly specified as `()`.
+///
+///
+/// // Set the `host` property of the URL.
+/// let host: Retained<NSString> = unsafe {
+/// msg_send![NSString::class(), stringWithUTF8String: c"example.com".as_ptr()]
+/// };
+/// let _: () = unsafe { msg_send![&components, setHost: &*host] };
+///
+///
+/// // Set the `scheme` property of the URL.
+/// let scheme: Retained<NSString> = unsafe {
+/// msg_send![NSString::class(), stringWithUTF8String: c"http".as_ptr()]
+/// };
+/// let _: () = unsafe { msg_send![&components, setScheme: &*scheme] };
+///
+///
+/// // Get the combined URL in string form.
+/// let string: Option<Retained<NSString>> = unsafe { msg_send![&components, string] };
+/// // ^^^^^^ the method can return NULL, so we specify an option here
+///
+///
+/// assert_eq!(string.unwrap().to_string(), "http://example.com:8080");
+/// ```
+///
+/// The example above uses only `msg_send!` for demonstration purposes; note
+/// that usually the interface you seek is already present in [the framework
+/// crates] and then the equivalent code can be as simple as:
+///
+/// [the framework crates]: crate::topics::about_generated
+///
+/// ```
+/// use objc2_foundation::{NSNumber, NSString, NSURLComponents};
+///
+/// let components = unsafe { NSURLComponents::new() };
+/// unsafe { components.setPort(Some(&NSNumber::new_i32(8080))) };
+/// unsafe { components.setHost(Some(&NSString::from_str("example.com"))) };
+/// unsafe { components.setScheme(Some(&NSString::from_str("http"))) };
+/// let string = unsafe { components.string() };
+///
+/// assert_eq!(string.unwrap().to_string(), "http://example.com:8080");
+/// ```
+///
+/// Sending messages to the superclass of an object.
+///
+/// ```no_run
+/// use objc2::runtime::NSObject;
+/// use objc2::{msg_send, ClassType};
+/// #
+/// # objc2::define_class!(
+/// # #[unsafe(super(NSObject))]
+/// # struct MyObject;
+/// # );
+/// #
+/// # let obj: objc2::rc::Retained<MyObject> = todo!();
+///
+/// // Call `someMethod` on the direct super class.
+/// let _: () = unsafe { msg_send![super(&obj), someMethod] };
+///
+/// // Or lower-level, a method on a specific superclass.
+/// let superclass = NSObject::class();
+/// let arg3: u32 = unsafe { msg_send![super(&obj, superclass), getArg3] };
+/// ```
+///
+/// Sending a message with automatic error handling.
+///
+/// ```no_run
+/// use objc2::msg_send;
+/// use objc2::rc::Retained;
+/// # #[cfg(requires_foundation)]
+/// use objc2_foundation::{NSBundle, NSError};
+/// # use objc2::runtime::NSObject as NSBundle;
+/// # use objc2::runtime::NSObject as NSError;
+///
+/// # #[cfg(requires_foundation)]
+/// let bundle = NSBundle::mainBundle();
+/// # let bundle = NSBundle::new();
+///
+/// let res: Result<(), Retained<NSError>> = unsafe {
+/// // -- -------- ^^^^^^^ must be NSError or NSObject
+/// // | |
+/// // | always retained
+/// // |
+/// // `()` means that the method returns `bool`, we check
+/// // that and return success if `true`, an error if `false`
+/// //
+/// msg_send![&bundle, preflightAndReturnError: _]
+/// // ^ activate error handling
+/// };
+/// ```
+///
+/// Sending a message with an out parameter _and_ automatic error handling.
+///
+/// ```no_run
+/// use objc2::msg_send;
+/// use objc2::rc::Retained;
+///
+/// # type NSFileManager = objc2::runtime::NSObject;
+/// # type NSURL = objc2::runtime::NSObject;
+/// # type NSError = objc2::runtime::NSObject;
+/// let obj: &NSFileManager;
+/// # obj = todo!();
+/// let url: &NSURL;
+/// # url = todo!();
+/// let mut result_url: Option<Retained<NSURL>> = None;
+/// unsafe {
+/// msg_send![
+/// obj,
+/// trashItemAtURL: url,
+/// resultingItemURL: Some(&mut result_url),
+/// error: _
+/// ]?
+/// // ^ is possible on error-returning methods, if the return type is specified
+/// };
+///
+/// // Use `result_url` here
+///
+/// # Ok::<(), Retained<NSError>>(())
+/// ```
+///
+/// Attempt to do an invalid message send. This is undefined behaviour, but
+/// will panic with `debug_assertions` enabled.
+///
+/// ```should_panic
+/// use objc2::msg_send;
+/// use objc2::runtime::NSObject;
+///
+/// let obj = NSObject::new();
+///
+/// // Wrong return type - this is UB!
+/// //
+/// // But it will be caught with `debug_assertions` enabled, stating that
+/// // the return type's encoding is not correct.
+/// let hash: f32 = unsafe { msg_send![&obj, hash] };
+/// #
+/// # panic!("does not panic in release mode, so for testing we make it!");
+/// ```
+#[macro_export]
+macro_rules! msg_send {
+ [super($obj:expr), $($selector_and_arguments:tt)+] => {
+ $crate::__msg_send_parse! {
+ ()
+ ()
+ ($($selector_and_arguments)+)
+
+ (MsgSendSuperError::send_super_message_static_error)
+ (MsgSendSuper::send_super_message_static)
+
+ ($crate::__msg_send_helper)
+ ($obj)
+ () // No method family
+ }
+ };
+ [super($obj:expr, $superclass:expr), $($selector_and_arguments:tt)+] => {
+ $crate::__msg_send_parse! {
+ ()
+ ()
+ ($($selector_and_arguments)+)
+
+ (MsgSendSuperError::send_super_message_error)
+ (MsgSendSuper::send_super_message)
+
+ ($crate::__msg_send_helper)
+ ($obj, $superclass)
+ () // No method family
+ }
+ };
+ [$obj:expr, $($selector_and_arguments:tt)+] => {
+ $crate::__msg_send_parse! {
+ ()
+ ()
+ ($($selector_and_arguments)+)
+
+ (MsgSendError::send_message_error)
+ (MsgSend::send_message)
+
+ ($crate::__msg_send_helper)
+ ($obj)
+ () // No method family
+ }
+ };
+}
+
+/// Use [`msg_send!`] instead, it now supports converting to/from `bool`.
+#[macro_export]
+#[deprecated = "use a normal msg_send! instead, it will perform the conversion for you"]
+macro_rules! msg_send_bool {
+ [$($msg_send_args:tt)+] => ({
+ // Use old impl for backwards compat
+ let result: $crate::runtime::Bool = $crate::msg_send![$($msg_send_args)+];
+ result.as_bool()
+ });
+}
+
+/// Use [`msg_send!`] instead, it now supports converting to/from
+/// [`Retained`][crate::rc::Retained].
+#[macro_export]
+#[deprecated = "use a normal msg_send! instead, it will now perform the conversion to/from `Retained` for you"]
+macro_rules! msg_send_id {
+ [$($msg_send_args:tt)+] => {
+ $crate::msg_send![$($msg_send_args)*]
+ }
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __msg_send_helper {
+ {
+ ($($fn_args:tt)+)
+ ($($method_family:tt)*)
+ ($trait:ident :: $fn:ident)
+ ($($selector:tt)*)
+ ($($argument:expr,)*)
+ } => ({
+ // Assign to intermediary variable for better UI, and to prevent
+ // miscompilation on older Rust versions (TODO: Which ones?).
+ //
+ // Note: This can be accessed from any expression in `fn_args` and
+ // `argument` - we won't (yet) bother with preventing that though.
+ let result;
+
+ // Always add trailing comma after each argument, so that we get a
+ // 1-tuple if there is only one.
+ //
+ // And use `::<_, _>` for better UI.
+ result = <$crate::__method_family!(($($method_family)*) ($($selector)*)) as $crate::__macro_helpers::$trait<_, _>>::$fn(
+ $($fn_args)+,
+ $crate::sel!($($selector)*),
+ ($($argument,)*),
+ );
+ result
+ });
+}
diff --git a/third_party/rust/objc2/src/main_thread_marker.rs b/third_party/rust/objc2/src/main_thread_marker.rs
@@ -0,0 +1,316 @@
+//! This belongs in `std` IMO, but wasn't accepted there:
+//! <https://github.com/rust-lang/rust/pull/136616>
+use core::fmt;
+use core::marker::PhantomData;
+
+use crate::rc::Allocated;
+use crate::{ClassType, MainThreadOnly};
+
+/// Whether the current thread is the main thread.
+#[inline]
+fn is_main_thread() -> bool {
+ #[cfg(target_vendor = "apple")]
+ {
+ // Normally you would use `+[NSThread isMainThread]`, but benchmarks
+ // have shown that calling the underlying `pthread_main_np` directly
+ // is up to four times faster, so we use that instead.
+
+ // SAFETY: The signature in here is the exact same as in `libc`.
+ //
+ // `pthread_main_np` is included via `libSystem` when `libstd` is
+ // linked. All of this is done to avoid a dependency on the `libc`
+ // crate.
+ //
+ // `extern "C"` is safe because this will never unwind.
+ #[cfg_attr(not(feature = "std"), link(name = "c", kind = "dylib"))]
+ extern "C" {
+ fn pthread_main_np() -> core::ffi::c_int;
+ }
+
+ // SAFETY: Can be called from any thread.
+ //
+ // Apple's man page says:
+ // > The pthread_main_np() function returns 1 if the calling thread is the initial thread, 0 if
+ // > the calling thread is not the initial thread, and -1 if the thread's initialization has not
+ // > yet completed.
+ //
+ // However, Apple's header says:
+ // > Returns non-zero if the current thread is the main thread.
+ //
+ // So unclear if we should be doing a comparison against 1, or a negative comparison against 0?
+ // To be safe, we compare against 1, though in reality, the current implementation can only ever
+ // return 0 or 1:
+ // https://github.com/apple-oss-distributions/libpthread/blob/libpthread-535/src/pthread.c#L1084-L1089
+ unsafe { pthread_main_np() == 1 }
+ }
+
+ #[cfg(not(target_vendor = "apple"))]
+ {
+ // Fall back to isMainThread on non-Apple platforms, as
+ // `pthread_main_np` is not always available there.
+ unsafe { crate::msg_send![crate::class!(NSThread), isMainThread] }
+ }
+}
+
+/// A marker type for functionality only available on the main thread.
+///
+/// The main thread is a system-level property on Apple/Darwin platforms, and
+/// has extra capabilities not available on other threads. This is usually
+/// relevant when using native GUI frameworks, where most operations must be
+/// done on the main thread.
+///
+/// This type enables you to manage that capability. By design, it is neither
+/// [`Send`] nor [`Sync`], and can only be created on the main thread, meaning
+/// that if you have an instance of this, you are guaranteed to be on the main
+/// thread / have the "main-thread capability".
+///
+/// [The `main` function][main-functions] will run on the main thread. This
+/// type can also be used with `#![no_main]` or other such cases where Rust is
+/// not defining the binary entry point.
+///
+/// See the following links for more information on main-thread-only APIs:
+/// - [Are the Cocoa Frameworks Thread Safe?](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW47)
+/// - [About Threaded Programming](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/AboutThreads/AboutThreads.html)
+/// - [Thread Safety Summary](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-SW1)
+/// - [Technical Note TN2028 - Threading Architectures](https://developer.apple.com/library/archive/technotes/tn/tn2028.html#//apple_ref/doc/uid/DTS10003065)
+/// - [Thread Management](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html)
+/// - [Swift's `@MainActor`](https://developer.apple.com/documentation/swift/mainactor)
+/// - [Main Thread Only APIs on OS X](https://www.dribin.org/dave/blog/archives/2009/02/01/main_thread_apis/)
+/// - [Mike Ash' article on thread safety](https://www.mikeash.com/pyblog/friday-qa-2009-01-09.html)
+///
+/// [main-functions]: https://doc.rust-lang.org/reference/crates-and-source-files.html#main-functions
+///
+///
+/// # Main Thread Checker
+///
+/// Xcode provides a tool called the ["Main Thread Checker"][mtc] which
+/// verifies that UI APIs are being used from the correct thread. This is not
+/// as principled as `MainThreadMarker`, but is helpful for catching mistakes.
+///
+/// You can use this tool on macOS by loading `libMainThreadChecker.dylib`
+/// into your process using `DYLD_INSERT_LIBRARIES`:
+///
+/// ```console
+/// DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/usr/lib/libMainThreadChecker.dylib MTC_RESET_INSERT_LIBRARIES=0 cargo run
+/// ```
+///
+/// If you're not running your binary through Cargo, you can omit
+/// [`MTC_RESET_INSERT_LIBRARIES`][mtc-reset].
+///
+/// ```console
+/// DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/usr/lib/libMainThreadChecker.dylib target/debug/myapp
+/// ```
+///
+/// If you're developing for iOS, you probably better off enabling the tool in
+/// Xcode's own UI.
+///
+/// See [this excellent blog post][mtc-cfg] for details on further
+/// configuration options.
+///
+/// [mtc]: https://developer.apple.com/documentation/xcode/diagnosing-memory-thread-and-crash-issues-early#Detect-improper-UI-updates-on-background-threads
+/// [mtc-reset]: https://bryce.co/main-thread-checker-configuration/#mtc_reset_insert_libraries
+/// [mtc-cfg]: https://bryce.co/main-thread-checker-configuration/
+///
+///
+/// # Examples
+///
+/// Retrieve the main thread marker in different situations.
+///
+/// ```
+/// use objc2::MainThreadMarker;
+///
+/// # // explicitly uses `fn main`
+/// fn main() {
+/// // The thread that `fn main` runs on is the main thread.
+/// assert!(MainThreadMarker::new().is_some());
+///
+/// // Subsequently spawned threads are not the main thread.
+/// std::thread::spawn(|| {
+/// assert!(MainThreadMarker::new().is_none());
+/// }).join().unwrap();
+/// }
+/// ```
+///
+/// Use when accessing APIs that are only safe to use on the main thread.
+///
+/// ```no_run
+/// use objc2::MainThreadMarker;
+/// # #[cfg(needs_app_kit)]
+/// use objc2_app_kit::NSApplication;
+/// #
+/// # use objc2::runtime::NSObject as NSApplication;
+/// # trait Foo {
+/// # fn sharedApplication(_mtm: MainThreadMarker) {}
+/// # }
+/// # impl Foo for NSApplication {}
+///
+/// # // explicitly uses `fn main`
+/// fn main() {
+/// // Create a new MainThreadMarker.
+/// let mtm = MainThreadMarker::new().expect("must be on the main thread");
+///
+/// // NSApplication is only usable on the main thread,
+/// // so we need to pass the marker as an argument.
+/// let app = NSApplication::sharedApplication(mtm);
+///
+/// // Do something with the application
+/// // app.run();
+/// }
+/// ```
+///
+/// Create a static that is only usable on the main thread. This is similar to
+/// a thread-local, but can be more efficient because it doesn't handle
+/// multiple threads.
+///
+/// See also `dispatch2::MainThreadBound`.
+///
+/// ```
+/// use objc2::MainThreadMarker;
+/// use std::cell::UnsafeCell;
+///
+/// struct SyncUnsafeCell<T>(UnsafeCell<T>);
+///
+/// unsafe impl<T> Sync for SyncUnsafeCell<T> {}
+///
+/// static MAIN_THREAD_ONLY_VALUE: SyncUnsafeCell<i32> = SyncUnsafeCell(UnsafeCell::new(0));
+///
+/// fn set(value: i32, _mtm: MainThreadMarker) {
+/// // SAFETY: We have an instance of `MainThreadMarker`, so we know that
+/// // we're running on the main thread (and thus do not need any
+/// // synchronization, since the only accesses to this value is from the
+/// // main thread).
+/// unsafe { *MAIN_THREAD_ONLY_VALUE.0.get() = value };
+/// }
+///
+/// fn get(_mtm: MainThreadMarker) -> i32 {
+/// // SAFETY: Same as above.
+/// unsafe { *MAIN_THREAD_ONLY_VALUE.0.get() }
+/// }
+///
+/// # // explicitly uses `fn main`
+/// fn main() {
+/// let mtm = MainThreadMarker::new().expect("must be on the main thread");
+/// set(42, mtm);
+/// assert_eq!(get(mtm), 42);
+/// }
+/// ```
+#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
+// ^^^^ this is valid because it's still `!Send` and `!Sync`.
+pub struct MainThreadMarker {
+ // No lifetime information needed; the main thread is static and available
+ // throughout the entire program!
+
+ // Ensure `!Send` and `!Sync`.
+ _priv: PhantomData<*mut ()>,
+}
+
+impl MainThreadMarker {
+ /// Construct a new `MainThreadMarker`.
+ ///
+ /// Returns [`None`] if the current thread was not the main thread.
+ ///
+ ///
+ /// # Example
+ ///
+ /// Check whether the current thread is the main thread.
+ ///
+ /// ```
+ /// use objc2::MainThreadMarker;
+ ///
+ /// if MainThreadMarker::new().is_some() {
+ /// // Is the main thread
+ /// } else {
+ /// // Not the main thread
+ /// }
+ /// ```
+ #[inline]
+ #[doc(alias = "is_main_thread")]
+ #[doc(alias = "pthread_main_np")]
+ #[doc(alias = "isMainThread")]
+ pub fn new() -> Option<Self> {
+ if is_main_thread() {
+ // SAFETY: We just checked that we are running on the main thread.
+ Some(unsafe { Self::new_unchecked() })
+ } else {
+ None
+ }
+ }
+
+ /// Construct a new `MainThreadMarker` without first checking whether the
+ /// current thread is the main one.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The current thread must be the main thread.
+ ///
+ /// Alternatively, you may create this briefly if you know that a an API
+ /// is safe in a specific case, but is not marked so. If you do that, you
+ /// must ensure that any use of the marker is actually safe to do from
+ /// another thread than the main one.
+ #[inline]
+ pub const unsafe fn new_unchecked() -> Self {
+ // SAFETY: Upheld by caller.
+ //
+ // We can't debug_assert that this actually is the main thread, both
+ // because this is `const` (to allow usage in `static`s), and because
+ // users may sometimes want to create this briefly, e.g. to access an
+ // API that in most cases requires the marker, but is safe to use
+ // without in specific cases.
+ Self { _priv: PhantomData }
+ }
+
+ /// Allocate a new instance of the specified class on the main thread.
+ ///
+ /// This can be useful in certain situations, such as generic contexts
+ /// where you don't know whether the class is main thread or not, but
+ /// usually you should prefer [`MainThreadOnly::alloc`].
+ #[inline]
+ pub fn alloc<T: ClassType>(self) -> Allocated<T> {
+ // SAFETY: We hold `MainThreadMarker`, and classes are either only
+ // safe to allocate on the main thread, or safe to allocate
+ // everywhere.
+ unsafe { Allocated::alloc(T::class()) }
+ }
+}
+
+/// Get a [`MainThreadMarker`] from a main-thread-only object.
+///
+/// This is a shorthand for [`MainThreadOnly::mtm`].
+impl<T: ?Sized + MainThreadOnly> From<&T> for MainThreadMarker {
+ #[inline]
+ fn from(obj: &T) -> Self {
+ obj.mtm()
+ }
+}
+
+impl fmt::Debug for MainThreadMarker {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_tuple("MainThreadMarker").finish()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::panic::{RefUnwindSafe, UnwindSafe};
+
+ static_assertions::assert_impl_all!(MainThreadMarker: Unpin, UnwindSafe, RefUnwindSafe, Sized);
+ static_assertions::assert_not_impl_any!(MainThreadMarker: Send, Sync);
+
+ #[test]
+ fn debug() {
+ // SAFETY: We don't use the marker for anything other than its Debug
+ // impl, so this test doesn't actually need to run on the main thread!
+ let marker = unsafe { MainThreadMarker::new_unchecked() };
+ assert_eq!(std::format!("{marker:?}"), "MainThreadMarker");
+ }
+
+ #[test]
+ fn test_not_main_thread() {
+ let res = std::thread::spawn(|| MainThreadMarker::new().is_none())
+ .join()
+ .unwrap();
+ assert!(res);
+ }
+}
diff --git a/third_party/rust/objc2/src/rc/allocated_partial_init.rs b/third_party/rust/objc2/src/rc/allocated_partial_init.rs
@@ -0,0 +1,413 @@
+use core::marker::PhantomData;
+use core::mem::ManuallyDrop;
+use core::ptr::NonNull;
+use core::{fmt, ptr};
+
+use crate::__macro_helpers::defined_ivars::initialize_ivars;
+use crate::runtime::{objc_release_fast, AnyClass, AnyObject};
+use crate::{DefinedClass, Message};
+
+/// An Objective-C object that has been allocated, but not initialized.
+///
+/// Objective-C splits the allocation and initialization steps up into two, so
+/// we need to track it in the type-system whether something has been
+/// initialized or not.
+///
+/// Note that allocation in Objective-C can fail, e.g. in Out Of Memory
+/// situations! This is handled by `objc2` automatically, but if you really
+/// need to, you can check for this explicitly by inspecting the pointer
+/// returned from [`as_ptr`].
+///
+/// Note also that this represents that the _current_ class's instance
+/// variables are not yet initialized; but subclass instance variables may
+/// have been so.
+///
+/// See [Apple's documentation on Object Allocation][object-allocation] for a
+/// few more details.
+///
+/// [`as_ptr`]: Self::as_ptr
+/// [object-allocation]: https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/ObjectAllocation/ObjectAllocation.html
+///
+///
+/// # Memory layout
+///
+/// This is guaranteed to have the same size and alignment as a pointer to the
+/// object, `*const T`. The pointer may be NULL.
+#[repr(transparent)]
+#[derive(Debug)]
+#[cfg_attr(
+ feature = "unstable-coerce-pointee",
+ derive(std::marker::CoercePointee)
+)]
+pub struct Allocated<T: ?Sized> {
+ /// The yet-to-be initialized object.
+ ///
+ /// We don't use `Retained` here, since that has different auto-trait
+ /// impls, and requires in its safety contract that the object is
+ /// initialized (which makes it difficult to ensure correctness if such
+ /// things are split across different files). Additionally, we want to
+ /// have fine control over NULL-ness.
+ ///
+ /// Covariance is correct, same as `Retained`.
+ ptr: *const T, // Intentionally not `NonNull`!
+ /// Necessary for dropck, as with `Retained`.
+ p: PhantomData<T>,
+ /// Necessary for restricting auto traits.
+ ///
+ /// We _could_ probably implement auto traits `Send` and `Sync` here, but to be
+ /// safe, we won't for now.
+ p_auto_traits: PhantomData<AnyObject>,
+}
+
+// Explicitly don't implement `Deref`, `Message` nor `RefEncode`.
+//
+// We do want to implement `Receiver` though, to allow the user to type
+// `self: Allocated<Self>`.
+#[cfg(feature = "unstable-arbitrary-self-types")]
+impl<T: ?Sized> core::ops::Receiver for Allocated<T> {
+ type Target = T;
+}
+
+impl<T: ?Sized + Message> Allocated<T> {
+ /// # Safety
+ ///
+ /// The caller must ensure the pointer is NULL, or that the given object
+ /// has +1 retain count, and that the object behind the pointer has been
+ /// allocated (but not yet initialized).
+ #[inline]
+ pub(crate) unsafe fn new(ptr: *mut T) -> Self {
+ Self {
+ ptr,
+ p: PhantomData,
+ p_auto_traits: PhantomData,
+ }
+ }
+
+ /// Allocate the object with a fast path using `objc_alloc`.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The object must be safe to allocate on the current thread, and the
+ /// object `T` must be an instance of the class.
+ #[doc(alias = "objc_alloc")]
+ #[inline]
+ pub(crate) unsafe fn alloc(cls: &AnyClass) -> Self
+ where
+ T: Sized,
+ {
+ // Available on non-fragile Apple runtimes.
+ #[cfg(all(
+ target_vendor = "apple",
+ not(all(target_os = "macos", target_arch = "x86"))
+ ))]
+ {
+ // SAFETY: Thread safety checked by the caller.
+ let obj: *mut T = unsafe { crate::ffi::objc_alloc(cls).cast() };
+ // SAFETY: The object is newly allocated, so this has +1 retain count
+ unsafe { Self::new(obj) }
+ }
+ #[cfg(not(all(
+ target_vendor = "apple",
+ not(all(target_os = "macos", target_arch = "x86"))
+ )))]
+ {
+ // SAFETY: Thread safety checked by the caller.
+ unsafe { crate::msg_send![cls, alloc] }
+ }
+ }
+
+ /// Returns a raw pointer to the object.
+ ///
+ /// The pointer is valid for at least as long as the `Allocated` is held.
+ ///
+ /// See [`Allocated::as_mut_ptr`] for the mutable equivalent.
+ ///
+ /// This is an associated method, and must be called as
+ /// `Allocated::as_ptr(obj)`.
+ #[inline]
+ pub fn as_ptr(this: &Self) -> *const T {
+ this.ptr
+ }
+
+ /// Returns a raw mutable pointer to the object.
+ ///
+ /// The pointer is valid for at least as long as the `Allocated` is held.
+ ///
+ /// See [`Allocated::as_ptr`] for the immutable equivalent.
+ ///
+ /// This is an associated method, and must be called as
+ /// `Allocated::as_mut_ptr(obj)`.
+ ///
+ ///
+ /// # Note about mutable references
+ ///
+ /// In general, you're not allowed to create a mutable reference from
+ /// `Allocated`, unless you're defining the object and know that to be
+ /// safe.
+ ///
+ /// For example, `+[NSMutableString alloc]` is allowed to return a
+ /// non-unique object as an optimization, and then only figure out
+ /// afterwards whether it needs to allocate, or if it can store an
+ /// `NSString` internally.
+ ///
+ /// Similarly, while e.g. `+[NSData alloc]` may return a unique object,
+ /// calling `-[NSData init]` afterwards could return a shared empty
+ /// `NSData` instance.
+ #[inline]
+ #[allow(unknown_lints)] // New lint below
+ #[allow(clippy::needless_pass_by_ref_mut)]
+ pub fn as_mut_ptr(this: &mut Self) -> *mut T {
+ // Note: Mutable pointers _can_ be safe for non-mutable classes,
+ // especially right when they're being allocated / initialized.
+ this.ptr as *mut T
+ }
+
+ #[inline]
+ pub(crate) fn into_ptr(this: Self) -> *mut T {
+ let this = ManuallyDrop::new(this);
+ this.ptr as *mut T
+ }
+
+ /// Initialize the instance variables for this object.
+ ///
+ /// This consumes the allocated instance, and returns the now partially
+ /// initialized instance instead, which can be further used in
+ /// [`msg_send!`] `super` calls.
+ ///
+ /// This works very similarly to [Swift's two-phase initialization
+ /// scheme][two-phase-init], see that for details.
+ ///
+ /// [`msg_send!`]: crate::msg_send
+ /// [two-phase-init]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/initialization/#Two-Phase-Initialization
+ ///
+ ///
+ /// # Panics
+ ///
+ /// If debug assertions are enabled, this function will panic if the
+ /// allocated instance is `NULL`, which usually only happens in Out of
+ /// Memory situations.
+ ///
+ /// If debug assertions are disabled, this will return a `NULL` instance
+ /// and the ivars will be dropped. The NULL instance cannot cause
+ /// unsoundness and will likely lead to an initialization failure later on
+ /// instead, but not panicking here is done as a code-size optimization.
+ //
+ // Note: This is intentionally _not_ an associated method, even though
+ // `Allocated` will become `MethodReceiver` in the future.
+ #[inline]
+ #[track_caller]
+ pub fn set_ivars(self, ivars: T::Ivars) -> PartialInit<T>
+ where
+ T: DefinedClass + Sized,
+ {
+ if let Some(ptr) = NonNull::new(ManuallyDrop::new(self).ptr as *mut T) {
+ // SAFETY: The pointer came from `self`, so it is valid.
+ unsafe { initialize_ivars::<T>(ptr, ivars) };
+
+ // SAFETY:
+ // - The pointer came from a `ManuallyDrop<Allocated<T>>`, which means
+ // that we've now transferred ownership over +1 retain count.
+ // - The instance variables for this class have been initialized above.
+ unsafe { PartialInit::new(ptr.as_ptr()) }
+ } else if cfg!(debug_assertions) {
+ panic!("tried to initialize instance variables on a NULL allocated object")
+ } else {
+ // Explicitly drop the ivars in this branch
+ drop(ivars);
+
+ // Create a new NULL PartialInit, which will likely be checked for
+ // NULL-ness later on, after initialization of it has failed.
+ //
+ // SAFETY: The pointer is NULL.
+ unsafe { PartialInit::new(ptr::null_mut()) }
+ }
+ }
+}
+
+impl<T: ?Sized> Drop for Allocated<T> {
+ #[inline]
+ fn drop(&mut self) {
+ // SAFETY: Allocated objects can always safely be released, since
+ // destructors are written to take into account that the object may
+ // not have been initialized.
+ //
+ // This is also safe in the case where the object is NULL,
+ // since `objc_release` allows NULL pointers.
+ //
+ // Rest is same as `Retained`'s `Drop`.
+ unsafe { objc_release_fast(self.ptr as *mut _) };
+ }
+}
+
+impl<T: ?Sized> fmt::Pointer for Allocated<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Pointer::fmt(&self.ptr, f)
+ }
+}
+
+/// An Objective-C object that has been allocated and initialized in the
+/// current class, but not yet initialized in the superclass.
+///
+/// This is returned by [`Allocated::set_ivars`], and is intended to be used
+/// further in [`msg_send!`] `super` calls.
+///
+/// [`msg_send!`]: crate::msg_send
+///
+///
+/// # Memory layout
+///
+/// The memory layout of this struct is NOT currently guaranteed, as we may
+/// want to be able to move a drop flag to the stack in the future.
+//
+// Internally, this is very similar to `Allocated`, except that we have
+// different guarantees on the validity of the object.
+#[repr(transparent)]
+#[derive(Debug)]
+pub struct PartialInit<T: ?Sized> {
+ /// The partially initialized object.
+ ///
+ /// Variance is same as `Retained`.
+ ptr: *const T, // Intentionally not NonNull<T>
+ /// Necessary for dropck, as with `Retained`.
+ p: PhantomData<T>,
+ /// Restrict auto traits, same as `Allocated<T>`.
+ p_auto_traits: PhantomData<AnyObject>,
+}
+
+impl<T: ?Sized + Message> PartialInit<T> {
+ /// # Safety
+ ///
+ /// The caller must ensure the pointer is NULL, or that the given object
+ /// is allocated, has +1 retain count, and that the class' instance
+ /// variables have been initialized.
+ #[inline]
+ pub(crate) unsafe fn new(ptr: *mut T) -> Self {
+ Self {
+ ptr,
+ p: PhantomData,
+ p_auto_traits: PhantomData,
+ }
+ }
+
+ /// Returns a raw pointer to the object.
+ ///
+ /// The pointer is valid for at least as long as the `PartialInit` is
+ /// held.
+ ///
+ /// See [`PartialInit::as_mut_ptr`] for the mutable equivalent.
+ ///
+ /// This is an associated method, and must be called as
+ /// `PartialInit::as_ptr(obj)`.
+ #[inline]
+ pub fn as_ptr(this: &Self) -> *const T {
+ this.ptr
+ }
+
+ /// Returns a raw mutable pointer to the object.
+ ///
+ /// The pointer is valid for at least as long as the `PartialInit` is
+ /// held.
+ ///
+ /// See [`PartialInit::as_ptr`] for the immutable equivalent.
+ ///
+ /// This is an associated method, and must be called as
+ /// `PartialInit::as_mut_ptr(obj)`.
+ #[inline]
+ #[allow(unknown_lints)] // New lint below
+ #[allow(clippy::needless_pass_by_ref_mut)]
+ pub fn as_mut_ptr(this: &mut Self) -> *mut T {
+ this.ptr as *mut T
+ }
+
+ #[inline]
+ pub(crate) fn into_ptr(this: Self) -> *mut T {
+ let this = ManuallyDrop::new(this);
+ this.ptr as *mut T
+ }
+}
+
+impl<T: ?Sized> Drop for PartialInit<T> {
+ #[inline]
+ fn drop(&mut self) {
+ // SAFETY: Partially initialized objects can always safely be
+ // released, since destructors are written to take into account that
+ // the object may not have been fully initialized.
+ //
+ // This is also safe in the case where the object is NULL,
+ // since `objc_release` allows NULL pointers.
+ //
+ // Rest is same as `Retained`.
+ unsafe { objc_release_fast(self.ptr as *mut _) };
+ }
+}
+
+impl<T: ?Sized> fmt::Pointer for PartialInit<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Pointer::fmt(&self.ptr, f)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::panic::{RefUnwindSafe, UnwindSafe};
+
+ use static_assertions::assert_not_impl_any;
+
+ use super::*;
+ use crate::rc::RcTestObject;
+ use crate::runtime::NSObject;
+
+ #[test]
+ fn auto_traits() {
+ assert_not_impl_any!(Allocated<()>: Send, Sync, UnwindSafe, RefUnwindSafe, Unpin);
+ assert_not_impl_any!(PartialInit<()>: Send, Sync, UnwindSafe, RefUnwindSafe, Unpin);
+ }
+
+ #[repr(C)]
+ struct MyObject<'a> {
+ inner: NSObject,
+ p: PhantomData<&'a str>,
+ }
+
+ /// Test that `Allocated<T>` is covariant over `T`.
+ #[allow(unused)]
+ fn assert_allocated_variance<'b>(obj: Allocated<MyObject<'static>>) -> Allocated<MyObject<'b>> {
+ obj
+ }
+
+ /// Test that `PartialInit<T>` is covariant over `T`.
+ #[allow(unused)]
+ fn assert_partialinit_variance<'b>(
+ obj: PartialInit<MyObject<'static>>,
+ ) -> PartialInit<MyObject<'b>> {
+ obj
+ }
+
+ #[test]
+ #[cfg_attr(
+ debug_assertions,
+ should_panic = "tried to initialize instance variables on a NULL allocated object"
+ )]
+ fn test_set_ivars_null() {
+ // SAFETY: The pointer is NULL
+ let obj: Allocated<RcTestObject> = unsafe { Allocated::new(ptr::null_mut()) };
+ let _ = obj.set_ivars(());
+ }
+
+ #[test]
+ #[cfg(feature = "unstable-arbitrary-self-types")]
+ fn arbitrary_self_types() {
+ use crate::rc::Retained;
+ use crate::{extern_methods, AnyThread};
+
+ impl RcTestObject {
+ extern_methods!(
+ #[unsafe(method(init))]
+ fn init_with_self(self: Allocated<Self>) -> Retained<Self>;
+ );
+ }
+
+ let _ = RcTestObject::alloc().init_with_self();
+ }
+}
diff --git a/third_party/rust/objc2/src/rc/autorelease.rs b/third_party/rust/objc2/src/rc/autorelease.rs
@@ -0,0 +1,615 @@
+use core::ffi::c_void;
+#[cfg(not(all(debug_assertions, not(feature = "unstable-autoreleasesafe"))))]
+use core::marker::PhantomData;
+#[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
+use std::{cell::RefCell, thread_local, vec::Vec};
+
+use crate::ffi;
+
+/// The actual pool object.
+///
+/// It is drained when dropped.
+///
+/// This is not [`Send`], since `objc_autoreleasePoolPop` must be called on
+/// the same thread as `objc_autoreleasePoolPush`.
+///
+/// And this is not [`Sync`], since that would make `AutoreleasePool` `Send`.
+#[derive(Debug)]
+struct Pool {
+ /// This is an opaque handle, and is not guaranteed to be neither a valid
+ /// nor an aligned pointer.
+ context: *mut c_void,
+}
+
+impl Pool {
+ /// Construct a new autorelease pool.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that when handing out `AutoreleasePool<'p>` to
+ /// functions that this is the innermost pool.
+ ///
+ /// Additionally, the pools must be dropped in the same order they were
+ /// created.
+ #[inline]
+ unsafe fn new() -> Self {
+ let context = unsafe { ffi::objc_autoreleasePoolPush() };
+ #[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
+ POOLS.with(|c| c.borrow_mut().push(context));
+ Self { context }
+ }
+
+ /// Drains the autoreleasepool.
+ ///
+ /// The [clang documentation] says that `@autoreleasepool` blocks are not
+ /// drained when exceptions occur because:
+ ///
+ /// > Not draining the pool during an unwind is apparently required by the
+ /// > Objective-C exceptions implementation.
+ ///
+ /// The above statement was true in the past, but since [revision `551.1`]
+ /// of objc4 (ships with MacOS 10.9) the exception is now retained when
+ /// `@throw` is encountered (on __OBJC2__, so e.g. not on macOS 32bit).
+ ///
+ /// Since an unwind here is probably caused by Rust, and forgetting to pop
+ /// the pool will likely leak memory, we would really like to do drain in
+ /// `drop` when possible, so that is what we are going to do.
+ ///
+ /// [clang documentation]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#autoreleasepool
+ /// [revision `551.1`]: https://github.com/apple-oss-distributions/objc4/blob/objc4-551.1/runtime/objc-exception.mm#L540
+ #[inline]
+ unsafe fn drain(self) {
+ #[cfg(all(target_os = "macos", target_arch = "x86"))]
+ unsafe {
+ ffi::objc_autoreleasePoolPop(self.context);
+ }
+ }
+}
+
+impl Drop for Pool {
+ #[inline]
+ fn drop(&mut self) {
+ #[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
+ POOLS.with(|c| {
+ assert_eq!(
+ c.borrow_mut().pop(),
+ Some(self.context),
+ "popped pool that was not the innermost pool"
+ );
+ });
+
+ // See `drain`.
+ #[cfg(not(all(target_os = "macos", target_arch = "x86")))]
+ unsafe {
+ ffi::objc_autoreleasePoolPop(self.context);
+ }
+ }
+}
+
+/// An Objective-C autorelease pool.
+///
+/// Autorelease pools are a way to store objects in a certain thread-local
+/// scope, such that they are only released at the end of said scope.
+///
+/// See [`autoreleasepool`] and [`autoreleasepool_leaking`] for how to create
+/// this.
+///
+/// This is not [`Send`] nor [`Sync`], since you can only autorelease a
+/// reference to a pool on the current thread.
+///
+///
+/// # Example
+///
+/// Use the pool as a bound on a function, and release an object to that pool.
+///
+/// ```
+/// use objc2::rc::{autoreleasepool, AutoreleasePool};
+/// use objc2::runtime::NSObject;
+/// use objc2::msg_send;
+///
+/// unsafe fn needs_lifetime_from_pool<'p>(pool: AutoreleasePool<'p>) -> &'p NSObject {
+/// let obj = NSObject::new();
+/// // Do action that returns an autoreleased object
+/// let description: *mut NSObject = unsafe { msg_send![&obj, description] };
+/// // Bound the lifetime of the reference to that of the pool.
+/// //
+/// // Note that this only helps ensuring soundness, our function is still
+/// // unsafe because the pool cannot be guaranteed to be the innermost
+/// // pool.
+/// unsafe { pool.ptr_as_ref(description) }
+/// }
+///
+/// autoreleasepool(|pool| {
+/// // SAFETY: The given pool is the innermost pool.
+/// let obj = unsafe { needs_lifetime_from_pool(pool) };
+/// println!("{obj:?}");
+/// });
+/// ```
+#[derive(Debug, Copy, Clone)]
+pub struct AutoreleasePool<'pool> {
+ /// A reference to the pool.
+ ///
+ /// The lifetime is covariant, since shortening the lifetime is not a
+ /// problem (the lifetime talks about the pool, and not any data inside
+ /// the pool).
+ ///
+ /// To somewhat prove this, consider the following example using
+ /// `typed-arena` to partially implement the autorelease pool:
+ ///
+ /// ```ignore
+ /// struct Pool(typed_arena::Arena<String>);
+ ///
+ /// pub struct AutoreleasePool<'pool>(&'pool Pool);
+ ///
+ /// impl<'pool> AutoreleasePool<'pool> {
+ /// pub fn autorelease(self, s: String) -> &'pool str {
+ /// &*self.0.0.alloc(s)
+ /// }
+ /// }
+ ///
+ /// pub fn autoreleasepool<F, R>(f: F) -> R
+ /// where
+ /// F: for<'pool> FnOnce(AutoreleasePool<'pool>) -> R
+ /// {
+ /// let pool = Pool(Default::default());
+ /// f(AutoreleasePool(&pool))
+ /// }
+ /// ```
+ ///
+ /// Hence assuming `typed-arena` is sound, having covariance here should
+ /// also be sound.
+ #[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
+ inner: Option<&'pool Pool>,
+ /// We use `PhantomData` here to make `AutoreleasePool` a ZST.
+ #[cfg(not(all(debug_assertions, not(feature = "unstable-autoreleasesafe"))))]
+ inner: PhantomData<&'pool Pool>,
+}
+
+#[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
+thread_local! {
+ /// We track the thread's pools to verify that object lifetimes are only
+ /// taken from the innermost pool.
+ static POOLS: RefCell<Vec<*mut c_void>> = const { RefCell::new(Vec::new()) };
+}
+
+impl<'pool> AutoreleasePool<'pool> {
+ fn new(_inner: Option<&'pool Pool>) -> Self {
+ Self {
+ #[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
+ inner: _inner,
+ #[cfg(not(all(debug_assertions, not(feature = "unstable-autoreleasesafe"))))]
+ inner: PhantomData,
+ }
+ }
+
+ #[inline]
+ pub(crate) fn __verify_is_inner(self) {
+ #[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
+ if let Some(pool) = &self.inner {
+ POOLS.with(|c| {
+ assert_eq!(
+ c.borrow().last(),
+ Some(&pool.context),
+ "tried to use lifetime from pool that was not innermost"
+ );
+ });
+ }
+ }
+
+ /// Returns a shared reference to the given autoreleased pointer object.
+ ///
+ /// This is the preferred way to make references from autoreleased
+ /// objects, since it binds the lifetime of the reference to the pool, and
+ /// does some extra checks when debug assertions are enabled.
+ ///
+ /// Note that this is helpful, but not sufficient, for ensuring that the
+ /// lifetime of the reference does not exceed the lifetime of the
+ /// autorelease pool. When calling this, you must also ensure that the
+ /// pool is actually the current innermost pool.
+ ///
+ ///
+ /// # Panics
+ ///
+ /// If the pool is not the innermost pool, this function may panic when
+ /// the `"std"` Cargo feature and debug assertions are enabled.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// This is equivalent to `&*ptr`, and shares the unsafety of that, except
+ /// the lifetime is bound to the pool instead of being unbounded.
+ ///
+ /// The pool must be the innermost pool for the lifetime to be correct.
+ #[inline]
+ pub unsafe fn ptr_as_ref<T: ?Sized>(self, ptr: *const T) -> &'pool T {
+ self.__verify_is_inner();
+ // SAFETY: Checked by the caller
+ unsafe { ptr.as_ref().unwrap_unchecked() }
+ }
+}
+
+/// We use a macro here so that the documentation is included whether the
+/// feature is enabled or not.
+#[cfg(not(feature = "unstable-autoreleasesafe"))]
+macro_rules! auto_trait {
+ {$(#[$fn_meta:meta])* $v:vis unsafe trait AutoreleaseSafe {}} => {
+ $(#[$fn_meta])*
+ $v unsafe trait AutoreleaseSafe {}
+ }
+}
+
+#[cfg(feature = "unstable-autoreleasesafe")]
+macro_rules! auto_trait {
+ {$(#[$fn_meta:meta])* $v:vis unsafe trait AutoreleaseSafe {}} => {
+ $(#[$fn_meta])*
+ $v unsafe auto trait AutoreleaseSafe {}
+ }
+}
+
+auto_trait! {
+ /// Marks types that are safe to pass across the closure in an
+ /// [`autoreleasepool`].
+ ///
+ /// With the `"unstable-autoreleasesafe"` feature enabled, this is an auto
+ /// trait that is implemented for all types except [`AutoreleasePool`].
+ ///
+ /// Otherwise it is a dummy trait that is implemented for all types; the
+ /// safety invariants are checked with debug assertions instead.
+ ///
+ /// You should not normally need to implement this trait yourself.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// Must not be implemented for types that interact with the autorelease
+ /// pool. So if you reimplement the [`AutoreleasePool`] struct or
+ /// likewise, this should be negatively implemented for that.
+ ///
+ /// This can be accomplished with an `PhantomData<AutoreleasePool<'_>>` if
+ /// the `"unstable-autoreleasesafe"` feature is enabled.
+ ///
+ ///
+ /// # Examples
+ ///
+ /// Most types are [`AutoreleaseSafe`].
+ ///
+ /// ```
+ /// use objc2::rc::{AutoreleasePool, AutoreleaseSafe};
+ /// fn requires_autoreleasesafe<T: AutoreleaseSafe>() {}
+ /// requires_autoreleasesafe::<()>();
+ /// requires_autoreleasesafe::<Box<Vec<i32>>>();
+ /// requires_autoreleasesafe::<fn(AutoreleasePool<'_>)>();
+ /// ```
+ ///
+ /// But [`AutoreleasePool`] isn't (if the `"unstable-autoreleasesafe"`
+ /// feature is enabled).
+ ///
+ #[cfg_attr(feature = "unstable-autoreleasesafe", doc = "```compile_fail,E0277")]
+ #[cfg_attr(not(feature = "unstable-autoreleasesafe"), doc = "```")]
+ /// use objc2::rc::AutoreleasePool;
+ /// # use objc2::rc::AutoreleaseSafe;
+ /// # fn requires_autoreleasesafe<T: AutoreleaseSafe>() {}
+ /// requires_autoreleasesafe::<AutoreleasePool<'static>>();
+ /// ```
+ ///
+ /// This also means that trait objects aren't (since they may contain an
+ /// [`AutoreleasePool`] internally):
+ ///
+ #[cfg_attr(feature = "unstable-autoreleasesafe", doc = "```compile_fail,E0277")]
+ #[cfg_attr(not(feature = "unstable-autoreleasesafe"), doc = "```")]
+ /// # use objc2::rc::AutoreleaseSafe;
+ /// # fn requires_autoreleasesafe<T: AutoreleaseSafe>() {}
+ /// requires_autoreleasesafe::<&dyn std::io::Write>();
+ /// ```
+ pub unsafe trait AutoreleaseSafe {}
+}
+
+#[cfg(not(feature = "unstable-autoreleasesafe"))]
+unsafe impl<T: ?Sized> AutoreleaseSafe for T {}
+
+#[cfg(feature = "unstable-autoreleasesafe")]
+impl !AutoreleaseSafe for Pool {}
+#[cfg(feature = "unstable-autoreleasesafe")]
+impl !AutoreleaseSafe for AutoreleasePool<'_> {}
+
+/// Execute `f` in the context of a new autorelease pool. The pool is drained
+/// after the execution of `f` completes.
+///
+/// This corresponds to `@autoreleasepool` blocks in Objective-C and Swift,
+/// see [Apple's documentation][apple-autorelease] for general information on
+/// when to use those.
+///
+/// [The pool] is passed as a parameter to the closure to give you a lifetime
+/// parameter that autoreleased objects can refer to.
+///
+/// Note that this is mostly useful for preventing leaks (as any Objective-C
+/// method may autorelease internally - see also [`autoreleasepool_leaking`]).
+/// If implementing an interface to an object, you should try to return
+/// retained pointers with [`msg_send!`] wherever you can instead, since
+/// it is usually more efficient, safer, and having to use this function can
+/// be quite cumbersome for users.
+///
+/// [apple-autorelease]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html
+/// [The pool]: AutoreleasePool
+/// [`msg_send!`]: crate::msg_send
+///
+///
+/// # Restrictions
+///
+/// The given parameter must not be used inside a nested `autoreleasepool`,
+/// since doing so will give the objects that it is used with an incorrect
+/// lifetime bound.
+///
+/// You can try to enable the `"unstable-autoreleasesafe"` Cargo feature using
+/// nightly Rust - if your use of this function compiles with that, it is more
+/// likely to be correct, though note that Rust does not have a way to express
+/// the lifetimes involved, so we cannot detect all invalid usage. See issue
+/// [#540] for details.
+///
+/// [#540]: https://github.com/madsmtm/objc2/issues/540
+///
+///
+/// # Examples
+///
+/// Use an external API, and ensure that the memory that it used is cleaned
+/// up afterwards.
+///
+/// ```
+/// use objc2::rc::autoreleasepool;
+/// # fn example_function() {}
+/// # #[cfg(for_illustrative_purposes)]
+/// use example_crate::example_function;
+///
+/// autoreleasepool(|_| {
+/// // Call `example_function` in the context of a pool
+/// example_function();
+/// }); // Memory released into the pool is cleaned up when the scope ends
+/// ```
+///
+/// Autorelease an object into an autorelease pool:
+///
+/// ```
+/// use objc2::rc::{autoreleasepool, Retained};
+/// use objc2::runtime::NSObject;
+///
+/// autoreleasepool(|pool| {
+/// // Create `obj` and autorelease it to the pool
+/// // SAFETY: The pool is the current innermost pool
+/// let obj = unsafe { Retained::autorelease(NSObject::new(), pool) };
+/// // We now have a reference that we can freely use
+/// println!("{obj:?}");
+/// }); // `obj` is deallocated when the pool ends
+/// // And is no longer usable outside the closure
+/// ```
+///
+/// Fails to compile because `obj` does not live long enough for us to take it
+/// out of the pool:
+///
+/// ```compile_fail
+/// use objc2::rc::{autoreleasepool, Retained};
+/// use objc2::runtime::NSObject;
+///
+/// let obj = autoreleasepool(|pool| unsafe {
+/// Retained::autorelease(NSObject::new(), pool)
+/// });
+/// ```
+///
+/// Fails to compile with the `"unstable-autoreleasesafe"` feature enabled, or
+/// panics with debug assertions enabled, because we tried to pass an outer
+/// pool to an inner pool:
+///
+#[cfg_attr(feature = "unstable-autoreleasesafe", doc = "```compile_fail,E0277")]
+#[cfg_attr(not(feature = "unstable-autoreleasesafe"), doc = "```should_panic")]
+/// use objc2::rc::{autoreleasepool, Retained};
+/// use objc2::runtime::NSObject;
+///
+/// autoreleasepool(|outer_pool| {
+/// let obj = autoreleasepool(|inner_pool| {
+/// // SAFETY: NOT safe, the pool is _not_ the innermost pool!
+/// unsafe { Retained::autorelease(NSObject::new(), outer_pool) }
+/// });
+/// // `obj` could wrongly be used here because its lifetime was
+/// // assigned to the outer pool, even though it was released by the
+/// // inner pool already.
+/// });
+/// #
+/// # panic!("Does not panic in release mode, so for testing we make it!");
+/// ```
+///
+/// It is impossible to extend the lifetime of the pool.
+///
+/// ```compile_fail,E0521
+/// use std::cell::RefCell;
+/// use objc2::rc::{autoreleasepool, AutoreleasePool};
+///
+/// thread_local! {
+/// static POOL: RefCell<Option<&'static AutoreleasePool<'static>>> = RefCell::new(None);
+/// }
+///
+/// autoreleasepool(|pool| {
+/// POOL.with(|p| {
+/// *p.borrow_mut() = Some(Box::leak(Box::new(pool)))
+/// });
+/// });
+/// ```
+#[doc(alias = "@autoreleasepool")]
+#[doc(alias = "objc_autoreleasePoolPush")]
+#[doc(alias = "objc_autoreleasePoolPop")]
+#[inline]
+pub fn autoreleasepool<T, F>(f: F) -> T
+where
+ for<'pool> F: AutoreleaseSafe + FnOnce(AutoreleasePool<'pool>) -> T,
+{
+ // SAFETY:
+ // - The `AutoreleaseSafe` bound on the closure ensures that no pool from
+ // a different "level" can be passed down through and used in this one.
+ // - The pools are guaranteed to be dropped in the reverse order they were
+ // created (since you can't possibly "interleave" closures).
+ //
+ // This would not work if we e.g. allowed users to create pools on the
+ // stack, since they could then safely control the drop order.
+ let pool = unsafe { Pool::new() };
+ let res = f(AutoreleasePool::new(Some(&pool)));
+ unsafe { pool.drain() };
+ res
+}
+
+/// Execute `f` in the context of a "fake" autorelease pool.
+///
+/// This is useful to create a context in which to use autoreleased objects,
+/// without the overhead of actually creating and draining the pool.
+///
+/// Any function boundary in Objective-C is an implicit autorelease pool, so
+/// there you'd do `id obj2 = [obj autorelease]` and be done with it - but we
+/// do this using a closure instead because we need some way to bind the
+/// lifetime of any objects released to the pool.
+///
+///
+/// # Examples
+///
+/// Autorelease an object to an outer pool, from inside an inner, "fake" pool.
+///
+/// ```
+/// use objc2::rc::{autoreleasepool, autoreleasepool_leaking, Retained};
+/// use objc2::runtime::NSObject;
+///
+/// autoreleasepool(|outer_pool| {
+/// let obj = autoreleasepool_leaking(|inner_pool| {
+/// // SAFETY: The given `outer_pool` is the actual innermost pool.
+/// unsafe { Retained::autorelease(NSObject::new(), outer_pool) }
+/// });
+/// // `obj` is still usable here, since the leaking pool doesn't actually
+/// // do anything.
+/// println!("{obj:?}");
+/// });
+///
+/// // But it is not usable here, since the outer pool has been closed
+/// ```
+///
+/// Like [`autoreleasepool`], you can't extend the lifetime of an object to
+/// outside the closure.
+///
+/// ```compile_fail
+/// use objc2::rc::{autoreleasepool_leaking, Retained};
+/// use objc2::runtime::NSObject;
+///
+/// let obj = autoreleasepool_leaking(|pool| unsafe {
+/// unsafe { Retained::autorelease(NSObject::new(), pool) }
+/// });
+/// ```
+///
+/// While you can pass an outer pool into this, you still can't pass the pool
+/// from this into [`autoreleasepool`]:
+///
+#[cfg_attr(feature = "unstable-autoreleasesafe", doc = "```compile_fail,E0277")]
+#[cfg_attr(not(feature = "unstable-autoreleasesafe"), doc = "```should_panic")]
+/// use objc2::rc::{autoreleasepool, autoreleasepool_leaking, Retained};
+/// use objc2::runtime::NSObject;
+///
+/// autoreleasepool_leaking(|outer_pool| {
+/// let obj = autoreleasepool(|inner_pool| {
+/// // SAFETY: NOT safe, the pool is _not_ the innermost pool!
+/// unsafe { Retained::autorelease(NSObject::new(), outer_pool) }
+/// });
+/// });
+/// #
+/// # panic!("Does not panic in release mode, so for testing we make it!");
+/// ```
+#[inline]
+pub fn autoreleasepool_leaking<T, F>(f: F) -> T
+where
+ for<'pool> F: FnOnce(AutoreleasePool<'pool>) -> T,
+{
+ // SAFETY: This is effectively what most Objective-C code does; they
+ // assume that there's an autorelease pool _somewhere_ in the call stack
+ // above it, and then use their autoreleased objects for a duration that
+ // is guaranteed to be shorter than that.
+ //
+ // The `AutoreleaseSafe` bound is not required, since we don't actually do
+ // anything inside this; hence if the user know they have the _actual_
+ // innermost pool, they may still safely use it to extend the lifetime
+ // beyond this closure.
+ f(AutoreleasePool::new(None))
+}
+
+#[cfg(test)]
+mod tests {
+ use core::mem;
+ use core::panic::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe};
+ use std::panic::catch_unwind;
+
+ use static_assertions::{assert_impl_all, assert_not_impl_any};
+
+ use super::{autoreleasepool, AutoreleasePool, AutoreleaseSafe};
+ use crate::rc::{RcTestObject, Retained, ThreadTestData};
+ use crate::runtime::AnyObject;
+
+ #[test]
+ fn auto_traits() {
+ assert_impl_all!(AutoreleasePool<'static>: Unpin, UnwindSafe, RefUnwindSafe);
+ assert_not_impl_any!(AutoreleasePool<'static>: Send, Sync);
+
+ assert_impl_all!(usize: AutoreleaseSafe);
+ assert_impl_all!(*mut AnyObject: AutoreleaseSafe);
+ assert_impl_all!(&mut AnyObject: AutoreleaseSafe);
+ #[cfg(feature = "unstable-autoreleasesafe")]
+ assert_not_impl_any!(AutoreleasePool<'static>: AutoreleaseSafe);
+ }
+
+ #[allow(unused)]
+ fn assert_covariant1<'a>(pool: AutoreleasePool<'static>) -> AutoreleasePool<'a> {
+ pool
+ }
+
+ #[allow(unused)]
+ fn assert_covariant2<'long: 'short, 'short>(
+ pool: AutoreleasePool<'long>,
+ ) -> AutoreleasePool<'short> {
+ pool
+ }
+
+ #[allow(unused)]
+ fn assert_object_safe(_: &dyn AutoreleaseSafe) {}
+
+ #[cfg_attr(
+ not(feature = "unstable-autoreleasesafe"),
+ ignore = "only stably ZST when `unstable-autoreleasesafe` is enabled"
+ )]
+ #[test]
+ fn assert_zst() {
+ assert_eq!(mem::size_of::<AutoreleasePool<'static>>(), 0);
+ }
+
+ #[test]
+ #[cfg_attr(panic = "abort", ignore = "requires `catch_unwind`")]
+ #[cfg_attr(
+ all(target_os = "macos", target_arch = "x86", not(panic = "abort")),
+ ignore = "unwinding through an auto release pool on macOS 32 bit won't pop the pool"
+ )]
+ fn test_unwind_still_autoreleases() {
+ let obj = RcTestObject::new();
+ let mut expected = ThreadTestData::current();
+
+ catch_unwind({
+ let obj = AssertUnwindSafe(obj);
+ let expected = AssertUnwindSafe(&mut expected);
+ || {
+ let obj = obj;
+ let mut expected = expected;
+
+ autoreleasepool(|pool| {
+ let _autoreleased = unsafe { Retained::autorelease(obj.0, pool) };
+ expected.autorelease += 1;
+ expected.assert_current();
+ panic!("unwind");
+ });
+ }
+ })
+ .unwrap_err();
+
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+ }
+}
diff --git a/third_party/rust/objc2/src/rc/mod.rs b/third_party/rust/objc2/src/rc/mod.rs
@@ -0,0 +1,75 @@
+//! # Reference counting utilities.
+//!
+//! The types in this module provide roughly the same benefits as ARC
+//! (Automatic Reference Counting) does to Objective-C.
+//!
+//! Most importantly, a smart pointer [`Retained`] is provided to ensure that
+//! objects are correctly retained and released when created and dropped,
+//! respectively.
+//!
+//! Weak references may be created using the [`Weak`] struct; these will not
+//! retain the object, but one can attempt to load them and obtain an `Retained`, or
+//! safely fail if the object has been deallocated.
+//!
+//! See [the clang documentation][clang-arc] and [the Apple article on memory
+//! management][mem-mgmt] (similar document exists [for Core Foundation][cf])
+//! for more information on automatic and manual reference counting.
+//!
+//! It can also be useful to [enable Malloc Debugging][mem-debug] if you're trying
+//! to figure out if/where your application has memory errors and leaks.
+//!
+//! [clang-arc]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html
+//! [mem-mgmt]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html
+//! [cf]: https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/CFMemoryMgmt.html
+//! [mem-debug]: https://developer.apple.com/library/archive/documentation/Performance/Conceptual/ManagingMemory/Articles/MallocDebug.html
+//!
+//!
+//! ## Example
+//!
+//! ```
+//! use objc2::rc::{autoreleasepool, Retained, Weak};
+//! use objc2::runtime::NSObject;
+//!
+//! // Allocate and initialize a new `NSObject`.
+//! // `Retained` will release the object when dropped.
+//! let obj: Retained<NSObject> = NSObject::new();
+//!
+//! // Cloning retains the object an additional time
+//! let cloned = obj.clone();
+//! autoreleasepool(|pool| {
+//! // Autorelease consumes the Retained, but won't actually
+//! // release it until the end of the autoreleasepool
+//! // SAFETY: The given is the innermost pool.
+//! let obj_ref: &NSObject = unsafe { Retained::autorelease(cloned, pool) };
+//! });
+//!
+//! // Weak references won't retain the object
+//! let weak = Weak::from_retained(&obj);
+//! drop(obj);
+//! assert!(weak.load().is_none());
+//! ```
+
+mod allocated_partial_init;
+mod autorelease;
+mod retained;
+mod retained_forwarding_impls;
+mod retained_traits;
+#[cfg(test)]
+mod test_object;
+mod weak;
+
+pub use self::allocated_partial_init::{Allocated, PartialInit};
+pub use self::autorelease::{
+ autoreleasepool, autoreleasepool_leaking, AutoreleasePool, AutoreleaseSafe,
+};
+// Re-export `Id` for backwards compatibility, but still mark it as deprecated.
+#[allow(deprecated)]
+pub use self::retained::Id;
+pub use self::retained::Retained;
+pub use self::retained_traits::{DefaultRetained, RetainedFromIterator, RetainedIntoIterator};
+#[cfg(test)]
+pub(crate) use self::test_object::{RcTestObject, ThreadTestData};
+pub use self::weak::Weak;
+// Same as above.
+#[allow(deprecated)]
+pub use self::weak::WeakId;
diff --git a/third_party/rust/objc2/src/rc/retained.rs b/third_party/rust/objc2/src/rc/retained.rs
@@ -0,0 +1,1068 @@
+use core::fmt;
+use core::marker::PhantomData;
+use core::mem::ManuallyDrop;
+use core::ops::Deref;
+use core::panic::{RefUnwindSafe, UnwindSafe};
+use core::ptr::{self, NonNull};
+
+use super::AutoreleasePool;
+use crate::runtime::{objc_release_fast, objc_retain_fast, AnyObject, ProtocolObject};
+use crate::{ffi, ClassType, DowncastTarget, Message};
+
+/// A reference counted pointer type for Objective-C objects.
+///
+/// [`Retained`] strongly references or "retains" the given object `T`, and
+/// decrements the retain count or "releases" it again when dropped, thereby
+/// ensuring it will be deallocated at the right time.
+///
+/// The type `T` inside `Retained<T>` can be anything that implements
+/// [`Message`].
+///
+/// This can usually be gotten from one of the methods in [the framework
+/// crates], but can also be created manually with the [`msg_send!`] macro, or
+/// even more manually with the [`Retained::retain`], [`Retained::from_raw`]
+/// and [`Retained::retain_autoreleased`] methods.
+///
+/// [the framework crates]: crate::topics::about_generated
+/// [`msg_send!`]: crate::msg_send
+///
+///
+/// # Comparison to `std` types
+///
+/// `Retained<T>` is the Objective-C equivalent of [`Arc`], that is, it is a
+/// thread-safe reference-counting pointer, and allows cloning by bumping the
+/// reference count, and weak references using [`rc::Weak`].
+///
+/// Unlike `Arc`, objects can be retained directly from a `&T` using
+/// [`Message::retain`] (for `Arc` you need `&Arc<T>`).
+///
+/// Even though most Objective-C types aren't thread safe, Objective-C has no
+/// concept of [`Rc`]. Retain/release operations are always atomic.
+///
+/// [`Arc`]: alloc::sync::Arc
+/// [`rc::Weak`]: crate::rc::Weak
+/// [`Rc`]: std::rc::Rc
+///
+///
+/// # Forwarding implementations
+///
+/// Since `Retained<T>` is a smart pointer, it [`Deref`]s to `T`.
+///
+/// It also forwards the implementation of a bunch of standard library traits
+/// such as [`PartialEq`], [`AsRef`], and so on, so that it becomes possible
+/// to use e.g. `Retained<NSString>` as if it was `NSString`. Note that having
+/// `NSString` directly is not possible since Objective-C objects cannot live
+/// on the stack, but instead must reside on the heap, and as such must be
+/// accessed behind a pointer or a reference (i.e. `&NSString`).
+///
+/// Note that because of current limitations in the Rust trait system, some
+/// traits like [`Default`], [`IntoIterator`], [`FromIterator`], [`From`] and
+/// [`Into`] are not directly implementable on `NSString`; for that use-case,
+/// we instead provide the [`DefaultRetained`], [`RetainedIntoIterator`] and
+/// [`RetainedFromIterator`] traits, which make some of the the aforementioned
+/// traits implementable on `Retained`.
+///
+/// [`DefaultRetained`]: crate::rc::DefaultRetained
+/// [`RetainedIntoIterator`]: crate::rc::RetainedIntoIterator
+/// [`RetainedFromIterator`]: crate::rc::RetainedFromIterator
+///
+///
+/// # Memory layout
+///
+/// This is guaranteed to have the same size and alignment as a pointer to the
+/// object, `*const T`.
+///
+/// Additionally, it participates in the null-pointer optimization, that is,
+/// `Option<Retained<T>>` is guaranteed to have the same size as
+/// `Retained<T>`.
+///
+///
+/// # Example
+///
+/// Various usage of `Retained` on an immutable object.
+///
+/// ```
+/// # use objc2::runtime::NSObject;
+/// # #[cfg(available_in_foundation)]
+/// use objc2_foundation::{NSObject, NSString};
+/// use objc2::rc::Retained;
+/// use objc2::{ClassType, msg_send};
+/// #
+/// # objc2::extern_class!(
+/// # #[unsafe(super(NSObject))]
+/// # pub struct NSString;
+/// # );
+///
+/// // Use `msg_send!` to create an `Retained` with correct memory management
+/// //
+/// // SAFETY: The types are correct, and it is safe to call the `new`
+/// // selector on `NSString`.
+/// let string: Retained<NSString> = unsafe { msg_send![NSString::class(), new] };
+/// // Or:
+/// // let string = NSString::new();
+///
+/// // Methods on `NSString` is usable via `Deref`
+/// # #[cfg(available_in_foundation)]
+/// assert_eq!(string.length(), 0);
+///
+/// // Bump the reference count of the object.
+/// let another_ref: Retained<NSString> = string.clone();
+///
+/// // Convert one of the references to a reference to `NSObject` instead
+/// let obj: Retained<NSObject> = string.into_super();
+///
+/// // And use the `Debug` impl from that
+/// assert_eq!(format!("{obj:?}"), "");
+///
+/// // Finally, the `Retained`s go out of scope, the reference counts are
+/// // decreased, and the string will deallocate
+/// ```
+#[repr(transparent)]
+#[doc(alias = "id")]
+#[doc(alias = "Id")] // Previous name
+#[doc(alias = "StrongPtr")]
+#[cfg_attr(
+ feature = "unstable-coerce-pointee",
+ derive(std::marker::CoercePointee)
+)]
+// TODO: Add `ptr::Thin` bound on `T` to allow for only extern types
+pub struct Retained<T: ?Sized> {
+ /// A pointer to the contained object. The pointer is always retained.
+ ///
+ /// It is important that this is `NonNull`, since we want to dereference
+ /// it later, and be able to use the null-pointer optimization.
+ ///
+ /// Additionally, covariance is correct because we're either the unique
+ /// owner of `T`, or `T` is immutable.
+ ptr: NonNull<T>,
+ /// Necessary for dropck even though we never actually run T's destructor,
+ /// because it might have a `dealloc` that assumes that contained
+ /// references outlive the type.
+ ///
+ /// See <https://doc.rust-lang.org/nightly/nomicon/phantom-data.html>
+ item: PhantomData<T>,
+ /// Marks the type as !UnwindSafe. Later on we'll re-enable this.
+ ///
+ /// See <https://github.com/rust-lang/rust/issues/93367> for why this is
+ /// required.
+ notunwindsafe: PhantomData<&'static mut ()>,
+}
+
+/// Short type-alias to [`Retained`].
+///
+/// This is fully deprecated since `v0.6.0`, use [`Retained`] instead.
+#[deprecated(since = "0.6.0", note = "Renamed to `Retained`.")]
+pub type Id<T> = Retained<T>;
+
+impl<T: ?Sized> Retained<T> {
+ #[inline]
+ pub(crate) unsafe fn new_nonnull(ptr: NonNull<T>) -> Self {
+ Self {
+ ptr,
+ item: PhantomData,
+ notunwindsafe: PhantomData,
+ }
+ }
+}
+
+impl<T: ?Sized + Message> Retained<T> {
+ /// Construct an [`Retained`] from a pointer that already has +1 retain count.
+ ///
+ /// Returns `None` if the pointer was NULL.
+ ///
+ /// This is useful when you have a retain count that has been handed off
+ /// from somewhere else, usually Objective-C methods like `init`, `alloc`,
+ /// `new`, `copy`, or methods with the `ns_returns_retained` attribute.
+ ///
+ /// If you do not have +1 retain count, such as if your object was
+ /// retrieved from other methods than the ones noted above, use
+ /// [`Retained::retain`] instead.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// You must uphold the same requirements as described in [`Retained::retain`].
+ ///
+ /// Additionally, you must ensure the given object pointer has +1 retain
+ /// count.
+ ///
+ ///
+ /// # Example
+ ///
+ /// Comparing different ways of creating a new `NSObject`.
+ ///
+ /// ```
+ /// use objc2::rc::Retained;
+ /// use objc2::runtime::NSObject;
+ /// use objc2::{msg_send, AnyThread, ClassType};
+ ///
+ /// // Manually using `msg_send!`, pointers and `Retained::from_raw`
+ /// let obj: *mut NSObject = unsafe { msg_send![NSObject::class(), alloc] };
+ /// let obj: *mut NSObject = unsafe { msg_send![obj, init] };
+ /// // SAFETY: `-[NSObject init]` returns +1 retain count
+ /// let obj: Retained<NSObject> = unsafe { Retained::from_raw(obj).unwrap() };
+ ///
+ /// // Or automatically by specifying `Retained` as the return value from
+ /// // `msg_send!` (it will do the correct conversion internally).
+ /// let obj: Retained<NSObject> = unsafe { msg_send![NSObject::alloc(), init] };
+ ///
+ /// // Or using the `NSObject::new` method
+ /// let obj = NSObject::new();
+ /// ```
+ #[inline]
+ // Note: We don't take a reference as a parameter since it would be too
+ // easy to accidentally create two aliasing mutable references.
+ pub unsafe fn from_raw(ptr: *mut T) -> Option<Self> {
+ // Should optimize down to a noop.
+ // SAFETY: Upheld by the caller
+ NonNull::new(ptr).map(|ptr| unsafe { Retained::new_nonnull(ptr) })
+ }
+
+ /// Deprecated alias for [`Retained::from_raw`], see that for details.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// Same as [`Retained::from_raw`].
+ #[deprecated = "use the more descriptive name `Retained::from_raw` instead"]
+ #[inline]
+ pub unsafe fn new(ptr: *mut T) -> Option<Self> {
+ // SAFETY: Upheld by caller
+ unsafe { Self::from_raw(ptr) }
+ }
+
+ /// Consumes the `Retained`, returning a raw pointer with +1 retain count.
+ ///
+ /// After calling this function, the caller is responsible for the memory
+ /// previously managed by the `Retained`.
+ ///
+ /// This is effectively the opposite of [`Retained::from_raw`], see that for
+ /// more details on when this function is useful.
+ ///
+ ///
+ /// # Examples
+ ///
+ /// Converting an `Retained` to a pointer and back.
+ ///
+ /// ```
+ /// use objc2::rc::Retained;
+ /// use objc2::runtime::NSObject;
+ ///
+ /// let obj = NSObject::new();
+ /// let ptr = Retained::into_raw(obj);
+ /// // SAFETY: The pointer is valid, and has +1 retain count from above.
+ /// let obj = unsafe { Retained::from_raw(ptr) }.unwrap();
+ /// ```
+ #[inline]
+ pub fn into_raw(this: Self) -> *mut T {
+ ManuallyDrop::new(this).ptr.as_ptr()
+ }
+
+ /// Returns a raw pointer to the object.
+ ///
+ /// The pointer is valid for at least as long as the `Retained` is held.
+ ///
+ /// This is an associated method, and must be called as `Retained::as_ptr(obj)`.
+ #[inline]
+ pub fn as_ptr(this: &Self) -> *const T {
+ this.ptr.as_ptr()
+ }
+
+ #[inline]
+ pub(crate) fn as_nonnull_ptr(&self) -> NonNull<T> {
+ self.ptr
+ }
+
+ #[inline]
+ pub(crate) fn consume_as_ptr_option(this: Option<Self>) -> *mut T
+ where
+ T: Sized,
+ {
+ this.map(|this| Retained::into_raw(this))
+ .unwrap_or_else(ptr::null_mut)
+ }
+}
+
+// TODO: Add ?Sized bound
+impl<T: Message> Retained<T> {
+ /// Attempt to downcast the object to a class of type `U`.
+ ///
+ /// See [`AnyObject::downcast_ref`] for more details.
+ ///
+ /// # Errors
+ ///
+ /// If casting failed, this will return the object back as the [`Err`]
+ /// type. If you do not care about this, and just want an [`Option`], use
+ /// `.downcast().ok()`.
+ ///
+ /// # Example
+ ///
+ /// Cast a string to an object, and back again.
+ ///
+ /// ```
+ /// use objc2_foundation::{NSString, NSObject};
+ ///
+ /// let string = NSString::new();
+ /// // The string is an object
+ /// let obj = string.downcast::<NSObject>().unwrap();
+ /// // And it is also a string
+ /// let string = obj.downcast::<NSString>().unwrap();
+ /// ```
+ ///
+ /// Try to cast an object to a string, which will fail and return the
+ /// object in [`Err`].
+ ///
+ /// ```
+ /// use objc2_foundation::{NSString, NSObject};
+ ///
+ /// let obj = NSObject::new();
+ /// let obj = obj.downcast::<NSString>().unwrap_err();
+ /// ```
+ //
+ // NOTE: This is _not_ an associated method, since we want it to be easy
+ // to call, and it does not conflict with `AnyObject::downcast_ref`.
+ #[inline]
+ pub fn downcast<U: DowncastTarget>(self) -> Result<Retained<U>, Retained<T>>
+ where
+ Self: 'static,
+ {
+ let ptr: *const AnyObject = Self::as_ptr(&self).cast();
+ // SAFETY: All objects are valid to re-interpret as `AnyObject`, even
+ // if the object has a lifetime (which it does not in our case).
+ let obj: &AnyObject = unsafe { &*ptr };
+
+ if obj.is_kind_of_class(U::class()).as_bool() {
+ // SAFETY: Just checked that the object is a class of type `U`,
+ // and `T` is `'static`.
+ //
+ // Generic `U` like `NSArray<NSString>` are ruled out by
+ // `U: DowncastTarget`.
+ Ok(unsafe { Self::cast_unchecked::<U>(self) })
+ } else {
+ Err(self)
+ }
+ }
+
+ /// Convert the type of the given object to another.
+ ///
+ /// This is equivalent to a `cast` between two pointers.
+ ///
+ /// See [`Retained::into_super`], [`ProtocolObject::from_retained`] and
+ /// [`Retained::downcast`] for safe alternatives.
+ ///
+ /// This is common to do when you know that an object is a subclass of
+ /// a specific class (e.g. casting an instance of `NSString` to `NSObject`
+ /// is safe because `NSString` is a subclass of `NSObject`), but do not
+ /// want to pay the (very slight) performance price of dynamically
+ /// checking that precondition with a [`downcast`].
+ ///
+ /// All `'static` objects can safely be cast to [`AnyObject`], since that
+ /// assumes no specific class.
+ ///
+ /// This is an associated method, and must be called as
+ /// `Retained::cast_unchecked(obj)`.
+ ///
+ /// [`AnyObject`]: crate::runtime::AnyObject
+ /// [`ProtocolObject::from_retained`]: crate::runtime::ProtocolObject::from_retained
+ /// [`downcast`]: Self::downcast
+ ///
+ ///
+ /// # Safety
+ ///
+ /// You must ensure that the object can be reinterpreted as the given
+ /// type.
+ ///
+ /// If `T` is not `'static`, you must ensure that `U` ensures that the
+ /// data contained by `T` is kept alive for as long as `U` lives.
+ ///
+ /// Additionally, you must ensure that any safety invariants that the new
+ /// type has are upheld.
+ #[inline]
+ pub unsafe fn cast_unchecked<U: Message>(this: Self) -> Retained<U> {
+ let ptr = ManuallyDrop::new(this).ptr.cast();
+ // SAFETY: The object is forgotten, so we have +1 retain count.
+ //
+ // Caller verifies that the returned object is of the correct type.
+ unsafe { Retained::new_nonnull(ptr) }
+ }
+
+ /// Deprecated alias of [`Retained::cast_unchecked`].
+ ///
+ /// # Safety
+ ///
+ /// See [`Retained::cast_unchecked`].
+ #[inline]
+ #[deprecated = "Use `downcast`, or `cast_unchecked` instead"]
+ pub unsafe fn cast<U: Message>(this: Self) -> Retained<U> {
+ unsafe { Self::cast_unchecked(this) }
+ }
+
+ /// Retain the pointer and construct an [`Retained`] from it.
+ ///
+ /// Returns `None` if the pointer was NULL.
+ ///
+ /// This is useful when you have been given a pointer to an object from
+ /// some API, and you would like to ensure that the object stays around
+ /// while you work on it.
+ ///
+ /// For normal Objective-C methods, you may want to use
+ /// [`Retained::retain_autoreleased`] instead, as that is usually more
+ /// performant.
+ ///
+ /// See also [`Message::retain`] for a safe alternative where you already
+ /// have a reference to the object.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The pointer must be valid as a reference (aligned, dereferenceable and
+ /// initialized, see the [`std::ptr`] module for more information), or
+ /// NULL.
+ ///
+ /// You must ensure that any data that `T` may reference lives for at
+ /// least as long as `T`.
+ ///
+ /// [`std::ptr`]: core::ptr
+ #[doc(alias = "objc_retain")]
+ #[inline]
+ pub unsafe fn retain(ptr: *mut T) -> Option<Retained<T>> {
+ // SAFETY: The caller upholds that the pointer is valid
+ let res: *mut T = unsafe { objc_retain_fast(ptr.cast()) }.cast();
+ debug_assert_eq!(res, ptr, "objc_retain did not return the same pointer");
+ // SAFETY: We just retained the object, so it has +1 retain count
+ unsafe { Self::from_raw(res) }
+ }
+
+ /// Retains a previously autoreleased object pointer.
+ ///
+ /// This is useful when calling Objective-C methods that return
+ /// autoreleased objects, see [Cocoa's Memory Management Policy][mmRules].
+ ///
+ /// This has exactly the same semantics as [`Retained::retain`], except it can
+ /// sometimes avoid putting the object into the autorelease pool, possibly
+ /// yielding increased speed and reducing memory pressure.
+ ///
+ /// Note: This relies heavily on being inlined right after [`msg_send!`],
+ /// be careful to not accidentally require instructions between these.
+ ///
+ /// [mmRules]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html
+ /// [`msg_send!`]: crate::msg_send
+ ///
+ ///
+ /// # Safety
+ ///
+ /// Same as [`Retained::retain`].
+ #[doc(alias = "objc_retainAutoreleasedReturnValue")]
+ #[inline]
+ pub unsafe fn retain_autoreleased(ptr: *mut T) -> Option<Retained<T>> {
+ // Add magic nop instruction to participate in the fast autorelease
+ // scheme.
+ //
+ // See `callerAcceptsOptimizedReturn` in `objc-object.h`:
+ // https://github.com/apple-oss-distributions/objc4/blob/objc4-838/runtime/objc-object.h#L1209-L1377
+ //
+ // We will unconditionally emit these instructions, even if they end
+ // up being unused (for example because we're unlucky with inlining,
+ // some other work is done between the objc_msgSend and this, or the
+ // runtime version is too old to support it).
+ //
+ // It may seem like there should be a better way to do this, but
+ // emitting raw assembly is exactly what Clang and Swift does:
+ // swiftc: https://github.com/apple/swift/blob/swift-5.5.3-RELEASE/lib/IRGen/GenObjC.cpp#L148-L173
+ // Clang: https://github.com/llvm/llvm-project/blob/889317d47b7f046cf0e68746da8f7f264582fb5b/clang/lib/CodeGen/CGObjC.cpp#L2339-L2373
+ //
+ // Note that LLVM may sometimes insert extra instructions between the
+ // assembly and the `objc_retainAutoreleasedReturnValue` call,
+ // especially when doing tail calls and it needs to clean up the
+ // function frame. Unsure how to avoid this in a performant manner?
+ // Maybe force not doing tail calls by inserting assembly to do the
+ // call manually?
+ //
+ // Resources:
+ // - https://www.mikeash.com/pyblog/friday-qa-2011-09-30-automatic-reference-counting.html
+ // - https://www.galloway.me.uk/2012/02/how-does-objc_retainautoreleasedreturnvalue-work/
+ // - https://github.com/gfx-rs/metal-rs/issues/222
+ // - https://news.ycombinator.com/item?id=29311736
+ // - https://stackoverflow.com/a/23765612
+ //
+ // SAFETY:
+ // Based on https://doc.rust-lang.org/stable/reference/inline-assembly.html#rules-for-inline-assembly
+ //
+ // We don't care about the value of the register (so it's okay to be
+ // undefined), and its value is preserved.
+ //
+ // nomem: No reads or writes to memory are performed (this `mov`
+ // operates entirely on registers).
+ // preserves_flags: `mov` doesn't modify any flags.
+ // nostack: We don't touch the stack.
+
+ // Only worth doing on the Apple runtime.
+ // Not supported on TARGET_OS_WIN32.
+ #[cfg(target_vendor = "apple")]
+ {
+ // Supported since macOS 10.7.
+ #[cfg(target_arch = "x86_64")]
+ {
+ // x86_64 looks at the next call instruction.
+ //
+ // This is expected to be a PLT entry - if the user specifies
+ // `-Zplt=no`, a GOT entry will be created instead, and this
+ // will not work.
+ }
+
+ // Supported since macOS 10.8.
+ #[cfg(target_arch = "arm")]
+ unsafe {
+ core::arch::asm!("mov r7, r7", options(nomem, preserves_flags, nostack))
+ };
+
+ // Supported since macOS 10.10.
+ //
+ // On macOS 13.0 / iOS 16.0 / tvOS 16.0 / watchOS 9.0, the runtime
+ // instead checks the return pointer address, so we no longer need
+ // to emit these extra instructions, see this video from WWDC22:
+ // https://developer.apple.com/videos/play/wwdc2022/110363/
+ #[cfg(all(target_arch = "aarch64", not(feature = "unstable-apple-new")))]
+ unsafe {
+ // Same as `mov x29, x29`.
+ core::arch::asm!("mov fp, fp", options(nomem, preserves_flags, nostack))
+ };
+
+ // Supported since macOS 10.12.
+ #[cfg(target_arch = "x86")]
+ unsafe {
+ core::arch::asm!("mov ebp, ebp", options(nomem, preserves_flags, nostack))
+ };
+ }
+
+ // SAFETY: Same as `Retained::retain`, this is just an optimization.
+ let res: *mut T = unsafe { ffi::objc_retainAutoreleasedReturnValue(ptr.cast()) }.cast();
+
+ // Ideally, we'd be able to specify that the above call should never
+ // be tail-call optimized (become a `jmp` instruction instead of a
+ // `call`); Rust doesn't really have a way of doing this currently, so
+ // we emit a `nop` to make such tail-call optimizations less likely to
+ // occur.
+ //
+ // This is brittle! We should find a better solution!
+ #[cfg(all(target_vendor = "apple", target_arch = "x86_64"))]
+ {
+ // SAFETY: Similar to above.
+ unsafe { core::arch::asm!("nop", options(nomem, preserves_flags, nostack)) };
+ // TODO: Possibly more efficient alternative? Also consider PLT.
+ // #![feature(asm_sym)]
+ // core::arch::asm!(
+ // "mov rdi, rax",
+ // "call {}",
+ // sym objc2::ffi::objc_retainAutoreleasedReturnValue,
+ // inout("rax") obj,
+ // clobber_abi("C-unwind"),
+ // );
+ }
+
+ debug_assert_eq!(
+ res, ptr,
+ "objc_retainAutoreleasedReturnValue did not return the same pointer"
+ );
+
+ // SAFETY: Same as `Retained::retain`.
+ unsafe { Self::from_raw(res) }
+ }
+
+ /// Autoreleases the [`Retained`], returning a pointer.
+ ///
+ /// The object is not immediately released, but will be when the innermost
+ /// / current autorelease pool is drained.
+ ///
+ /// This is useful when defining your own classes and you have some error
+ /// parameter passed as `Option<&mut *mut NSError>`, and you want to
+ /// create and autorelease an error before returning.
+ ///
+ /// This is an associated method, and must be called as
+ /// `Retained::autorelease_ptr(obj)`.
+ ///
+ /// # Safety
+ ///
+ /// This method is safe to call, but the returned pointer is only
+ /// guaranteed to be valid until the innermost autorelease pool is
+ /// drained.
+ #[doc(alias = "objc_autorelease")]
+ #[must_use = "if you don't intend to use the object any more, drop it as usual"]
+ #[inline]
+ pub fn autorelease_ptr(this: Self) -> *mut T {
+ let ptr = ManuallyDrop::new(this).ptr.as_ptr();
+ // SAFETY:
+ // - The `ptr` is guaranteed to be valid and have at least one
+ // retain count.
+ // - Because of the ManuallyDrop, we don't call the Drop
+ // implementation, so the object won't also be released there.
+ let res: *mut T = unsafe { ffi::objc_autorelease(ptr.cast()) }.cast();
+ debug_assert_eq!(res, ptr, "objc_autorelease did not return the same pointer");
+ res
+ }
+
+ /// Autoreleases the [`Retained`], returning a reference bound to the pool.
+ ///
+ /// The object is not immediately released, but will be when the innermost
+ /// / current autorelease pool (given as a parameter) is drained.
+ ///
+ /// This is an associated method, and must be called as
+ /// `Retained::autorelease(obj, pool)`.
+ ///
+ /// # Safety
+ ///
+ /// The given pool must represent the innermost pool, to ensure that the
+ /// reference is not moved outside the autorelease pool into which it has
+ /// been put in.
+ #[doc(alias = "objc_autorelease")]
+ #[must_use = "if you don't intend to use the object any more, drop it as usual"]
+ #[inline]
+ #[allow(clippy::needless_lifetimes)]
+ pub unsafe fn autorelease<'p>(this: Self, pool: AutoreleasePool<'p>) -> &'p T {
+ let ptr = Self::autorelease_ptr(this);
+ // SAFETY: The pointer is valid as a reference
+ unsafe { pool.ptr_as_ref(ptr) }
+ }
+
+ #[inline]
+ pub(crate) fn autorelease_return_option(this: Option<Self>) -> *mut T {
+ let ptr: *mut T = this
+ .map(|this| ManuallyDrop::new(this).ptr.as_ptr())
+ .unwrap_or_else(ptr::null_mut);
+
+ // SAFETY: Same as `autorelease_inner`, this is just an optimization.
+ let res: *mut T = unsafe { ffi::objc_autoreleaseReturnValue(ptr.cast()) }.cast();
+ debug_assert_eq!(
+ res, ptr,
+ "objc_autoreleaseReturnValue did not return the same pointer"
+ );
+ res
+ }
+
+ /// Autoreleases and prepares the [`Retained`] to be returned to Objective-C.
+ ///
+ /// The object is not immediately released, but will be when the innermost
+ /// autorelease pool is drained.
+ ///
+ /// This is useful when [defining your own methods][classbuilder] where
+ /// you will often find yourself in need of returning autoreleased objects
+ /// to properly follow [Cocoa's Memory Management Policy][mmRules].
+ ///
+ /// To that end, you could also use [`Retained::autorelease_ptr`], but
+ /// this is more efficient than a normal `autorelease`, since it makes a
+ /// best effort attempt to hand off ownership of the retain count to a
+ /// subsequent call to `objc_retainAutoreleasedReturnValue` /
+ /// [`Retained::retain_autoreleased`] in the enclosing call frame.
+ ///
+ /// This optimization relies heavily on this function being tail called,
+ /// so make sure you only call this function at the end of your method.
+ ///
+ /// [classbuilder]: crate::runtime::ClassBuilder
+ /// [mmRules]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html
+ ///
+ ///
+ /// # Example
+ ///
+ /// Returning an `Retained` from a custom method (note: the
+ /// [`define_class!`] macro supports doing this for you automatically).
+ ///
+ /// ```
+ /// use objc2::{class, msg_send, sel};
+ /// use objc2::rc::Retained;
+ /// use objc2::runtime::{AnyClass, AnyObject, ClassBuilder, Sel};
+ ///
+ /// let mut builder = ClassBuilder::new(c"ExampleObject", class!(NSObject)).unwrap();
+ ///
+ /// extern "C-unwind" fn get(cls: &AnyClass, _cmd: Sel) -> *mut AnyObject {
+ /// let obj: Retained<AnyObject> = unsafe { msg_send![cls, new] };
+ /// Retained::autorelease_return(obj)
+ /// }
+ ///
+ /// unsafe {
+ /// builder.add_class_method(
+ /// sel!(get),
+ /// get as extern "C-unwind" fn(_, _) -> _,
+ /// );
+ /// }
+ ///
+ /// let cls = builder.register();
+ /// ```
+ ///
+ /// [`define_class!`]: crate::define_class
+ #[doc(alias = "objc_autoreleaseReturnValue")]
+ #[must_use = "if you don't intend to use the object any more, drop it as usual"]
+ #[inline]
+ pub fn autorelease_return(this: Self) -> *mut T {
+ Self::autorelease_return_option(Some(this))
+ }
+}
+
+impl<T: ClassType + 'static> Retained<T>
+where
+ T::Super: 'static,
+{
+ /// Convert the object into its superclass.
+ //
+ // NOTE: This is _not_ an associated method, since we want it to be easy
+ // to call, and it it unlikely to conflict with anything (the reference
+ // version is called `ClassType::as_super`).
+ #[inline]
+ pub fn into_super(self) -> Retained<T::Super> {
+ // SAFETY:
+ // - The casted-to type is a superclass of the type.
+ // - Both types are `'static`, so no lifetime information is lost
+ // (this could maybe be relaxed a bit, but let's be on the safe side
+ // for now).
+ unsafe { Self::cast_unchecked::<T::Super>(self) }
+ }
+}
+
+// TODO: Add ?Sized bound
+impl<T: Message> Clone for Retained<T> {
+ /// Retain the object, increasing its reference count.
+ ///
+ /// This is equivalent to [`Message::retain`].
+ #[doc(alias = "objc_retain")]
+ #[doc(alias = "retain")]
+ #[inline]
+ fn clone(&self) -> Self {
+ self.retain()
+ }
+}
+
+/// `#[may_dangle]` (see [this][dropck_eyepatch]) doesn't apply here since we
+/// don't run `T`'s destructor (rather, we want to discourage having `T`s with
+/// a destructor); and even if we did run the destructor, it would not be safe
+/// to add since we cannot verify that a `dealloc` method doesn't access
+/// borrowed data.
+///
+/// [dropck_eyepatch]: https://doc.rust-lang.org/nightly/nomicon/dropck.html#an-escape-hatch
+impl<T: ?Sized> Drop for Retained<T> {
+ /// Releases the retained object.
+ ///
+ /// The contained object's destructor (`Drop` impl, if it has one) is
+ /// never run - override the `dealloc` method instead (which
+ /// `define_class!` does for you).
+ #[doc(alias = "objc_release")]
+ #[doc(alias = "release")]
+ #[inline]
+ fn drop(&mut self) {
+ // We could technically run the destructor for `T` when it is mutable,
+ // but that would be confusing and inconsistent since we cannot really
+ // guarantee that it is run if the `Retained<T>` is passed to Objective-C.
+
+ // SAFETY: The `ptr` is guaranteed to be valid and have at least one
+ // retain count.
+ unsafe { objc_release_fast(self.ptr.as_ptr().cast()) };
+ }
+}
+
+impl<T: ?Sized> Deref for Retained<T> {
+ type Target = T;
+
+ /// Obtain an immutable reference to the object.
+ // Box doesn't inline, but that's because it's a compiler built-in
+ #[inline]
+ fn deref(&self) -> &T {
+ // SAFETY: The pointer's validity is verified when the type is
+ // created.
+ unsafe { self.ptr.as_ref() }
+ }
+}
+
+impl<T: ?Sized> fmt::Pointer for Retained<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Pointer::fmt(&self.ptr.as_ptr(), f)
+ }
+}
+
+// Sadly, it is not possible to implement general conversions between
+// `Retained`, as it conflicts with the generic `impl From<T> for T`.
+//
+// impl<T: Upcast, U> From<Retained<U>> for Retained<T> {
+// fn from(obj: &Retained<T>) -> Self {
+// obj.as_super().retain()
+// }
+// }
+//
+// But we _can_ do the following implementations:
+
+impl<T: ?Sized + AsRef<U>, U: Message> From<&T> for Retained<U> {
+ /// Cast the object to its superclass, and retain it.
+ #[inline]
+ fn from(obj: &T) -> Self {
+ obj.as_ref().retain()
+ }
+}
+
+// Bounded by `T: ClassType` to prevent overlapping impls
+// (`AnyObject` implements `Message`).
+impl<T: ClassType + 'static> From<Retained<T>> for Retained<AnyObject> {
+ /// Convert the object to `AnyObject`.
+ #[inline]
+ fn from(obj: Retained<T>) -> Self {
+ // SAFETY: All 'static objects can be converted to `AnyObject`.
+ unsafe { Retained::cast_unchecked(obj) }
+ }
+}
+
+impl<P: ?Sized + 'static> From<Retained<ProtocolObject<P>>> for Retained<AnyObject> {
+ /// Convert the protocol object to `AnyObject`.
+ #[inline]
+ fn from(obj: Retained<ProtocolObject<P>>) -> Self {
+ // SAFETY: All protocol objects are Objective-C objects too.
+ unsafe { Retained::cast_unchecked(obj) }
+ }
+}
+
+/// `Retained<T>` is `Send` if `T` is `Send + Sync`.
+//
+// SAFETY:
+// - `T: Send` is required because otherwise you could move the object to
+// another thread and let `dealloc` get called there.
+// - `T: Sync` is required because otherwise you could clone `&Retained<T>`,
+// send it to another thread, and drop the clone last, making `dealloc` get
+// called on the other thread.
+//
+// This is the same reasoning as for `Arc`.
+// https://doc.rust-lang.org/nomicon/arc-mutex/arc-base.html#send-and-sync
+unsafe impl<T: ?Sized + Sync + Send> Send for Retained<T> {}
+
+/// `Retained<T>` is `Sync` if `T` is `Send + Sync`.
+//
+// SAFETY:
+// - `T: Sync` is required because `&Retained<T>` give access to `&T`.
+// -`T: Send` is required because otherwise you could clone `&Retained<T>`
+// from another thread, and drop the clone last, making `dealloc` get called
+// on the other thread.
+//
+// This is the same reasoning as for `Arc`.
+// https://doc.rust-lang.org/nomicon/arc-mutex/arc-base.html#send-and-sync
+unsafe impl<T: ?Sized + Sync + Send> Sync for Retained<T> {}
+
+// This is valid without `T: Unpin` because we don't implement any projection.
+//
+// See https://doc.rust-lang.org/1.54.0/src/alloc/boxed.rs.html#1652-1675
+// and the `Arc` implementation.
+impl<T: ?Sized> Unpin for Retained<T> {}
+
+// Same as Arc
+impl<T: ?Sized + RefUnwindSafe> RefUnwindSafe for Retained<T> {}
+
+// Same as Arc
+impl<T: ?Sized + RefUnwindSafe> UnwindSafe for Retained<T> {}
+
+#[cfg(test)]
+mod tests {
+ use core::mem::size_of;
+
+ use static_assertions::{assert_impl_all, assert_not_impl_any};
+
+ use super::*;
+ use crate::rc::{autoreleasepool, RcTestObject, ThreadTestData};
+ use crate::runtime::{AnyObject, NSObject, NSObjectProtocol};
+ use crate::{define_class, msg_send};
+
+ #[test]
+ fn auto_traits() {
+ macro_rules! helper {
+ ($name:ident) => {
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[name = concat!(stringify!($name), "Test")]
+ // Make the type not thread safe by default.
+ #[ivars = *const ()]
+ struct $name;
+ );
+ };
+ }
+
+ helper!(Object);
+ helper!(SendObject);
+ unsafe impl Send for SendObject {}
+ helper!(SyncObject);
+ unsafe impl Sync for SyncObject {}
+ helper!(SendSyncObject);
+ unsafe impl Send for SendSyncObject {}
+ unsafe impl Sync for SendSyncObject {}
+
+ assert_impl_all!(Retained<AnyObject>: Unpin);
+ assert_not_impl_any!(Retained<AnyObject>: Send, Sync, UnwindSafe, RefUnwindSafe);
+
+ assert_not_impl_any!(Retained<Object>: Send, Sync);
+ assert_not_impl_any!(Retained<SendObject>: Send, Sync);
+ assert_not_impl_any!(Retained<SyncObject>: Send, Sync);
+ assert_impl_all!(Retained<SendSyncObject>: Send, Sync);
+ }
+
+ #[test]
+ fn test_drop() {
+ let mut expected = ThreadTestData::current();
+
+ let obj = RcTestObject::new();
+ expected.alloc += 1;
+ expected.init += 1;
+ expected.assert_current();
+
+ drop(obj);
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+ }
+
+ #[test]
+ fn test_autorelease() {
+ let obj = RcTestObject::new();
+ let cloned = obj.clone();
+ let mut expected = ThreadTestData::current();
+
+ autoreleasepool(|pool| {
+ let _ref = unsafe { Retained::autorelease(obj, pool) };
+ expected.autorelease += 1;
+ expected.assert_current();
+ assert_eq!(cloned.retainCount(), 2);
+ });
+ expected.release += 1;
+ expected.assert_current();
+ assert_eq!(cloned.retainCount(), 1);
+
+ autoreleasepool(|pool| {
+ let _ref = unsafe { Retained::autorelease(cloned, pool) };
+ expected.autorelease += 1;
+ expected.assert_current();
+ });
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+ }
+
+ #[test]
+ fn test_clone() {
+ let obj = RcTestObject::new();
+ assert_eq!(obj.retainCount(), 1);
+ let mut expected = ThreadTestData::current();
+
+ expected.assert_current();
+ assert_eq!(obj.retainCount(), 1);
+
+ let cloned = obj.clone();
+ expected.retain += 1;
+ expected.assert_current();
+ assert_eq!(cloned.retainCount(), 2);
+ assert_eq!(obj.retainCount(), 2);
+
+ let obj = obj.into_super().into_super();
+ let cloned_and_type_erased = obj.clone();
+ expected.retain += 1;
+ expected.assert_current();
+ let retain_count: usize = unsafe { msg_send![&cloned_and_type_erased, retainCount] };
+ assert_eq!(retain_count, 3);
+ let retain_count: usize = unsafe { msg_send![&obj, retainCount] };
+ assert_eq!(retain_count, 3);
+
+ drop(obj);
+ expected.release += 1;
+ expected.assert_current();
+ assert_eq!(cloned.retainCount(), 2);
+
+ drop(cloned_and_type_erased);
+ expected.release += 1;
+ expected.assert_current();
+ assert_eq!(cloned.retainCount(), 1);
+
+ drop(cloned);
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+ }
+
+ #[test]
+ fn test_retain_autoreleased_works_as_retain() {
+ let obj = RcTestObject::new();
+ let mut expected = ThreadTestData::current();
+
+ let ptr = Retained::as_ptr(&obj) as *mut RcTestObject;
+ let _obj2 = unsafe { Retained::retain_autoreleased(ptr) }.unwrap();
+ expected.retain += 1;
+ expected.assert_current();
+ }
+
+ #[test]
+ fn test_cast() {
+ let obj: Retained<RcTestObject> = RcTestObject::new();
+ let expected = ThreadTestData::current();
+
+ let obj: Retained<AnyObject> = obj.into();
+ expected.assert_current();
+
+ let _obj: Retained<RcTestObject> = Retained::downcast(obj).unwrap();
+ expected.assert_current();
+ }
+
+ #[repr(C)]
+ struct MyObject<'a> {
+ inner: NSObject,
+ p: PhantomData<&'a str>,
+ }
+
+ /// Test that `Retained<T>` is covariant over `T`.
+ #[allow(unused)]
+ fn assert_retained_variance<'b>(obj: Retained<MyObject<'static>>) -> Retained<MyObject<'b>> {
+ obj
+ }
+
+ #[test]
+ fn test_size_of() {
+ let ptr_size = size_of::<&NSObject>();
+
+ assert_eq!(size_of::<Retained<NSObject>>(), ptr_size);
+ assert_eq!(size_of::<Option<Retained<NSObject>>>(), ptr_size);
+ }
+
+ #[test]
+ fn test_into() {
+ let obj = NSObject::new();
+ let obj: Retained<NSObject> = Into::into(obj);
+ let _: Retained<AnyObject> = Into::into(obj);
+
+ let obj_ref = &*NSObject::new();
+ let _: Retained<NSObject> = Into::into(obj_ref);
+ let _: Retained<AnyObject> = Into::into(obj_ref);
+
+ let obj_retained_ref = &NSObject::new();
+ let _: Retained<NSObject> = Into::into(obj_retained_ref);
+ let _: Retained<AnyObject> = Into::into(obj_retained_ref);
+
+ let protocol_obj = ProtocolObject::<dyn NSObjectProtocol>::from_retained(NSObject::new());
+ let _: Retained<AnyObject> = Into::into(protocol_obj);
+ }
+
+ #[test]
+ #[cfg(feature = "unstable-coerce-pointee")]
+ fn test_coercion() {
+ use crate::extern_protocol;
+
+ extern_protocol!(
+ unsafe trait ExampleProtocol: NSObjectProtocol {}
+ );
+
+ unsafe impl ExampleProtocol for RcTestObject {}
+
+ let obj = RcTestObject::new();
+ let mut expected = ThreadTestData::current();
+
+ let obj: Retained<dyn ExampleProtocol> = obj;
+ expected.assert_current();
+
+ let obj: Retained<dyn NSObjectProtocol> = obj;
+ expected.assert_current();
+
+ // TODO: Allow calling methods on trait objects like this.
+ // let _ = obj.hash();
+
+ drop(obj);
+ expected.release += 1;
+ expected.drop += 1;
+ expected.assert_current();
+ }
+}
diff --git a/third_party/rust/objc2/src/rc/retained_forwarding_impls.rs b/third_party/rust/objc2/src/rc/retained_forwarding_impls.rs
@@ -0,0 +1,425 @@
+//! Trivial forwarding impls on `Retained`-like types.
+//!
+//! Kept here to keep `retained.rs` free from this boilerplate, and to allow
+//! re-use in `objc2-core-foundation` and `dispatch2` (this file is symlinked
+//! there as well).
+
+#![forbid(unsafe_code)]
+
+use core::borrow::Borrow;
+use core::cmp::Ordering;
+use core::fmt;
+use core::future::Future;
+use core::hash;
+use core::pin::Pin;
+use core::task::{Context, Poll};
+#[cfg(feature = "std")] // TODO: Use core::error::Error once 1.81 is in MSRV
+use std::error::Error;
+#[cfg(feature = "std")]
+use std::io;
+
+use super::Retained;
+
+impl<T: ?Sized + PartialEq<U>, U: ?Sized> PartialEq<Retained<U>> for Retained<T> {
+ #[inline]
+ fn eq(&self, other: &Retained<U>) -> bool {
+ (**self).eq(&**other)
+ }
+
+ #[inline]
+ #[allow(clippy::partialeq_ne_impl)]
+ fn ne(&self, other: &Retained<U>) -> bool {
+ (**self).ne(&**other)
+ }
+}
+
+impl<T: Eq + ?Sized> Eq for Retained<T> {}
+
+impl<T: ?Sized + PartialOrd<U>, U: ?Sized> PartialOrd<Retained<U>> for Retained<T> {
+ #[inline]
+ fn partial_cmp(&self, other: &Retained<U>) -> Option<Ordering> {
+ (**self).partial_cmp(&**other)
+ }
+ #[inline]
+ fn lt(&self, other: &Retained<U>) -> bool {
+ (**self).lt(&**other)
+ }
+ #[inline]
+ fn le(&self, other: &Retained<U>) -> bool {
+ (**self).le(&**other)
+ }
+ #[inline]
+ fn ge(&self, other: &Retained<U>) -> bool {
+ (**self).ge(&**other)
+ }
+ #[inline]
+ fn gt(&self, other: &Retained<U>) -> bool {
+ (**self).gt(&**other)
+ }
+}
+
+impl<T: Ord + ?Sized> Ord for Retained<T> {
+ #[inline]
+ fn cmp(&self, other: &Self) -> Ordering {
+ (**self).cmp(&**other)
+ }
+}
+
+impl<T: hash::Hash + ?Sized> hash::Hash for Retained<T> {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) {
+ (**self).hash(state);
+ }
+}
+
+impl<T: ?Sized> hash::Hasher for Retained<T>
+where
+ for<'a> &'a T: hash::Hasher,
+{
+ fn finish(&self) -> u64 {
+ (&**self).finish()
+ }
+ fn write(&mut self, bytes: &[u8]) {
+ (&**self).write(bytes);
+ }
+ fn write_u8(&mut self, i: u8) {
+ (&**self).write_u8(i);
+ }
+ fn write_u16(&mut self, i: u16) {
+ (&**self).write_u16(i);
+ }
+ fn write_u32(&mut self, i: u32) {
+ (&**self).write_u32(i);
+ }
+ fn write_u64(&mut self, i: u64) {
+ (&**self).write_u64(i);
+ }
+ fn write_u128(&mut self, i: u128) {
+ (&**self).write_u128(i);
+ }
+ fn write_usize(&mut self, i: usize) {
+ (&**self).write_usize(i);
+ }
+ fn write_i8(&mut self, i: i8) {
+ (&**self).write_i8(i);
+ }
+ fn write_i16(&mut self, i: i16) {
+ (&**self).write_i16(i);
+ }
+ fn write_i32(&mut self, i: i32) {
+ (&**self).write_i32(i);
+ }
+ fn write_i64(&mut self, i: i64) {
+ (&**self).write_i64(i);
+ }
+ fn write_i128(&mut self, i: i128) {
+ (&**self).write_i128(i);
+ }
+ fn write_isize(&mut self, i: isize) {
+ (&**self).write_isize(i);
+ }
+}
+
+impl<'a, T: ?Sized> hash::Hasher for &'a Retained<T>
+where
+ &'a T: hash::Hasher,
+{
+ fn finish(&self) -> u64 {
+ (&***self).finish()
+ }
+ fn write(&mut self, bytes: &[u8]) {
+ (&***self).write(bytes);
+ }
+ fn write_u8(&mut self, i: u8) {
+ (&***self).write_u8(i);
+ }
+ fn write_u16(&mut self, i: u16) {
+ (&***self).write_u16(i);
+ }
+ fn write_u32(&mut self, i: u32) {
+ (&***self).write_u32(i);
+ }
+ fn write_u64(&mut self, i: u64) {
+ (&***self).write_u64(i);
+ }
+ fn write_u128(&mut self, i: u128) {
+ (&***self).write_u128(i);
+ }
+ fn write_usize(&mut self, i: usize) {
+ (&***self).write_usize(i);
+ }
+ fn write_i8(&mut self, i: i8) {
+ (&***self).write_i8(i);
+ }
+ fn write_i16(&mut self, i: i16) {
+ (&***self).write_i16(i);
+ }
+ fn write_i32(&mut self, i: i32) {
+ (&***self).write_i32(i);
+ }
+ fn write_i64(&mut self, i: i64) {
+ (&***self).write_i64(i);
+ }
+ fn write_i128(&mut self, i: i128) {
+ (&***self).write_i128(i);
+ }
+ fn write_isize(&mut self, i: isize) {
+ (&***self).write_isize(i);
+ }
+}
+
+macro_rules! forward_fmt_impl {
+ ($trait:path) => {
+ impl<T: $trait + ?Sized> $trait for Retained<T> {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ (**self).fmt(f)
+ }
+ }
+ };
+}
+
+forward_fmt_impl!(fmt::Binary);
+forward_fmt_impl!(fmt::Debug);
+forward_fmt_impl!(fmt::Display);
+forward_fmt_impl!(fmt::LowerExp);
+forward_fmt_impl!(fmt::LowerHex);
+forward_fmt_impl!(fmt::Octal);
+// forward_fmt_impl!(fmt::Pointer);
+forward_fmt_impl!(fmt::UpperExp);
+forward_fmt_impl!(fmt::UpperHex);
+
+impl<'a, T: ?Sized> fmt::Write for &'a Retained<T>
+where
+ &'a T: fmt::Write,
+{
+ #[inline]
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ (&***self).write_str(s)
+ }
+
+ #[inline]
+ fn write_char(&mut self, c: char) -> fmt::Result {
+ (&***self).write_char(c)
+ }
+
+ #[inline]
+ fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
+ (&***self).write_fmt(args)
+ }
+}
+
+impl<T: ?Sized> Borrow<T> for Retained<T> {
+ fn borrow(&self) -> &T {
+ // Auto-derefs
+ self
+ }
+}
+
+// Forward to inner type's `AsRef`.
+//
+// This is different from what `Box` does, but is desirable in our case, as it
+// allows going directly to superclasses.
+//
+// See also discussion in:
+// <https://internals.rust-lang.org/t/semantics-of-asref/17016>
+impl<T: ?Sized + AsRef<U>, U: ?Sized> AsRef<U> for Retained<T> {
+ fn as_ref(&self) -> &U {
+ (**self).as_ref()
+ }
+}
+
+#[cfg(feature = "std")]
+impl<T: ?Sized + Error> Error for Retained<T> {
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ (**self).source()
+ }
+}
+
+#[cfg(feature = "std")]
+impl<T: ?Sized> io::Read for Retained<T>
+where
+ for<'a> &'a T: io::Read,
+{
+ #[inline]
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ (&**self).read(buf)
+ }
+
+ #[inline]
+ fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
+ (&**self).read_vectored(bufs)
+ }
+
+ #[inline]
+ fn read_to_end(&mut self, buf: &mut std::vec::Vec<u8>) -> io::Result<usize> {
+ (&**self).read_to_end(buf)
+ }
+
+ #[inline]
+ fn read_to_string(&mut self, buf: &mut std::string::String) -> io::Result<usize> {
+ (&**self).read_to_string(buf)
+ }
+
+ #[inline]
+ fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
+ (&**self).read_exact(buf)
+ }
+}
+
+#[cfg(feature = "std")]
+impl<'a, T: ?Sized> io::Read for &'a Retained<T>
+where
+ &'a T: io::Read,
+{
+ #[inline]
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ (&***self).read(buf)
+ }
+
+ #[inline]
+ fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
+ (&***self).read_vectored(bufs)
+ }
+
+ #[inline]
+ fn read_to_end(&mut self, buf: &mut std::vec::Vec<u8>) -> io::Result<usize> {
+ (&***self).read_to_end(buf)
+ }
+
+ #[inline]
+ fn read_to_string(&mut self, buf: &mut std::string::String) -> io::Result<usize> {
+ (&***self).read_to_string(buf)
+ }
+
+ #[inline]
+ fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
+ (&***self).read_exact(buf)
+ }
+}
+
+#[cfg(feature = "std")]
+impl<T: ?Sized> io::Write for Retained<T>
+where
+ for<'a> &'a T: io::Write,
+{
+ #[inline]
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ (&**self).write(buf)
+ }
+
+ #[inline]
+ fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
+ (&**self).write_vectored(bufs)
+ }
+
+ #[inline]
+ fn flush(&mut self) -> io::Result<()> {
+ (&**self).flush()
+ }
+
+ #[inline]
+ fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
+ (&**self).write_all(buf)
+ }
+
+ #[inline]
+ fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
+ (&**self).write_fmt(fmt)
+ }
+}
+
+#[cfg(feature = "std")]
+impl<'a, T: ?Sized> io::Write for &'a Retained<T>
+where
+ &'a T: io::Write,
+{
+ #[inline]
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ (&***self).write(buf)
+ }
+
+ #[inline]
+ fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
+ (&***self).write_vectored(bufs)
+ }
+
+ #[inline]
+ fn flush(&mut self) -> io::Result<()> {
+ (&***self).flush()
+ }
+
+ #[inline]
+ fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
+ (&***self).write_all(buf)
+ }
+
+ #[inline]
+ fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
+ (&***self).write_fmt(fmt)
+ }
+}
+
+#[cfg(feature = "std")]
+impl<T: ?Sized> io::Seek for Retained<T>
+where
+ for<'a> &'a T: io::Seek,
+{
+ #[inline]
+ fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
+ (&**self).seek(pos)
+ }
+
+ #[inline]
+ fn stream_position(&mut self) -> io::Result<u64> {
+ (&**self).stream_position()
+ }
+}
+
+#[cfg(feature = "std")]
+impl<'a, T: ?Sized> io::Seek for &'a Retained<T>
+where
+ &'a T: io::Seek,
+{
+ #[inline]
+ fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
+ (&***self).seek(pos)
+ }
+
+ #[inline]
+ fn stream_position(&mut self) -> io::Result<u64> {
+ (&***self).stream_position()
+ }
+}
+
+impl<'a, T: ?Sized> Future for &'a Retained<T>
+where
+ &'a T: Future,
+{
+ type Output = <&'a T as Future>::Output;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ <&T>::poll(Pin::new(&mut &***self), cx)
+ }
+}
+
+impl<A, T: ?Sized> Extend<A> for Retained<T>
+where
+ for<'a> &'a T: Extend<A>,
+{
+ #[inline]
+ fn extend<I: IntoIterator<Item = A>>(&mut self, iter: I) {
+ (&**self).extend(iter)
+ }
+}
+
+impl<'a, A, T: ?Sized> Extend<A> for &'a Retained<T>
+where
+ &'a T: Extend<A>,
+{
+ #[inline]
+ fn extend<I: IntoIterator<Item = A>>(&mut self, iter: I) {
+ (&***self).extend(iter)
+ }
+}
+
+// TODO: impl Fn traits, CoerceUnsized, Stream and so on when stabilized
diff --git a/third_party/rust/objc2/src/rc/retained_traits.rs b/third_party/rust/objc2/src/rc/retained_traits.rs
@@ -0,0 +1,204 @@
+//! Helper traits for Retained.
+
+use super::Retained;
+
+/// Helper trait to implement [`Default`] on [`Retained`].
+#[doc(alias = "DefaultId")] // Previous name
+pub trait DefaultRetained {
+ /// The default [`Retained`] for a type.
+ ///
+ /// On most objects the implementation would be sending a message to the
+ /// `new` selector.
+ fn default_retained() -> Retained<Self>;
+}
+
+impl<T: ?Sized + DefaultRetained> Default for Retained<T> {
+ #[inline]
+ fn default() -> Self {
+ T::default_retained()
+ }
+}
+
+/// Helper trait to implement [`IntoIterator`] on [`Retained`].
+///
+/// This should be implemented in exactly the same fashion as if you were
+/// implementing `IntoIterator` for your type normally.
+//
+// Note that [`Box<T>` gets to cheat with regards moves][box-move], so
+// `boxed.into_iter()` is possible, while `obj.into_iter()` is not possible
+// without this helper trait.
+//
+// [box-move]: https://doc.rust-lang.org/reference/expressions.html#moved-and-copied-types
+#[doc(alias = "IdIntoIterator")] // Previous name
+pub trait RetainedIntoIterator {
+ /// The type of the elements being iterated over.
+ type Item;
+
+ /// Which kind of iterator are we turning this into?
+ type IntoIter: Iterator<Item = Self::Item>;
+
+ /// Creates an iterator from an [`Retained`].
+ ///
+ /// You would normally not call this function directly; instead, you'd
+ /// call [`into_iter`](IntoIterator::into_iter) on an [`Retained`].
+ fn retained_into_iter(this: Retained<Self>) -> Self::IntoIter;
+}
+
+// Note: These `IntoIterator` implementations conflict with an `Iterator`
+// implementation for `Retained`.
+//
+// For our case however (in contrast with `Box`), that is the better tradeoff,
+// which I will show with an example:
+//
+// ```
+// let xs = Box::new(vec![]);
+// for x in &xs { // Doesn't compile, `&Box` doesn't implement `IntoIterator`
+// // ...
+// }
+// ```
+//
+// Here, you're expected to write `xs.iter()` or `&**xs` instead, which is
+// fairly acceptable, since usually people don't wrap things in boxes so much;
+// but in Objective-C, _everything_ is wrapped in an `Retained`, and hence we should
+// attempt to make that common case easier:
+//
+// ```
+// let obj = NSArray::new(); // `Retained<NSArray<_>>`
+// for item in &obj { // Should compile
+// // ...
+// }
+// ```
+//
+// The loss of the `Iterator` impl is a bit unfortunate, but not a big deal,
+// since there is only one iterator in Objective-C anyhow, `NSEnumerator`, and
+// for that we can make other abstractions instead.
+impl<T: ?Sized + RetainedIntoIterator> IntoIterator for Retained<T> {
+ type Item = <T as RetainedIntoIterator>::Item;
+ type IntoIter = <T as RetainedIntoIterator>::IntoIter;
+
+ #[inline]
+ fn into_iter(self) -> Self::IntoIter {
+ T::retained_into_iter(self)
+ }
+}
+
+impl<'a, T: ?Sized> IntoIterator for &'a Retained<T>
+where
+ &'a T: IntoIterator,
+{
+ type Item = <&'a T as IntoIterator>::Item;
+ type IntoIter = <&'a T as IntoIterator>::IntoIter;
+
+ #[inline]
+ fn into_iter(self) -> Self::IntoIter {
+ (&**self).into_iter()
+ }
+}
+
+/// Helper trait to implement [`FromIterator`] on [`Retained`].
+///
+/// This should be implemented in exactly the same fashion as if you were
+/// implementing `FromIterator` for your type normally.
+#[doc(alias = "IdFromIterator")] // Previous name
+pub trait RetainedFromIterator<T>: Sized {
+ /// Creates an `Retained` from an iterator.
+ fn retained_from_iter<I>(iter: I) -> Retained<Self>
+ where
+ I: IntoIterator<Item = T>;
+}
+
+impl<T, U: RetainedFromIterator<T>> FromIterator<T> for Retained<U> {
+ #[inline]
+ fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
+ U::retained_from_iter(iter)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::runtime::NSObject;
+ use crate::{define_class, msg_send, ClassType};
+
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[derive(PartialEq, Eq, Hash, Debug)]
+ struct Collection;
+ );
+
+ impl DefaultRetained for Collection {
+ fn default_retained() -> Retained<Self> {
+ unsafe { msg_send![Collection::class(), new] }
+ }
+ }
+
+ struct Iter<'a> {
+ _inner: &'a Collection,
+ }
+
+ impl<'a> Iterator for Iter<'a> {
+ type Item = &'a NSObject;
+ fn next(&mut self) -> Option<Self::Item> {
+ None
+ }
+ }
+
+ impl<'a> IntoIterator for &'a Collection {
+ type Item = &'a NSObject;
+ type IntoIter = Iter<'a>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ Iter { _inner: self }
+ }
+ }
+
+ struct IntoIter {
+ _inner: Retained<Collection>,
+ }
+
+ impl Iterator for IntoIter {
+ type Item = Retained<NSObject>;
+ fn next(&mut self) -> Option<Self::Item> {
+ None
+ }
+ }
+
+ impl RetainedIntoIterator for Collection {
+ type Item = Retained<NSObject>;
+ type IntoIter = IntoIter;
+
+ fn retained_into_iter(this: Retained<Self>) -> Self::IntoIter {
+ IntoIter { _inner: this }
+ }
+ }
+
+ impl RetainedFromIterator<Retained<NSObject>> for Collection {
+ fn retained_from_iter<I: IntoIterator<Item = Retained<NSObject>>>(
+ _iter: I,
+ ) -> Retained<Self> {
+ Collection::default_retained()
+ }
+ }
+
+ #[test]
+ fn test_default() {
+ let obj1: Retained<Collection> = Default::default();
+ let obj2 = Collection::default_retained();
+ assert_ne!(obj1, obj2);
+ }
+
+ #[test]
+ fn test_into_iter() {
+ let obj: Retained<Collection> = Default::default();
+
+ for _ in &*obj {}
+ for _ in &obj {}
+
+ for _ in obj {}
+ }
+
+ #[test]
+ fn test_from_iter() {
+ let _: Retained<Collection> = [NSObject::new()].into_iter().collect();
+ }
+}
diff --git a/third_party/rust/objc2/src/rc/test_object.rs b/third_party/rust/objc2/src/rc/test_object.rs
@@ -0,0 +1,320 @@
+//! Note: This file is included in the `tests` crate viaa symlink as well.
+use core::cell::RefCell;
+use core::ptr;
+
+use crate::rc::{Allocated, DefaultRetained, Retained};
+use crate::runtime::{NSObject, NSObjectProtocol, NSZone};
+use crate::{define_class, msg_send, ClassType};
+
+// TODO: Put tests that use this in another crate
+#[derive(Debug, Clone, Default, PartialEq, Eq)]
+#[allow(missing_copy_implementations)]
+#[doc(hidden)]
+pub(crate) struct ThreadTestData {
+ pub(crate) alloc: usize,
+ pub(crate) drop: usize,
+ pub(crate) init: usize,
+ pub(crate) retain: usize,
+ pub(crate) copy: usize,
+ pub(crate) mutable_copy: usize,
+ pub(crate) release: usize,
+ pub(crate) autorelease: usize,
+ pub(crate) try_retain: usize,
+ pub(crate) try_retain_fail: usize,
+ // TODO: Is there some way we can test weak pointers? Or is that implemented entirely in Foundation?
+ // Maybe `_setWeaklyReferenced` can be useful?
+}
+
+impl ThreadTestData {
+ /// Get the amount of method calls performed on the current thread.
+ pub(crate) fn current() -> Self {
+ TEST_DATA.with(|data| data.borrow().clone())
+ }
+
+ #[track_caller]
+ #[allow(clippy::missing_panics_doc)]
+ #[allow(dead_code)]
+ pub(crate) fn assert_current(&self) {
+ let current = Self::current();
+ let mut expected = self.clone();
+ if cfg!(feature = "gnustep-1-7") {
+ // GNUStep doesn't have `tryRetain`, it uses `retain` directly
+ let retain_diff = expected.try_retain - current.try_retain;
+ expected.retain += retain_diff;
+ expected.try_retain -= retain_diff;
+
+ // GNUStep doesn't call `autorelease` if it's overridden
+ expected.autorelease = 0;
+ }
+ if current != expected {
+ panic!(
+ "got differing amounts of calls:
+ current: `{current:?}`,
+ expected: `{expected:?}`"
+ )
+ }
+ }
+}
+
+std::thread_local! {
+ static TEST_DATA: RefCell<ThreadTestData> = RefCell::default();
+}
+
+define_class!(
+ /// A helper object that counts how many times various reference-counting
+ /// primitives are called.
+ #[derive(Debug, PartialEq, Eq, Hash)]
+ #[doc(hidden)]
+ #[unsafe(super(NSObject))]
+ #[name = "__RcTestObject"]
+ pub(crate) struct RcTestObject;
+
+ impl RcTestObject {
+ #[unsafe(method_id(newReturningNull))]
+ fn new_returning_null() -> Option<Retained<Self>> {
+ None
+ }
+
+ #[unsafe(method_id(newMethodOnInstance))]
+ fn new_method_on_instance(&self) -> Retained<Self> {
+ Self::new()
+ }
+
+ #[unsafe(method_id(newMethodOnInstanceNull))]
+ fn new_method_on_instance_null(&self) -> Option<Retained<Self>> {
+ None
+ }
+
+ #[unsafe(method(alloc))]
+ fn alloc_() -> *mut Self {
+ TEST_DATA.with(|data| data.borrow_mut().alloc += 1);
+ let superclass = NSObject::class().metaclass();
+ let zone: *const NSZone = ptr::null();
+ unsafe { msg_send![super(Self::class(), superclass), allocWithZone: zone] }
+ }
+
+ #[unsafe(method(allocWithZone:))]
+ fn alloc_with_zone(zone: *const NSZone) -> *mut Self {
+ TEST_DATA.with(|data| data.borrow_mut().alloc += 1);
+ let superclass = NSObject::class().metaclass();
+ unsafe { msg_send![super(Self::class(), superclass), allocWithZone: zone] }
+ }
+
+ #[unsafe(method(allocReturningNull))]
+ fn alloc_returning_null() -> *mut Self {
+ ptr::null_mut()
+ }
+
+ #[unsafe(method_id(init))]
+ unsafe fn init(this: Allocated<Self>) -> Retained<Self> {
+ TEST_DATA.with(|data| data.borrow_mut().init += 1);
+ let this = this.set_ivars(());
+ unsafe { msg_send![super(this), init] }
+ }
+
+ #[unsafe(method_id(initReturningNull))]
+ fn init_returning_null(_this: Allocated<Self>) -> Option<Retained<Self>> {
+ None
+ }
+
+ #[unsafe(method(retain))]
+ fn retain(&self) -> *mut Self {
+ TEST_DATA.with(|data| data.borrow_mut().retain += 1);
+ unsafe { msg_send![super(self), retain] }
+ }
+
+ #[unsafe(method(release))]
+ fn release(&self) {
+ TEST_DATA.with(|data| data.borrow_mut().release += 1);
+ unsafe { msg_send![super(self), release] }
+ }
+
+ #[unsafe(method(autorelease))]
+ fn autorelease(&self) -> *mut Self {
+ TEST_DATA.with(|data| data.borrow_mut().autorelease += 1);
+ unsafe { msg_send![super(self), autorelease] }
+ }
+
+ #[unsafe(method(_tryRetain))]
+ unsafe fn try_retain(&self) -> bool {
+ TEST_DATA.with(|data| data.borrow_mut().try_retain += 1);
+ let res: bool = unsafe { msg_send![super(self), _tryRetain] };
+ if !res {
+ TEST_DATA.with(|data| data.borrow_mut().try_retain -= 1);
+ TEST_DATA.with(|data| data.borrow_mut().try_retain_fail += 1);
+ }
+ res
+ }
+
+ #[unsafe(method_id(copyWithZone:))]
+ fn copy_with_zone(&self, _zone: *const NSZone) -> Retained<Self> {
+ TEST_DATA.with(|data| data.borrow_mut().copy += 1);
+ Self::new()
+ }
+
+ #[unsafe(method_id(mutableCopyWithZone:))]
+ fn mutable_copy_with_zone(&self, _zone: *const NSZone) -> Retained<Self> {
+ TEST_DATA.with(|data| data.borrow_mut().mutable_copy += 1);
+ Self::new()
+ }
+
+ #[unsafe(method_id(copyReturningNull))]
+ fn copy_returning_null(_this: &Self) -> Option<Retained<Self>> {
+ None
+ }
+
+ #[unsafe(method_id(methodReturningNull))]
+ fn method_returning_null(self: &Self) -> Option<Retained<Self>> {
+ None
+ }
+
+ #[unsafe(method_id(aMethod:))]
+ fn a_method(&self, param: bool) -> Option<Retained<Self>> {
+ param.then(Self::new)
+ }
+
+ #[unsafe(method(boolAndShouldError:error:))]
+ fn class_error_bool(should_error: bool, err: Option<&mut *mut RcTestObject>) -> bool {
+ if should_error {
+ if let Some(err) = err {
+ *err = Retained::autorelease_ptr(RcTestObject::new());
+ }
+ false
+ } else {
+ true
+ }
+ }
+
+ #[unsafe(method(boolAndShouldError:error:))]
+ fn instance_error_bool(
+ &self,
+ should_error: bool,
+ err: Option<&mut *mut RcTestObject>,
+ ) -> bool {
+ if should_error {
+ if let Some(err) = err {
+ *err = Retained::autorelease_ptr(RcTestObject::new());
+ }
+ false
+ } else {
+ true
+ }
+ }
+
+ #[unsafe(method_id(idAndShouldError:error:))]
+ fn class_error_retained(
+ should_error: bool,
+ err: Option<&mut *mut RcTestObject>,
+ ) -> Option<Retained<Self>> {
+ if should_error {
+ if let Some(err) = err {
+ *err = Retained::autorelease_ptr(RcTestObject::new());
+ }
+ None
+ } else {
+ Some(Self::new())
+ }
+ }
+
+ #[unsafe(method_id(idAndShouldError:error:))]
+ fn instance_error_retained(
+ self: &Self,
+ should_error: bool,
+ err: Option<&mut *mut RcTestObject>,
+ ) -> Option<Retained<Self>> {
+ if should_error {
+ if let Some(err) = err {
+ *err = Retained::autorelease_ptr(RcTestObject::new());
+ }
+ None
+ } else {
+ Some(Self::new())
+ }
+ }
+
+ #[unsafe(method_id(newAndShouldError:error:))]
+ fn new_error(
+ should_error: bool,
+ err: Option<&mut *mut RcTestObject>,
+ ) -> Option<Retained<Self>> {
+ if should_error {
+ if let Some(err) = err {
+ *err = Retained::autorelease_ptr(RcTestObject::new());
+ }
+ None
+ } else {
+ unsafe { msg_send![Self::class(), new] }
+ }
+ }
+
+ #[unsafe(method(allocAndShouldError:error:))]
+ fn alloc_error(should_error: bool, err: Option<&mut *mut RcTestObject>) -> *mut Self {
+ if should_error {
+ if let Some(err) = err {
+ *err = Retained::autorelease_ptr(RcTestObject::new());
+ }
+ ptr::null_mut()
+ } else {
+ unsafe { msg_send![Self::class(), alloc] }
+ }
+ }
+
+ #[unsafe(method_id(initAndShouldError:error:))]
+ fn init_error(
+ this: Allocated<Self>,
+ should_error: bool,
+ err: Option<&mut *mut RcTestObject>,
+ ) -> Option<Retained<Self>> {
+ if should_error {
+ if let Some(err) = err {
+ *err = Retained::autorelease_ptr(RcTestObject::new());
+ }
+ None
+ } else {
+ unsafe { msg_send![this, init] }
+ }
+ }
+
+ #[unsafe(method(outParamNull:))]
+ fn out_param_null(param: Option<&mut *mut RcTestObject>) {
+ if let Some(param) = param {
+ *param = ptr::null_mut();
+ }
+ }
+
+ #[unsafe(method(willPanicWith:panicsAfter:))]
+ fn will_panic(param: Option<&mut *mut RcTestObject>, panic_after: bool) {
+ if !panic_after {
+ panic!("intentional panic");
+ }
+ if let Some(param) = param {
+ *param = Retained::autorelease_ptr(RcTestObject::new());
+ }
+ if panic_after {
+ panic!("intentional panic");
+ }
+ }
+ }
+
+ unsafe impl NSObjectProtocol for RcTestObject {}
+);
+
+impl Drop for RcTestObject {
+ fn drop(&mut self) {
+ TEST_DATA.with(|data| data.borrow_mut().drop += 1);
+ }
+}
+
+impl DefaultRetained for RcTestObject {
+ fn default_retained() -> Retained<Self> {
+ Self::new()
+ }
+}
+
+impl RcTestObject {
+ #[doc(hidden)]
+ pub(crate) fn new() -> Retained<Self> {
+ // Use raw `msg_send!`, the automatic conversion are tested elsewhere.
+ unsafe { Retained::from_raw(msg_send![Self::class(), new]) }.unwrap()
+ }
+}
diff --git a/third_party/rust/objc2/src/rc/weak.rs b/third_party/rust/objc2/src/rc/weak.rs
@@ -0,0 +1,288 @@
+use alloc::boxed::Box;
+use core::cell::UnsafeCell;
+use core::fmt;
+use core::marker::PhantomData;
+use core::ptr;
+use std::panic::{RefUnwindSafe, UnwindSafe};
+
+use super::Retained;
+use crate::runtime::AnyObject;
+use crate::{ffi, Message};
+
+/// A weak pointer to an Objective-C reference counted object.
+///
+/// The object is allowed to be deallocated while the weak pointer is alive,
+/// though the backing allocation for the object can only be released once all
+/// weak pointers are gone.
+///
+/// Useful for breaking reference cycles and safely checking whether an
+/// object has been deallocated.
+///
+///
+/// # Comparison to `std` types
+///
+/// This is the Objective-C equivalent of [`sync::Weak`] from the standard
+/// library, and hence is only usable on types where `Retained<T>` acts like
+/// [`sync::Arc`], a.k.a. on non-mutable types.
+///
+/// [`sync::Weak`]: std::sync::Weak
+/// [`sync::Arc`]: std::sync::Arc
+#[repr(transparent)] // This is not a public guarantee
+#[doc(alias = "WeakId")] // Previous name
+pub struct Weak<T: ?Sized> {
+ /// We give the runtime the address to this box, so that it can modify it
+ /// even if the `Weak` is moved.
+ ///
+ /// Loading may modify the pointer through a shared reference, so we use
+ /// an UnsafeCell to get a *mut without self being mutable.
+ ///
+ /// Remember that any thread may actually modify the inner value
+ /// concurrently, but as long as we only use it through the `objc_XXXWeak`
+ /// methods, all access is behind a lock.
+ ///
+ /// TODO: Verify the need for UnsafeCell?
+ /// TODO: Investigate if we can avoid some allocations using `Pin`.
+ /// TODO: Add derive(CoercePointee) once this doesn't Box internally.
+ inner: Box<UnsafeCell<*mut AnyObject>>,
+ /// Weak inherits variance, dropck and various marker traits from
+ /// `Retained<T>`.
+ item: PhantomData<Retained<T>>,
+}
+
+/// Fully-deprecated type-alias to [`Weak`].
+#[deprecated(since = "0.6.0", note = "Renamed to `Weak`.")]
+pub type WeakId<T> = Weak<T>;
+
+impl<T: Message> Weak<T> {
+ /// Construct a new weak pointer that references the given object.
+ #[doc(alias = "objc_initWeak")]
+ #[inline]
+ pub fn new(obj: &T) -> Self {
+ // SAFETY: Pointer is valid since it came from a reference.
+ unsafe { Self::new_inner(obj) }
+ }
+
+ /// Construct a new weak pointer that references the given [`Retained`].
+ #[doc(alias = "objc_initWeak")]
+ #[deprecated = "use `Weak::from_retained` instead"]
+ #[inline]
+ pub fn from_id(obj: &Retained<T>) -> Self {
+ Self::from_retained(obj)
+ }
+
+ /// Construct a new weak pointer that references the given [`Retained`].
+ #[doc(alias = "objc_initWeak")]
+ #[inline]
+ pub fn from_retained(obj: &Retained<T>) -> Self {
+ // SAFETY: Pointer is valid since it came from `Retained`.
+ unsafe { Self::new_inner(Retained::as_ptr(obj)) }
+ }
+
+ /// Raw constructor.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The object must be valid or null.
+ unsafe fn new_inner(obj: *const T) -> Self {
+ let inner = Box::new(UnsafeCell::new(ptr::null_mut()));
+ // SAFETY: `ptr` will never move, and the caller verifies `obj`
+ let _ = unsafe { ffi::objc_initWeak(inner.get(), (obj as *mut T).cast()) };
+ Self {
+ inner,
+ item: PhantomData,
+ }
+ }
+
+ /// Load the object into an [`Retained`] if it still exists.
+ ///
+ /// Returns [`None`] if the object has been deallocated, or the `Weak`
+ /// was created with [`Default::default`].
+ #[doc(alias = "retain")]
+ #[doc(alias = "objc_loadWeak")]
+ #[doc(alias = "objc_loadWeakRetained")]
+ #[inline]
+ pub fn load(&self) -> Option<Retained<T>> {
+ let ptr = self.inner.get();
+ let obj = unsafe { ffi::objc_loadWeakRetained(ptr) }.cast();
+ // SAFETY: The object has +1 retain count
+ unsafe { Retained::from_raw(obj) }
+ }
+
+ // TODO: Add `autorelease(&self, pool) -> Option<&T>` using `objc_loadWeak`?
+}
+
+impl<T: ?Sized> Drop for Weak<T> {
+ /// Destroys the weak pointer.
+ #[doc(alias = "objc_destroyWeak")]
+ #[inline]
+ fn drop(&mut self) {
+ unsafe { ffi::objc_destroyWeak(self.inner.get()) }
+ }
+}
+
+// TODO: Add ?Sized
+impl<T: Message> Clone for Weak<T> {
+ /// Make a clone of the weak pointer that points to the same object.
+ #[doc(alias = "objc_copyWeak")]
+ fn clone(&self) -> Self {
+ let ptr = Box::new(UnsafeCell::new(ptr::null_mut()));
+ unsafe { ffi::objc_copyWeak(ptr.get(), self.inner.get()) };
+ Self {
+ inner: ptr,
+ item: PhantomData,
+ }
+ }
+}
+
+// TODO: Add ?Sized
+impl<T: Message> Default for Weak<T> {
+ /// Constructs a new weak pointer that doesn't reference any object.
+ ///
+ /// Calling [`Self::load`] on the return value always gives [`None`].
+ #[inline]
+ fn default() -> Self {
+ // SAFETY: The pointer is null
+ unsafe { Self::new_inner(ptr::null()) }
+ }
+}
+
+impl<T: ?Sized> fmt::Debug for Weak<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Note: We intentionally don't try to debug-print the value, since
+ // that could lead to cycles. See:
+ // https://github.com/rust-lang/rust/pull/90291
+ write!(f, "(Weak)")
+ }
+}
+
+// SAFETY: Same as `std::sync::Weak<T>`.
+unsafe impl<T: ?Sized + Sync + Send> Sync for Weak<T> {}
+
+// SAFETY: Same as `std::sync::Weak<T>`.
+unsafe impl<T: ?Sized + Sync + Send> Send for Weak<T> {}
+
+// Same as `std::sync::Weak<T>`.
+impl<T: ?Sized> Unpin for Weak<T> {}
+
+// Same as `std::sync::Weak<T>`.
+impl<T: ?Sized + RefUnwindSafe> RefUnwindSafe for Weak<T> {}
+
+// Same as `std::sync::Weak<T>`.
+impl<T: ?Sized + RefUnwindSafe> UnwindSafe for Weak<T> {}
+
+impl<T: Message> From<&T> for Weak<T> {
+ #[inline]
+ fn from(obj: &T) -> Self {
+ Weak::new(obj)
+ }
+}
+
+impl<T: Message> From<&Retained<T>> for Weak<T> {
+ #[inline]
+ fn from(obj: &Retained<T>) -> Self {
+ Weak::from_retained(obj)
+ }
+}
+
+impl<T: Message> From<Retained<T>> for Weak<T> {
+ #[inline]
+ fn from(obj: Retained<T>) -> Self {
+ Weak::from_retained(&obj)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::mem;
+
+ use super::*;
+ use crate::rc::{RcTestObject, ThreadTestData};
+ use crate::runtime::NSObject;
+
+ #[test]
+ fn test_weak() {
+ let obj = RcTestObject::new();
+ let mut expected = ThreadTestData::current();
+
+ let weak = Weak::from(&obj);
+ expected.assert_current();
+
+ let strong = weak.load().unwrap();
+ expected.try_retain += 1;
+ expected.assert_current();
+ assert!(ptr::eq(&*strong, &*obj));
+
+ drop(obj);
+ drop(strong);
+ expected.release += 2;
+ expected.drop += 1;
+ expected.assert_current();
+
+ if cfg!(not(feature = "gnustep-1-7")) {
+ // This loads the object on GNUStep for some reason??
+ assert!(weak.load().is_none());
+ expected.assert_current();
+ }
+
+ drop(weak);
+ expected.assert_current();
+ }
+
+ #[test]
+ fn test_weak_clone() {
+ let obj = RcTestObject::new();
+ let mut expected = ThreadTestData::current();
+
+ let weak = Weak::from(&obj);
+ expected.assert_current();
+
+ let weak2 = weak.clone();
+ if cfg!(target_vendor = "apple") {
+ expected.try_retain += 1;
+ expected.release += 1;
+ }
+ expected.assert_current();
+
+ let strong = weak.load().unwrap();
+ expected.try_retain += 1;
+ expected.assert_current();
+ assert!(ptr::eq(&*strong, &*obj));
+
+ let strong2 = weak2.load().unwrap();
+ expected.try_retain += 1;
+ expected.assert_current();
+ assert!(ptr::eq(&*strong, &*strong2));
+
+ drop(weak);
+ drop(weak2);
+ expected.assert_current();
+ }
+
+ #[test]
+ fn test_weak_default() {
+ let weak: Weak<RcTestObject> = Weak::default();
+ assert!(weak.load().is_none());
+ drop(weak);
+ }
+
+ #[repr(C)]
+ struct MyObject<'a> {
+ inner: NSObject,
+ p: PhantomData<&'a str>,
+ }
+
+ /// Test that `Weak<T>` is covariant over `T`.
+ #[allow(unused)]
+ fn assert_variance<'a, 'b>(obj: &'a Weak<MyObject<'static>>) -> &'a Weak<MyObject<'b>> {
+ obj
+ }
+
+ #[test]
+ fn test_size_of() {
+ assert_eq!(
+ mem::size_of::<Option<Weak<NSObject>>>(),
+ mem::size_of::<*const ()>()
+ );
+ }
+}
diff --git a/third_party/rust/objc2/src/runtime/__nsstring.rs b/third_party/rust/objc2/src/runtime/__nsstring.rs
@@ -0,0 +1,108 @@
+use core::ffi::c_char;
+use core::slice;
+use core::str;
+
+use crate::ffi::NSUInteger;
+use crate::msg_send;
+use crate::rc::AutoreleasePool;
+use crate::runtime::NSObject;
+
+// Note: While this is not public, it is still a breaking change to modify,
+// since `objc2-foundation` relies on it.
+#[cfg(not(feature = "gnustep-1-7"))]
+pub const UTF8_ENCODING: usize = 4;
+#[cfg(feature = "gnustep-1-7")]
+pub const UTF8_ENCODING: i32 = 4;
+
+/// The number of UTF-8 code units in the given string.
+///
+/// # Safety
+///
+/// The object must be an instance of `NSString`.
+//
+// Note: While this is not public, it is still a breaking change to modify,
+// since `objc2-foundation` relies on it.
+#[inline]
+pub unsafe fn nsstring_len(obj: &NSObject) -> NSUInteger {
+ unsafe { msg_send![obj, lengthOfBytesUsingEncoding: UTF8_ENCODING] }
+}
+
+/// Extract a [`str`](`prim@str`) representation out of the given NSString.
+///
+/// Uses [`UTF8String`] under the hood.
+///
+/// [`UTF8String`]: https://developer.apple.com/documentation/foundation/nsstring/1411189-utf8string?language=objc
+///
+///
+/// # Safety
+///
+/// - The object must be an instance of `NSString`.
+/// - The returned string must not be moved outside the autorelease pool into
+/// which it (may) have been released.
+///
+/// Furthermore, the object must not, as is always the case for strings, be
+/// mutated in parallel.
+//
+// Note: While this is not public, it is still a breaking change to modify,
+// since `objc2-foundation` relies on it.
+pub unsafe fn nsstring_to_str<'r, 's: 'r, 'p: 'r>(
+ obj: &'s NSObject,
+ pool: AutoreleasePool<'p>,
+) -> &'r str {
+ // This is necessary until `auto` types stabilizes.
+ pool.__verify_is_inner();
+
+ // The documentation on `UTF8String` is quite sparse, but with educated
+ // guesses, testing, reading the code for `CFString` and a bit of
+ // reverse-engineering, we've determined that `NSString` stores a pointer
+ // to the string data, sometimes with an UTF-8 encoding (usual for ascii
+ // data), sometimes in other encodings (often UTF-16).
+ //
+ // `UTF8String` then checks the internal encoding:
+ // - If the data is UTF-8 encoded, and (since macOS 10.6) if the string is
+ // immutable, it returns the internal pointer using
+ // `CFStringGetCStringPtr`.
+ // - Otherwise, if the data is in another encoding or is mutable, it
+ // creates a new allocation, writes the UTF-8 representation of the
+ // string into it, autoreleases the allocation, and returns a pointer to
+ // it (similar to `CFStringGetCString`).
+ //
+ // If the string is a tagged pointer, or a custom subclass, then another
+ // code-path is taken that always creates a new allocation and copies the
+ // string into that using (effectively) `length` and `characterAtIndex:`.
+ //
+ // As a result, the lifetime of the returned pointer is either the same as
+ // the passed-in `NSString` OR the lifetime of the current / innermost
+ // `@autoreleasepool`.
+ //
+ // Furthermore, we can allow creating a `&str` from `&obj`, even if the
+ // string is originally a `NSMutableString` which may be mutated later on,
+ // since in that case the lifetime will be tied to the pool and not the
+ // string.
+ let bytes: *const c_char = unsafe { msg_send![obj, UTF8String] };
+ let bytes: *const u8 = bytes.cast();
+
+ // SAFETY: Caller ensures that the object is an instance of `NSString`.
+ let len = unsafe { nsstring_len(obj) };
+
+ // SAFETY:
+ // The held AutoreleasePool is the innermost, and the reference is
+ // constrained both by the pool and the NSString.
+ //
+ // `len` is the length of the string in the UTF-8 encoding.
+ //
+ // `bytes` is a null-terminated C string (with length = len + 1), so
+ // it is never a NULL pointer.
+ let bytes: &'r [u8] = unsafe { slice::from_raw_parts(bytes, len) };
+
+ // SAFETY: The bytes are valid UTF-8.
+ #[cfg(not(debug_assertions))]
+ unsafe {
+ str::from_utf8_unchecked(bytes)
+ }
+
+ #[cfg(debug_assertions)]
+ {
+ str::from_utf8(bytes).expect("invalid UTF-8 in NSString")
+ }
+}
diff --git a/third_party/rust/objc2/src/runtime/bool.rs b/third_party/rust/objc2/src/runtime/bool.rs
@@ -0,0 +1,335 @@
+#![allow(clippy::upper_case_acronyms)]
+use core::{fmt, hash};
+
+use crate::encode::{Encode, Encoding, RefEncode};
+
+// Apple's objc4.
+//
+// Don't be fooled by the backup definition in `objc.h`; __OBJC_BOOL_IS_BOOL
+// is always defined by `clang` when compiling Objective-C sources. The below
+// cfgs are determined experimentally via cross compiling.
+//
+// See also <https://www.jviotti.com/2024/01/05/is-objective-c-bool-a-boolean-type-it-depends.html>.
+#[cfg(all(not(feature = "gnustep-1-7"), not(feature = "unstable-objfw")))]
+mod inner {
+ // __OBJC_BOOL_IS_BOOL
+ #[cfg(any(
+ // aarch64-apple-*
+ target_arch = "aarch64",
+ // + x86_64-apple-ios (i.e. the simulator) (but not x86_64-apple-ios-macabi)
+ all(target_os = "ios", target_pointer_width = "64", not(target_abi_macabi)),
+ // + x86_64-apple-tvos
+ all(target_os = "tvos", target_pointer_width = "64"),
+ // + *-apple-watchos
+ target_os = "watchos",
+ ))]
+ // C: _Bool
+ pub(crate) type BOOL = bool;
+
+ // Inverse of the above
+ #[cfg(not(any(
+ target_arch = "aarch64",
+ all(target_os = "ios", target_pointer_width = "64", not(target_abi_macabi)),
+ all(target_os = "tvos", target_pointer_width = "64"),
+ target_os = "watchos",
+ )))]
+ // C: (explicitly) signed char
+ pub(crate) type BOOL = i8;
+}
+
+// GNUStep's and Microsoft's libobjc2
+#[cfg(all(
+ feature = "gnustep-1-7",
+ feature = "unstable-gnustep-strict-apple-compat"
+))]
+mod inner {
+ // C: (explicitly) signed char
+ pub(crate) type BOOL = i8;
+}
+
+#[cfg(all(
+ feature = "gnustep-1-7",
+ not(feature = "unstable-gnustep-strict-apple-compat")
+))]
+mod inner {
+ // windows && !32bit-MinGW
+ #[cfg(all(windows, not(all(target_pointer_width = "64", target_env = "gnu"))))]
+ pub(crate) type BOOL = core::ffi::c_int;
+
+ // The inverse
+ #[cfg(not(all(windows, not(all(target_pointer_width = "64", target_env = "gnu")))))]
+ // C: unsigned char
+ pub(crate) type BOOL = u8;
+}
+
+// ObjFW
+#[cfg(feature = "unstable-objfw")]
+mod inner {
+ // Defined in ObjFW-RT.h
+ // C: signed char
+ // This has changed since v0.90, but we don't support that yet.
+ pub(crate) type BOOL = i8;
+
+ // Note that ObjFW usually uses `bool` in return types, but that doesn't
+ // change the ABI, so we'll use `BOOL` there as well, for ease of use.
+}
+
+/// The Objective-C `BOOL` type.
+///
+/// The type of `BOOL` varies across platforms, so we expose this wrapper. It
+/// is intended that you convert this into a Rust [`bool`] with the
+/// [`Bool::as_bool`] method as soon as possible.
+///
+/// This is FFI-safe and can be used directly with `msg_send!` and `extern`
+/// functions as a substitute for `BOOL` in Objective-C. If your Objective-C
+/// code uses C99 `_Bool`, you should use a `#[repr(transparent)]` wrapper
+/// around `bool` instead.
+///
+/// Note that this is able to contain more states than `bool` on some
+/// platforms, but these cases should not be relied on! The comparison traits
+/// `PartialEq`, `PartialOrd` etc. will completely ignore these states.
+///
+/// See also the [corresponding documentation entry][docs].
+///
+/// [docs]: https://developer.apple.com/documentation/objectivec/bool?language=objc
+#[repr(transparent)]
+#[derive(Copy, Clone, Default)]
+pub struct Bool {
+ value: inner::BOOL,
+}
+
+impl Bool {
+ /// The equivalent of [`true`] for Objective-C's `BOOL` type.
+ #[allow(clippy::unnecessary_cast)]
+ pub const YES: Self = Self::from_raw(true as inner::BOOL); // true -> 1
+
+ /// The equivalent of [`false`] for Objective-C's `BOOL` type.
+ #[allow(clippy::unnecessary_cast)]
+ pub const NO: Self = Self::from_raw(false as inner::BOOL); // false -> 0
+
+ /// Creates an Objective-C boolean from a Rust boolean.
+ #[inline]
+ pub const fn new(value: bool) -> Self {
+ // true as BOOL => 1 (YES)
+ // false as BOOL => 0 (NO)
+ let value = value as inner::BOOL;
+ Self { value }
+ }
+
+ /// Creates this from a raw boolean value.
+ ///
+ /// Avoid this, and instead use `Bool` in the raw FFI signature.
+ #[inline]
+ pub const fn from_raw(value: inner::BOOL) -> Self {
+ Self { value }
+ }
+
+ /// Retrieves the inner boolean type.
+ ///
+ /// Avoid this, and instead use `Bool` in the raw FFI signature.
+ #[inline]
+ pub const fn as_raw(self) -> inner::BOOL {
+ self.value
+ }
+
+ /// Returns `true` if `self` is [`NO`][Self::NO].
+ ///
+ /// You should prefer using [`as_bool`][Self::as_bool].
+ #[inline]
+ pub const fn is_false(self) -> bool {
+ !self.as_bool()
+ }
+
+ /// Returns `true` if `self` is not [`NO`][Self::NO].
+ ///
+ /// You should prefer using [`as_bool`][Self::as_bool].
+ #[inline]
+ pub const fn is_true(self) -> bool {
+ self.as_bool()
+ }
+
+ /// Converts this into the [`bool`] equivalent.
+ #[inline]
+ pub const fn as_bool(self) -> bool {
+ // Always compare with 0 (NO)
+ // This is what happens with the `!` operator / when using `if` in C.
+ self.value != (false as inner::BOOL)
+ }
+}
+
+impl From<bool> for Bool {
+ #[inline]
+ fn from(b: bool) -> Bool {
+ Bool::new(b)
+ }
+}
+
+impl From<Bool> for bool {
+ #[inline]
+ fn from(b: Bool) -> bool {
+ b.as_bool()
+ }
+}
+
+impl fmt::Debug for Bool {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(if self.as_bool() { "YES" } else { "NO" })
+ }
+}
+
+// Implement comparison traits by first converting to a boolean.
+
+impl PartialEq for Bool {
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ self.as_bool() == other.as_bool()
+ }
+}
+
+impl Eq for Bool {}
+
+impl hash::Hash for Bool {
+ #[inline]
+ fn hash<H: hash::Hasher>(&self, state: &mut H) {
+ self.as_bool().hash(state);
+ }
+}
+
+impl PartialOrd for Bool {
+ #[inline]
+ fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for Bool {
+ #[inline]
+ fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+ self.as_bool().cmp(&other.as_bool())
+ }
+}
+
+trait Helper {
+ const __ENCODING: Encoding;
+}
+
+impl<T: Encode> Helper for T {
+ const __ENCODING: Encoding = T::ENCODING;
+}
+
+impl Helper for bool {
+ const __ENCODING: Encoding = Encoding::Bool;
+}
+
+// SAFETY: `Bool` is `repr(transparent)`.
+unsafe impl Encode for Bool {
+ // i8::__ENCODING == Encoding::Char
+ // u8::__ENCODING == Encoding::UChar
+ // bool::__ENCODING == Encoding::Bool
+ // i32::__ENCODING == Encoding::Int
+ const ENCODING: Encoding = inner::BOOL::__ENCODING;
+}
+
+// Note that we shouldn't delegate to `BOOL`'s `ENCODING_REF` since `BOOL` is
+// sometimes `i8`/`u8`, and their `ENCODING_REF`s are `Encoding::String`,
+// which is incorrect for `BOOL`:
+//
+// ```objc
+// @encode(BOOL); // -> "c", "C" or "B"
+// @encode(BOOL*); // -> "^c", "^C" or "^B"
+// @encode(char); // -> "c" or "C"
+// @encode(char*); // -> "*"
+// ```
+unsafe impl RefEncode for Bool {
+ const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::__macro_helpers::{ConvertArgument, ConvertReturn};
+ use alloc::format;
+
+ #[test]
+ fn test_basic() {
+ let b = Bool::new(true);
+ assert!(b.as_bool());
+ assert!(b.is_true());
+ assert!(!b.is_false());
+ assert!(bool::from(b));
+ assert_eq!(b.as_raw() as usize, 1);
+
+ let b = Bool::new(false);
+ assert!(!b.as_bool());
+ assert!(!b.is_true());
+ assert!(b.is_false());
+ assert!(!bool::from(b));
+ assert_eq!(b.as_raw() as usize, 0);
+ }
+
+ #[test]
+ fn test_associated_constants() {
+ let b = Bool::YES;
+ assert!(b.as_bool());
+ assert!(b.is_true());
+ assert_eq!(b.as_raw() as usize, 1);
+
+ let b = Bool::NO;
+ assert!(!b.as_bool());
+ assert!(b.is_false());
+ assert_eq!(b.as_raw() as usize, 0);
+ }
+
+ #[test]
+ fn test_encode() {
+ assert_eq!(bool::__ENCODING, Encoding::Bool);
+
+ assert_eq!(
+ <bool as ConvertArgument>::__Inner::__ENCODING,
+ <bool as ConvertArgument>::__Inner::ENCODING
+ );
+ assert_eq!(
+ <bool as ConvertReturn<()>>::Inner::__ENCODING,
+ <bool as ConvertReturn<()>>::Inner::ENCODING
+ );
+ }
+
+ #[test]
+ fn test_impls() {
+ let b: Bool = Default::default();
+ assert!(!b.as_bool());
+ assert!(b.is_false());
+
+ assert!(Bool::from(true).as_bool());
+ assert!(Bool::from(true).is_true());
+ assert!(Bool::from(false).is_false());
+
+ assert!(Bool::from(true).is_true());
+ assert!(Bool::from(false).is_false());
+
+ assert_eq!(Bool::new(true), Bool::new(true));
+ assert_eq!(Bool::new(false), Bool::new(false));
+
+ assert!(Bool::new(false) < Bool::new(true));
+ }
+
+ #[test]
+ fn test_debug() {
+ assert_eq!(format!("{:?}", Bool::from(true)), "YES");
+ assert_eq!(format!("{:?}", Bool::from(false)), "NO");
+ }
+
+ #[test]
+ // Test on platform where we know the type of BOOL
+ #[cfg(all(target_vendor = "apple", target_os = "macos", target_arch = "x86_64"))]
+ fn test_outside_normal() {
+ let b = Bool::from_raw(42);
+ assert!(b.is_true());
+ assert!(!b.is_false());
+ assert_eq!(b.as_raw(), 42);
+
+ // PartialEq ignores extra data
+ assert_eq!(b, Bool::new(true));
+ assert_ne!(b, Bool::new(false));
+ }
+}
diff --git a/third_party/rust/objc2/src/runtime/define.rs b/third_party/rust/objc2/src/runtime/define.rs
@@ -0,0 +1,1102 @@
+//! # Dynamically creating classes and protocols.
+use alloc::ffi::CString;
+use alloc::format;
+use alloc::string::ToString;
+use core::ffi::CStr;
+use core::mem;
+use core::mem::ManuallyDrop;
+use core::ptr;
+use core::ptr::NonNull;
+
+use crate::encode::{Encode, EncodeArguments, EncodeReturn, Encoding};
+use crate::ffi;
+use crate::runtime::{AnyClass, AnyObject, AnyProtocol, Bool, Imp, MethodImplementation, Sel};
+use crate::sel;
+use crate::Message;
+
+fn method_type_encoding(ret: &Encoding, args: &[Encoding]) -> CString {
+ // First two arguments are always self and the selector
+ let mut types = format!("{ret}{}{}", <*mut AnyObject>::ENCODING, Sel::ENCODING);
+ for enc in args {
+ use core::fmt::Write;
+ write!(&mut types, "{enc}").unwrap();
+ }
+ CString::new(types).unwrap()
+}
+
+trait Log2Alignment {
+ const LOG2_ALIGNMENT: u8;
+}
+
+impl<T> Log2Alignment for T {
+ const LOG2_ALIGNMENT: u8 = {
+ let align = mem::align_of::<T>();
+ assert!(
+ align.count_ones() == 1,
+ "alignment required to be a power of 2"
+ );
+ // log2 of a power of 2 is the number of trailing zeros
+ align.trailing_zeros() as u8
+ };
+}
+
+/// A type for creating a new class and adding new methods and ivars to it
+/// before registering it.
+///
+/// **Note**: You likely don't need the dynamicism that this provides!
+/// Consider using the [`define_class!`][crate::define_class] macro instead.
+///
+///
+/// # Example
+///
+/// Create a class named `MyNumber` that has one ivar, a `u32` named `_number`
+/// and a few constructor methods and methods for interfacing with the number
+/// (using interior mutability, as is common for Objective-C objects).
+///
+/// ```
+/// use core::cell::Cell;
+///
+/// use objc2::rc::Retained;
+/// use objc2::runtime::{AnyClass, AnyObject, ClassBuilder, NSObject, Sel};
+/// use objc2::{sel, msg_send, ClassType};
+///
+/// fn register_class() -> &'static AnyClass {
+/// // Inherit from NSObject
+/// let mut builder = ClassBuilder::new(c"MyNumber", NSObject::class())
+/// .expect("a class with the name MyNumber likely already exists");
+///
+/// // Add an instance variable of type `Cell<u32>`
+/// builder.add_ivar::<Cell<u32>>(c"_number");
+///
+/// // Add an Objective-C method for initializing an instance with a number
+/// //
+/// // We "cheat" a bit here, and use `AnyObject` instead of `NSObject`,
+/// // since only the former is allowed to be a mutable receiver (which is
+/// // always safe in `init` methods, but not in others).
+/// unsafe extern "C-unwind" fn init_with_number(
+/// this: &mut AnyObject,
+/// _cmd: Sel,
+/// number: u32,
+/// ) -> Option<&mut AnyObject> {
+/// let this: Option<&mut AnyObject> = msg_send![super(this, NSObject::class()), init];
+/// this.map(|this| {
+/// let ivar = AnyClass::get(c"MyNumber").unwrap().instance_variable(c"_number").unwrap();
+/// // SAFETY: The ivar is added with the same type above
+/// *ivar.load_mut::<Cell<u32>>(this) = Cell::new(number);
+/// this
+/// })
+/// }
+/// unsafe {
+/// builder.add_method(
+/// sel!(initWithNumber:),
+/// init_with_number as unsafe extern "C-unwind" fn(_, _, _) -> _,
+/// );
+/// }
+///
+/// // Add convenience method for getting a new instance with the number
+/// extern "C-unwind" fn with_number(
+/// cls: &AnyClass,
+/// _cmd: Sel,
+/// number: u32,
+/// ) -> *mut NSObject {
+/// let obj: Option<Retained<NSObject>> = unsafe {
+/// msg_send![
+/// msg_send![cls, alloc],
+/// initWithNumber: number,
+/// ]
+/// };
+/// obj.map(Retained::autorelease_return).unwrap_or(std::ptr::null_mut())
+/// }
+/// unsafe {
+/// builder.add_class_method(
+/// sel!(withNumber:),
+/// with_number as extern "C-unwind" fn(_, _, _) -> _,
+/// );
+/// }
+///
+/// // Add an Objective-C method for setting the number
+/// extern "C-unwind" fn my_number_set(this: &NSObject, _cmd: Sel, number: u32) {
+/// let ivar = AnyClass::get(c"MyNumber").unwrap().instance_variable(c"_number").unwrap();
+/// // SAFETY: The ivar is added with the same type above
+/// unsafe { ivar.load::<Cell<u32>>(this) }.set(number);
+/// }
+/// unsafe {
+/// builder.add_method(sel!(setNumber:), my_number_set as extern "C-unwind" fn(_, _, _));
+/// }
+///
+/// // Add an Objective-C method for getting the number
+/// extern "C-unwind" fn my_number_get(this: &NSObject, _cmd: Sel) -> u32 {
+/// let ivar = AnyClass::get(c"MyNumber").unwrap().instance_variable(c"_number").unwrap();
+/// // SAFETY: The ivar is added with the same type above
+/// unsafe { ivar.load::<Cell<u32>>(this) }.get()
+/// }
+/// unsafe {
+/// builder.add_method(sel!(number), my_number_get as extern "C-unwind" fn(_, _) -> _);
+/// }
+///
+/// builder.register()
+/// }
+///
+/// // Usage
+///
+/// // Note: you should only do class registration once! This can be ensured
+/// // with `std::sync::Once` or the `once_cell` crate.
+/// let cls = register_class();
+///
+/// let obj: Retained<NSObject> = unsafe {
+/// msg_send![cls, withNumber: 42u32]
+/// };
+///
+/// let n: u32 = unsafe { msg_send![&obj, number] };
+/// assert_eq!(n, 42);
+///
+/// let _: () = unsafe { msg_send![&obj, setNumber: 12u32] };
+/// let n: u32 = unsafe { msg_send![&obj, number] };
+/// assert_eq!(n, 12);
+/// ```
+#[derive(Debug)]
+pub struct ClassBuilder {
+ // Note: Don't ever construct a &mut AnyClass, since it is possible to
+ // get this pointer using `AnyClass::classes`!
+ cls: NonNull<AnyClass>,
+}
+
+// SAFETY: The stuff that touch global state does so using locks internally.
+//
+// Modifying the class itself can only be done through `&mut`, so Sync is
+// safe (e.g. we can't accidentally call `add_ivar` at the same time from two
+// different threads).
+//
+// (Though actually, that would be safe since the entire runtime is locked
+// when doing so...).
+//
+// Finally, there are no requirements that the class must be registered on the
+// same thread that allocated it (so Send is safe).
+unsafe impl Send for ClassBuilder {}
+unsafe impl Sync for ClassBuilder {}
+
+impl ClassBuilder {
+ fn as_mut_ptr(&mut self) -> *mut AnyClass {
+ self.cls.as_ptr()
+ }
+
+ #[allow(unused)]
+ pub(crate) fn superclass(&self) -> Option<&AnyClass> {
+ // SAFETY: Though the class is not finalized, `class_getSuperclass` is
+ // still safe to call.
+ unsafe { AnyClass::superclass_raw(self.cls.as_ptr()) }
+ }
+
+ #[allow(unused)]
+ fn name(&self) -> &CStr {
+ // SAFETY: Same as `superclass`
+ unsafe { AnyClass::name_raw(self.cls.as_ptr()) }
+ }
+
+ #[inline]
+ fn with_superclass(name: &CStr, superclass: Option<&AnyClass>) -> Option<Self> {
+ let super_ptr = superclass.map_or(ptr::null(), |c| c).cast();
+ let cls = unsafe { ffi::objc_allocateClassPair(super_ptr, name.as_ptr(), 0) };
+ NonNull::new(cls).map(|cls| Self { cls })
+ }
+
+ /// Constructs a [`ClassBuilder`] with the given name and superclass.
+ ///
+ /// Returns [`None`] if the class couldn't be allocated, or a class with
+ /// that name already exist.
+ #[inline]
+ pub fn new(name: &CStr, superclass: &AnyClass) -> Option<Self> {
+ Self::with_superclass(name, Some(superclass))
+ }
+
+ /// Constructs a [`ClassBuilder`] that will construct a new root class
+ /// with the given name.
+ ///
+ /// Returns [`None`] if the class couldn't be allocated.
+ ///
+ /// An implementation for `+initialize` must also be given; the runtime
+ /// calls this method for all classes, so it must be defined on root
+ /// classes.
+ ///
+ /// Note that implementing a root class is not a simple endeavor!
+ /// For example, your class probably cannot be passed to Cocoa code unless
+ /// the entire `NSObject` protocol is implemented.
+ /// Functionality it expects, like implementations of `-retain` and
+ /// `-release` used by ARC, will not be present otherwise.
+ pub fn root<F>(name: &CStr, initialize_fn: F) -> Option<Self>
+ where
+ F: MethodImplementation<Callee = AnyClass, Arguments = (), Return = ()>,
+ {
+ Self::with_superclass(name, None).map(|mut this| {
+ unsafe { this.add_class_method(sel!(initialize), initialize_fn) };
+ this
+ })
+ }
+
+ /// Adds a method with the given name and implementation.
+ ///
+ ///
+ /// # Panics
+ ///
+ /// Panics if the method wasn't successfully added (e.g. a method with that
+ /// name already exists).
+ ///
+ /// May also panic if the method was detected to be invalid in some way;
+ /// for example if `debug_assertions` are enabled and the method is
+ /// overriding another method, we verify that their encodings are equal.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that the types match those that are expected
+ /// when the method is invoked from Objective-C.
+ pub unsafe fn add_method<T, F>(&mut self, sel: Sel, func: F)
+ where
+ T: Message + ?Sized,
+ F: MethodImplementation<Callee = T>,
+ {
+ unsafe {
+ self.add_method_inner(
+ sel,
+ F::Arguments::ENCODINGS,
+ &F::Return::ENCODING_RETURN,
+ func.__imp(),
+ );
+ }
+ }
+
+ unsafe fn add_method_inner(
+ &mut self,
+ sel: Sel,
+ enc_args: &[Encoding],
+ enc_ret: &Encoding,
+ func: Imp,
+ ) {
+ let sel_args = sel.number_of_arguments();
+ assert_eq!(
+ sel_args,
+ enc_args.len(),
+ "selector {sel} accepts {sel_args} arguments, but function accepts {}",
+ enc_args.len(),
+ );
+
+ // Verify that, if the method is present on the superclass, that the
+ // encoding is correct.
+ #[cfg(all(debug_assertions, not(feature = "disable-encoding-assertions")))]
+ if let Some(superclass) = self.superclass() {
+ if let Some(method) = superclass.instance_method(sel) {
+ if let Err(err) = crate::verify::verify_method_signature(method, enc_args, enc_ret)
+ {
+ panic!(
+ "defined invalid method -[{} {sel}]: {err}",
+ self.name().to_string_lossy()
+ )
+ }
+ }
+ }
+
+ let types = method_type_encoding(enc_ret, enc_args);
+ let success = unsafe { ffi::class_addMethod(self.as_mut_ptr(), sel, func, types.as_ptr()) };
+ assert!(success.as_bool(), "failed to add method {sel}");
+ }
+
+ fn metaclass_mut(&mut self) -> *mut AnyClass {
+ unsafe { ffi::object_getClass(self.as_mut_ptr().cast()) as *mut AnyClass }
+ }
+
+ /// Adds a class method with the given name and implementation.
+ ///
+ ///
+ /// # Panics
+ ///
+ /// Panics in the same cases as [`add_method`][Self::add_method].
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that the types match those that are expected
+ /// when the method is invoked from Objective-C.
+ pub unsafe fn add_class_method<F>(&mut self, sel: Sel, func: F)
+ where
+ F: MethodImplementation<Callee = AnyClass>,
+ {
+ unsafe {
+ self.add_class_method_inner(
+ sel,
+ F::Arguments::ENCODINGS,
+ &F::Return::ENCODING_RETURN,
+ func.__imp(),
+ );
+ }
+ }
+
+ unsafe fn add_class_method_inner(
+ &mut self,
+ sel: Sel,
+ enc_args: &[Encoding],
+ enc_ret: &Encoding,
+ func: Imp,
+ ) {
+ let sel_args = sel.number_of_arguments();
+ assert_eq!(
+ sel_args,
+ enc_args.len(),
+ "selector {sel} accepts {sel_args} arguments, but function accepts {}",
+ enc_args.len(),
+ );
+
+ // Verify that, if the method is present on the superclass, that the
+ // encoding is correct.
+ #[cfg(all(debug_assertions, not(feature = "disable-encoding-assertions")))]
+ if let Some(superclass) = self.superclass() {
+ if let Some(method) = superclass.class_method(sel) {
+ if let Err(err) = crate::verify::verify_method_signature(method, enc_args, enc_ret)
+ {
+ panic!(
+ "defined invalid method +[{} {sel}]: {err}",
+ self.name().to_string_lossy()
+ )
+ }
+ }
+ }
+
+ let types = method_type_encoding(enc_ret, enc_args);
+ let success =
+ unsafe { ffi::class_addMethod(self.metaclass_mut(), sel, func, types.as_ptr()) };
+ assert!(success.as_bool(), "failed to add class method {sel}");
+ }
+
+ /// Adds an ivar with type `T` and the provided name.
+ ///
+ ///
+ /// # Panics
+ ///
+ /// If the ivar wasn't successfully added for some reason - this usually
+ /// happens if there already was an ivar with that name.
+ pub fn add_ivar<T: Encode>(&mut self, name: &CStr) {
+ // SAFETY: The encoding is correct
+ unsafe { self.add_ivar_inner::<T>(name, &T::ENCODING) }
+ }
+
+ pub(crate) unsafe fn add_ivar_inner<T>(&mut self, name: &CStr, encoding: &Encoding) {
+ unsafe { self.add_ivar_inner_mono(name, mem::size_of::<T>(), T::LOG2_ALIGNMENT, encoding) }
+ }
+
+ // Monomorphized version
+ unsafe fn add_ivar_inner_mono(
+ &mut self,
+ name: &CStr,
+ size: usize,
+ align: u8,
+ encoding: &Encoding,
+ ) {
+ let encoding = CString::new(encoding.to_string()).unwrap();
+
+ // Note: The Objective-C runtime contains functionality to do stuff
+ // with "instance variable layouts", but we don't have to touch any of
+ // that, it was only used in the garbage-collecting runtime.
+ //
+ // Note: On GNUStep, instance variables cannot have the same name
+ // on subclasses as it has on superclasses.
+ //
+ // See <https://github.com/gnustep/libobjc2/issues/246>
+ let success = unsafe {
+ ffi::class_addIvar(
+ self.as_mut_ptr(),
+ name.as_ptr(),
+ size,
+ align,
+ encoding.as_ptr(),
+ )
+ };
+ assert!(success.as_bool(), "failed to add ivar {name:?}");
+ }
+
+ /// Makes the class conform to the given protocol.
+ ///
+ /// This will also make the class conform to any super-protocols that the
+ /// given protocol may have.
+ ///
+ /// Returns whether the class did not already conform to the protocol.
+ /// This may commonly return false if you first add e.g.
+ /// `NSProgressReporting`, and then later try to add `NSObjectProtocol`,
+ /// which is a super-protocol thereof.
+ #[inline]
+ pub fn add_protocol(&mut self, proto: &AnyProtocol) -> bool {
+ let success = unsafe { ffi::class_addProtocol(self.as_mut_ptr(), proto) };
+ success.as_bool()
+ }
+
+ // fn add_property(&self, name: &CStr, attributes: &[ffi::objc_property_attribute_t]);
+
+ /// Registers the [`ClassBuilder`], consuming it, and returns a reference
+ /// to the newly registered [`AnyClass`].
+ #[inline]
+ pub fn register(self) -> &'static AnyClass {
+ // Forget self, otherwise the class will be disposed in drop
+ let mut this = ManuallyDrop::new(self);
+ unsafe { ffi::objc_registerClassPair(this.as_mut_ptr()) };
+ unsafe { this.cls.as_ref() }
+ }
+}
+
+impl Drop for ClassBuilder {
+ #[inline]
+ fn drop(&mut self) {
+ // Disposing un-registered classes doesn't work properly on GNUStep,
+ // so we register the class before disposing it.
+ //
+ // Doing it this way is _technically_ a race-condition, since other
+ // code could read e.g. `AnyClass::classes()` and then pick the class
+ // before it got disposed - but let's not worry about that for now.
+ #[cfg(feature = "gnustep-1-7")]
+ unsafe {
+ ffi::objc_registerClassPair(self.as_mut_ptr());
+ }
+
+ unsafe { ffi::objc_disposeClassPair(self.as_mut_ptr()) }
+ }
+}
+
+/// A type for creating a new protocol and adding new methods to it
+/// before registering it.
+#[derive(Debug)]
+pub struct ProtocolBuilder {
+ proto: NonNull<AnyProtocol>,
+}
+
+// SAFETY: Similar to ClassBuilder
+unsafe impl Send for ProtocolBuilder {}
+unsafe impl Sync for ProtocolBuilder {}
+
+impl ProtocolBuilder {
+ fn as_mut_ptr(&mut self) -> *mut AnyProtocol {
+ self.proto.as_ptr()
+ }
+
+ /// Constructs a [`ProtocolBuilder`] with the given name.
+ ///
+ /// Returns [`None`] if the protocol couldn't be allocated.
+ ///
+ ///
+ /// # Panics
+ ///
+ /// Panics if the name contains an internal NULL byte.
+ #[inline]
+ pub fn new(name: &CStr) -> Option<Self> {
+ let proto = unsafe { ffi::objc_allocateProtocol(name.as_ptr()) };
+ NonNull::new(proto.cast()).map(|proto| Self { proto })
+ }
+
+ fn add_method_description_inner(
+ &mut self,
+ sel: Sel,
+ enc_args: &[Encoding],
+ enc_ret: &Encoding,
+ required: bool,
+ instance_method: bool,
+ ) {
+ let sel_args = sel.number_of_arguments();
+ assert_eq!(
+ sel_args,
+ enc_args.len(),
+ "selector {sel} accepts {sel_args} arguments, but function accepts {}",
+ enc_args.len(),
+ );
+ let types = method_type_encoding(enc_ret, enc_args);
+ unsafe {
+ ffi::protocol_addMethodDescription(
+ self.as_mut_ptr(),
+ sel,
+ types.as_ptr(),
+ Bool::new(required),
+ Bool::new(instance_method),
+ );
+ }
+ }
+
+ /// Add an instance method with a given description.
+ pub fn add_method_description<Args, Ret>(&mut self, sel: Sel, required: bool)
+ where
+ Args: EncodeArguments,
+ Ret: EncodeReturn,
+ {
+ self.add_method_description_inner(
+ sel,
+ Args::ENCODINGS,
+ &Ret::ENCODING_RETURN,
+ required,
+ true,
+ );
+ }
+
+ /// Add a class method with a given description.
+ pub fn add_class_method_description<Args, Ret>(&mut self, sel: Sel, required: bool)
+ where
+ Args: EncodeArguments,
+ Ret: EncodeReturn,
+ {
+ self.add_method_description_inner(
+ sel,
+ Args::ENCODINGS,
+ &Ret::ENCODING_RETURN,
+ required,
+ false,
+ );
+ }
+
+ /// Adds a requirement on another protocol.
+ pub fn add_protocol(&mut self, proto: &AnyProtocol) {
+ unsafe { ffi::protocol_addProtocol(self.as_mut_ptr(), proto) };
+ }
+
+ /// Registers the [`ProtocolBuilder`], consuming it and returning a reference
+ /// to the newly registered [`AnyProtocol`].
+ pub fn register(mut self) -> &'static AnyProtocol {
+ unsafe {
+ ffi::objc_registerProtocol(self.as_mut_ptr());
+ self.proto.as_ref()
+ }
+ }
+}
+
+impl Drop for ProtocolBuilder {
+ #[inline]
+ fn drop(&mut self) {
+ // We implement Drop to communicate to the type-system that this type
+ // may drop in the future (once Apple add some way of disposing
+ // protocols).
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::hash::Hasher;
+ use std::collections::hash_map::DefaultHasher;
+ use std::hash::Hash;
+
+ use memoffset::offset_of;
+
+ use super::*;
+ use crate::encode::RefEncode;
+ use crate::rc::Retained;
+ use crate::runtime::{NSObject, NSObjectProtocol};
+ use crate::{define_class, extern_methods, msg_send, test_utils, ClassType, ProtocolType};
+
+ // TODO: Remove once c"" strings are in MSRV
+ fn c(s: &str) -> CString {
+ CString::new(s).unwrap()
+ }
+
+ #[test]
+ fn test_alignment() {
+ assert_eq!(<()>::LOG2_ALIGNMENT, 0);
+
+ assert_eq!(u8::LOG2_ALIGNMENT, 0);
+ assert_eq!(u16::LOG2_ALIGNMENT, 1);
+ assert_eq!(u32::LOG2_ALIGNMENT, 2);
+
+ assert_eq!(
+ u64::LOG2_ALIGNMENT,
+ if cfg!(target_pointer_width = "64") {
+ 3
+ } else {
+ 2
+ }
+ );
+
+ #[repr(align(16))]
+ struct Align16;
+ assert_eq!(Align16::LOG2_ALIGNMENT, 4);
+
+ #[repr(align(32))]
+ struct Align32;
+ assert_eq!(Align32::LOG2_ALIGNMENT, 5);
+
+ #[repr(align(64))]
+ struct Align64;
+ assert_eq!(Align64::LOG2_ALIGNMENT, 6);
+
+ #[repr(align(128))]
+ struct Align128;
+ assert_eq!(Align128::LOG2_ALIGNMENT, 7);
+
+ #[repr(align(256))]
+ struct Align256;
+ assert_eq!(Align256::LOG2_ALIGNMENT, 8);
+
+ #[repr(align(536870912))]
+ struct Align536870912;
+ assert_eq!(Align536870912::LOG2_ALIGNMENT, 29);
+ }
+
+ #[test]
+ fn test_classbuilder_duplicate() {
+ let cls = test_utils::custom_class();
+ let builder = ClassBuilder::new(&c("TestClassBuilderDuplicate"), cls).unwrap();
+ let _ = builder.register();
+
+ assert!(ClassBuilder::new(&c("TestClassBuilderDuplicate"), cls).is_none());
+ }
+
+ #[test]
+ #[should_panic = "failed to add ivar \"xyz\""]
+ fn duplicate_ivar() {
+ let cls = test_utils::custom_class();
+ let mut builder = ClassBuilder::new(&c("TestClassBuilderDuplicateIvar"), cls).unwrap();
+
+ builder.add_ivar::<i32>(&c("xyz"));
+ // Should panic:
+ builder.add_ivar::<i32>(&c("xyz"));
+ }
+
+ #[test]
+ #[should_panic = "failed to add method xyz"]
+ fn duplicate_method() {
+ let cls = test_utils::custom_class();
+ let mut builder = ClassBuilder::new(&c("TestClassBuilderDuplicateMethod"), cls).unwrap();
+
+ extern "C" fn xyz(_this: &NSObject, _cmd: Sel) {}
+
+ unsafe {
+ builder.add_method(sel!(xyz), xyz as extern "C" fn(_, _));
+ // Should panic:
+ builder.add_method(sel!(xyz), xyz as extern "C" fn(_, _));
+ }
+ }
+
+ #[test]
+ #[should_panic = "selector xyz: accepts 1 arguments, but function accepts 0"]
+ fn wrong_arguments() {
+ let cls = test_utils::custom_class();
+ let mut builder = ClassBuilder::new(&c("TestClassBuilderWrongArguments"), cls).unwrap();
+
+ extern "C" fn xyz(_this: &NSObject, _cmd: Sel) {}
+
+ unsafe {
+ // Should panic:
+ builder.add_method(sel!(xyz:), xyz as extern "C" fn(_, _));
+ }
+ }
+
+ #[test]
+ #[cfg_attr(
+ all(debug_assertions, not(feature = "disable-encoding-assertions")),
+ should_panic = "defined invalid method -[TestClassBuilderInvalidMethod foo]: expected return to have type code 'I', but found 's'"
+ )]
+ fn invalid_method() {
+ let cls = test_utils::custom_class();
+ let mut builder = ClassBuilder::new(&c("TestClassBuilderInvalidMethod"), cls).unwrap();
+
+ extern "C" fn foo(_this: &NSObject, _cmd: Sel) -> i16 {
+ 0
+ }
+
+ unsafe {
+ builder.add_method(sel!(foo), foo as extern "C" fn(_, _) -> _);
+ }
+ }
+
+ #[test]
+ #[cfg_attr(
+ all(
+ debug_assertions,
+ not(feature = "disable-encoding-assertions"),
+ not(feature = "relax-sign-encoding")
+ ),
+ should_panic = "defined invalid method +[TestClassBuilderInvalidClassMethod classFoo]: expected return to have type code 'I', but found 'i'"
+ )]
+ fn invalid_class_method() {
+ let cls = test_utils::custom_class();
+ let mut builder = ClassBuilder::new(&c("TestClassBuilderInvalidClassMethod"), cls).unwrap();
+
+ extern "C" fn class_foo(_cls: &AnyClass, _cmd: Sel) -> i32 {
+ 0
+ }
+
+ unsafe {
+ builder.add_class_method(sel!(classFoo), class_foo as extern "C" fn(_, _) -> _);
+ }
+ }
+
+ #[test]
+ fn inheriting_does_not_implement_protocols() {
+ let builder = ClassBuilder::new(
+ &c("TestClassBuilderInheritingDoesNotImplementProtocols"),
+ NSObject::class(),
+ )
+ .unwrap();
+
+ let cls = builder.register();
+ let conforms = cls.conforms_to(<dyn NSObjectProtocol>::protocol().unwrap());
+ if cfg!(feature = "gnustep-1-7") {
+ // FIXME: GNUStep works differently here!
+ assert!(conforms);
+ } else {
+ assert!(!conforms);
+ }
+ }
+
+ #[test]
+ fn inherit_nsobject_add_protocol() {
+ let mut builder = ClassBuilder::new(
+ &c("TestClassBuilderInheritNSObjectAddProtocol"),
+ NSObject::class(),
+ )
+ .unwrap();
+
+ let protocol = <dyn NSObjectProtocol>::protocol().unwrap();
+
+ // GNUStep is more eagerly returning false in the case where we
+ // inherit something that implements the protocol.
+ if cfg!(feature = "gnustep-1-7") {
+ assert!(!builder.add_protocol(protocol));
+ } else {
+ assert!(builder.add_protocol(protocol));
+ }
+
+ let cls = builder.register();
+ assert!(cls.conforms_to(protocol));
+ }
+
+ #[test]
+ fn duplicate_protocol() {
+ let cls = test_utils::custom_class();
+ let mut builder = ClassBuilder::new(&c("TestClassBuilderDuplicateProtocol"), cls).unwrap();
+
+ let protocol = ProtocolBuilder::new(&c("TestClassBuilderDuplicateProtocol"))
+ .unwrap()
+ .register();
+
+ assert!(builder.add_protocol(protocol));
+ assert!(!builder.add_protocol(protocol));
+ }
+
+ #[test]
+ fn add_protocol_subprotocol_ordering() {
+ // The value returned by `class_addProtocol` is inherently dependent
+ // on the order in which you add the super- and subprotocols.
+ let builder = ProtocolBuilder::new(&c("Superprotocol")).unwrap();
+ let superprotocol = builder.register();
+
+ let mut builder = ProtocolBuilder::new(&c("Subprotocol")).unwrap();
+ builder.add_protocol(superprotocol);
+ let subprotocol = builder.register();
+
+ let mut builder =
+ ClassBuilder::new(&c("AddProtocolSuperThenSub"), NSObject::class()).unwrap();
+ assert!(builder.add_protocol(superprotocol));
+ assert!(builder.add_protocol(subprotocol));
+ let _cls = builder.register();
+
+ let mut builder =
+ ClassBuilder::new(&c("AddProtocolSubThenSuper"), NSObject::class()).unwrap();
+ assert!(builder.add_protocol(subprotocol));
+ assert!(!builder.add_protocol(superprotocol));
+ let _cls = builder.register();
+ }
+
+ #[test]
+ fn test_classbuilder_drop() {
+ let cls = test_utils::custom_class();
+ let builder = ClassBuilder::new(&c("TestClassBuilderDrop"), cls).unwrap();
+ drop(builder);
+ // After we dropped the class, we can create a new one with the same name:
+ let _builder = ClassBuilder::new(&c("TestClassBuilderDrop"), cls).unwrap();
+ }
+
+ #[test]
+ fn test_custom_class() {
+ // Registering the custom class is in test_utils
+ let obj = test_utils::custom_object();
+ let _: () = unsafe { msg_send![&obj, setFoo: 13u32] };
+ let result: u32 = unsafe { msg_send![&obj, foo] };
+ assert_eq!(result, 13);
+ }
+
+ #[test]
+ fn test_in_all_classes() {
+ fn is_present(cls: *const AnyClass) -> bool {
+ // Check whether the class is present in AnyClass::classes()
+ AnyClass::classes().iter().any(|item| ptr::eq(cls, *item))
+ }
+
+ let superclass = test_utils::custom_class();
+ let builder = ClassBuilder::new(&c("TestFetchWhileCreatingClass"), superclass).unwrap();
+
+ if cfg!(all(
+ target_vendor = "apple",
+ any(target_arch = "aarch64", target_arch = "x86_64")
+ )) {
+ // It is IMO a bug that it is present here!
+ assert!(is_present(builder.cls.as_ptr().cast()));
+ } else {
+ assert!(!is_present(builder.cls.as_ptr().cast()));
+ }
+
+ let cls = builder.register();
+ assert!(is_present(cls));
+ }
+
+ #[test]
+ fn test_class_method() {
+ let cls = test_utils::custom_class();
+ let result: u32 = unsafe { msg_send![cls, classFoo] };
+ assert_eq!(result, 7);
+ }
+
+ // Proof-of-concept how we could make define_class! accept generic types.
+ #[test]
+ fn test_generic() {
+ struct GenericDefineClass<T>(T);
+
+ unsafe impl<T> RefEncode for GenericDefineClass<T> {
+ const ENCODING_REF: Encoding = Encoding::Object;
+ }
+
+ unsafe impl<T> Message for GenericDefineClass<T> {}
+
+ unsafe impl<T> ClassType for GenericDefineClass<T> {
+ type Super = NSObject;
+ type ThreadKind = <Self::Super as ClassType>::ThreadKind;
+ const NAME: &'static str = "GenericDefineClass";
+
+ #[inline]
+ fn as_super(&self) -> &Self::Super {
+ unimplemented!()
+ }
+
+ fn class() -> &'static AnyClass {
+ let superclass = NSObject::class();
+ let mut builder = ClassBuilder::new(&c(Self::NAME), superclass).unwrap();
+
+ unsafe {
+ builder.add_method(
+ sel!(generic),
+ <GenericDefineClass<T>>::generic as unsafe extern "C" fn(_, _),
+ );
+ }
+
+ builder.register()
+ }
+
+ const __INNER: () = ();
+
+ type __SubclassingType = Self;
+ }
+
+ impl<T> GenericDefineClass<T> {
+ extern "C" fn generic(&self, _cmd: Sel) {}
+ }
+
+ let _ = GenericDefineClass::<()>::class();
+ }
+
+ #[test]
+ fn test_inherited_nsobject_methods_work() {
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[name = "TestInheritedNSObjectMethodsWork"]
+ #[derive(Debug, PartialEq, Eq, Hash)]
+ struct Custom;
+ );
+
+ impl Custom {
+ extern_methods!(
+ #[unsafe(method(new))]
+ fn new() -> Retained<Self>;
+ );
+ }
+
+ let obj1 = Custom::new();
+ let obj2 = Custom::new();
+
+ // isEqual:
+ assert_eq!(obj1, obj1);
+ assert_ne!(obj1, obj2);
+
+ // description
+ let expected =
+ format!("Custom {{ super: <TestInheritedNSObjectMethodsWork: {obj1:p}>, ivars: () }}");
+ assert_eq!(format!("{obj1:?}"), expected);
+
+ // hash
+ let mut hashstate1 = DefaultHasher::new();
+ let mut hashstate2 = DefaultHasher::new();
+
+ obj1.hash(&mut hashstate1);
+ obj1.hash(&mut hashstate2);
+
+ assert_eq!(hashstate1.finish(), hashstate2.finish());
+
+ let mut hashstate2 = DefaultHasher::new();
+ obj2.hash(&mut hashstate2);
+ assert_ne!(hashstate1.finish(), hashstate2.finish());
+
+ // isKindOfClass:
+ assert!(obj1.isKindOfClass(NSObject::class()));
+ assert!(obj1.isKindOfClass(Custom::class()));
+ assert!((**obj1).isKindOfClass(Custom::class()));
+ }
+
+ #[test]
+ #[cfg_attr(
+ feature = "gnustep-1-7",
+ ignore = "ivars cannot have the same name on GNUStep"
+ )]
+ fn test_ivar_sizing() {
+ #[repr(align(16))]
+ struct U128align16 {
+ _inner: [u64; 2],
+ }
+
+ unsafe impl Encode for U128align16 {
+ const ENCODING: Encoding = <[u64; 2]>::ENCODING;
+ }
+
+ let mut superclass =
+ ClassBuilder::new(&c("DefineClassDuplicateIvarSuperclass"), NSObject::class()).unwrap();
+ superclass.add_ivar::<u8>(&c("ivar1"));
+ superclass.add_ivar::<U128align16>(&c("ivar2"));
+ superclass.add_ivar::<u8>(&c("ivar3"));
+ superclass.add_ivar::<[u8; 0]>(&c("ivar4"));
+ let superclass = superclass.register();
+
+ let mut subclass =
+ ClassBuilder::new(&c("DefineClassDuplicateIvarSubclass"), superclass).unwrap();
+ // Try to overwrite instance variables
+ subclass.add_ivar::<i16>(&c("ivar1"));
+ subclass.add_ivar::<usize>(&c("ivar2"));
+ subclass.add_ivar::<*const AnyObject>(&c("ivar3"));
+ subclass.add_ivar::<usize>(&c("ivar4"));
+ let subclass = subclass.register();
+
+ // Test that ivar layout matches that of C
+ //
+ // In particular, ivars are not reordered, though any extra padding on
+ // superclasses are utilized on subclasses.
+ #[repr(C)]
+ struct NSObjectLayout {
+ isa: *const AnyClass,
+ }
+ assert_eq!(
+ NSObject::class().instance_size(),
+ mem::size_of::<NSObjectLayout>(),
+ );
+
+ #[repr(C)]
+ struct SuperLayout {
+ isa: *const AnyClass,
+ ivar1: u8,
+ // Padding (7 on 64bit, 11 on 32bit)
+ ivar2: U128align16,
+ ivar3: u8,
+ ivar4: [u8; 0],
+ // Padding (15 in Rust, 7 on 64bit, 3 on 32bit)
+ }
+ // Class's ivar size is only rounded up to a pointer-sized boundary,
+ // not all the way up to the maximum alignment.
+ //
+ // This is surprising, but actually fine, since Objective-C objects
+ // are never packed closely like Rust structs would be in an array.
+ assert_eq!(
+ superclass.instance_size(),
+ mem::size_of::<SuperLayout>() - 16 + mem::size_of::<*const AnyClass>(),
+ );
+
+ #[repr(C)]
+ struct SubLayout {
+ isa: *const AnyClass,
+ ivar1: u8,
+ // Padding (7 on 64bit, 11 on 32bit)
+ ivar2: U128align16,
+ ivar3: u8,
+ ivar4: [u8; 0],
+ // Padding (1)
+ ivar1_b: i16,
+ // Padding (4)
+ ivar2_b: usize,
+ ivar3_b: *const AnyObject,
+ ivar4_b: usize,
+ }
+ assert_eq!(subclass.instance_size(), mem::size_of::<SubLayout>());
+
+ let superclass_ivar1 = superclass.instance_variable(&c("ivar1")).unwrap();
+ let superclass_ivar2 = superclass.instance_variable(&c("ivar2")).unwrap();
+ let superclass_ivar3 = superclass.instance_variable(&c("ivar3")).unwrap();
+ let superclass_ivar4 = superclass.instance_variable(&c("ivar4")).unwrap();
+ let subclass_ivar1 = subclass.instance_variable(&c("ivar1")).unwrap();
+ let subclass_ivar2 = subclass.instance_variable(&c("ivar2")).unwrap();
+ let subclass_ivar3 = subclass.instance_variable(&c("ivar3")).unwrap();
+ let subclass_ivar4 = subclass.instance_variable(&c("ivar4")).unwrap();
+
+ // Ensure that duplicate names do not conflict
+ assert_ne!(superclass_ivar1, subclass_ivar1);
+ assert_ne!(superclass_ivar2, subclass_ivar2);
+ assert_ne!(superclass_ivar3, subclass_ivar3);
+ assert_ne!(superclass_ivar4, subclass_ivar4);
+
+ // Ensure that all offsets are as expected
+ assert_eq!(
+ superclass_ivar1.offset(),
+ offset_of!(SuperLayout, ivar1) as isize
+ );
+ assert_eq!(
+ superclass_ivar2.offset(),
+ offset_of!(SuperLayout, ivar2) as isize
+ );
+ assert_eq!(
+ superclass_ivar3.offset(),
+ offset_of!(SuperLayout, ivar3) as isize
+ );
+ assert_eq!(
+ superclass_ivar4.offset(),
+ offset_of!(SuperLayout, ivar4) as isize
+ );
+ assert_eq!(
+ subclass_ivar1.offset(),
+ offset_of!(SubLayout, ivar1_b) as isize
+ );
+ assert_eq!(
+ subclass_ivar2.offset(),
+ offset_of!(SubLayout, ivar2_b) as isize
+ );
+ assert_eq!(
+ subclass_ivar3.offset(),
+ offset_of!(SubLayout, ivar3_b) as isize
+ );
+ assert_eq!(
+ subclass_ivar4.offset(),
+ offset_of!(SubLayout, ivar4_b) as isize
+ );
+
+ // Ensure our ivar loading works correctly
+ let obj: Retained<NSObject> = unsafe { msg_send![subclass, new] };
+ let ptr = unsafe { *subclass_ivar3.load::<*const AnyObject>(&obj) };
+ assert!(ptr.is_null());
+
+ // Illustration of what goes wrong with the naive approach of loading
+ // the Ivar dynamically; in short, we can't be sure of which instance
+ // variable we're referring to here.
+ //
+ // let ivar = *obj.get_ivar::<u8>("ivar3");
+ }
+
+ #[test]
+ fn auto_name() {
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[ivars = ()]
+ struct AutoName;
+ );
+
+ let expected = format!(
+ "objc2::runtime::define::tests::AutoName{}",
+ env!("CARGO_PKG_VERSION")
+ );
+
+ let cls = AutoName::class();
+ assert_eq!(cls.name().to_str().unwrap(), expected);
+ assert_eq!(AutoName::NAME, expected);
+ }
+}
diff --git a/third_party/rust/objc2/src/runtime/malloc.rs b/third_party/rust/objc2/src/runtime/malloc.rs
@@ -0,0 +1,140 @@
+//! A minimal alternative to crates like `malloc_buf`, `mbox` and `malloced`.
+use core::ffi::c_char;
+use core::ffi::CStr;
+use core::fmt;
+use core::marker::PhantomData;
+use core::ops::Deref;
+use core::ptr::{self, NonNull};
+
+use crate::ffi;
+
+#[repr(transparent)]
+pub(crate) struct MallocSlice<T> {
+ ptr: NonNull<[T]>,
+ // Necessary for dropck
+ _p: PhantomData<[T]>,
+}
+
+impl<T> MallocSlice<T> {
+ pub(crate) unsafe fn from_array(mut ptr: *mut T, len: usize) -> Self {
+ // If the length is 0, the pointer is usually NULL, and as such we
+ // need to conjure some other pointer (slices are always non-null).
+ if len == 0 {
+ ptr = NonNull::dangling().as_ptr();
+ }
+
+ let ptr = ptr::slice_from_raw_parts_mut(ptr, len);
+ let ptr = NonNull::new(ptr).expect("tried to construct MallocSlice from a NULL pointer");
+ Self {
+ ptr,
+ _p: PhantomData,
+ }
+ }
+
+ fn len(&self) -> usize {
+ // TODO: Use `self.ptr.len()` once in MSRV
+ (**self).len()
+ }
+}
+
+impl<T> Drop for MallocSlice<T> {
+ #[allow(clippy::len_zero)]
+ fn drop(&mut self) {
+ // If the length is 0, then the pointer is dangling from `from_array`
+ // (since the length is immutable), and we can skip calling `free`.
+ if self.len() != 0 {
+ // SAFETY: We take ownership over the slice elements in
+ // `from_array`.
+ unsafe { ptr::drop_in_place(self.ptr.as_ptr()) };
+ // SAFETY: We take ownership over the pointer in `from_array`,
+ // and the pointer is valid if the length is non-zero.
+ unsafe { ffi::free(self.ptr.cast().as_ptr()) };
+ }
+ }
+}
+
+impl<T> Deref for MallocSlice<T> {
+ type Target = [T];
+
+ #[inline]
+ fn deref(&self) -> &[T] {
+ // SAFETY:
+ // - That the pointer is aligned, dereferenceable and initialized is
+ // ensured by the caller of `from_array` (which usually get it from
+ // some external API that will do this for you).
+ // - The lifetime is bound to the `MallocSlice`, which in turn ensures
+ // the pointer is valid until it is dropped.
+ unsafe { self.ptr.as_ref() }
+ }
+}
+
+impl<T: fmt::Debug> fmt::Debug for MallocSlice<T> {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(&**self, f)
+ }
+}
+
+impl<T> AsRef<[T]> for MallocSlice<T> {
+ #[inline]
+ fn as_ref(&self) -> &[T] {
+ self
+ }
+}
+
+#[repr(transparent)]
+pub(crate) struct MallocCStr {
+ ptr: NonNull<CStr>,
+}
+
+impl MallocCStr {
+ pub(crate) unsafe fn from_c_str(ptr: *mut c_char) -> Self {
+ if ptr.is_null() {
+ panic!("tried to construct MallocStr from a NULL pointer");
+ }
+ // SAFETY: We just checked that the pointer is not NULL.
+ //
+ // Further validity of the pointer is ensured by the caller.
+ let cstr = unsafe { CStr::from_ptr(ptr) };
+ // Note that we construct this `NonNull` from an immutable reference
+ // (there is not yet a `CStr::from_mut_ptr`).
+ //
+ // This means that we're (probably) no longer allowed to mutate the
+ // value, if that is desired for `MallocStr` in the future, then we'll
+ // have to implement this method a bit differently.
+ let ptr = NonNull::from(cstr);
+ Self { ptr }
+ }
+}
+
+impl Drop for MallocCStr {
+ #[inline]
+ fn drop(&mut self) {
+ // SAFETY: We take ownership in `from_c_str`.
+ unsafe { ffi::free(self.ptr.cast().as_ptr()) };
+ }
+}
+
+impl Deref for MallocCStr {
+ type Target = CStr;
+
+ #[inline]
+ fn deref(&self) -> &CStr {
+ // SAFETY: Same as `MallocSlice::deref`
+ unsafe { self.ptr.as_ref() }
+ }
+}
+
+impl fmt::Debug for MallocCStr {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(&**self, f)
+ }
+}
+
+impl AsRef<CStr> for MallocCStr {
+ #[inline]
+ fn as_ref(&self) -> &CStr {
+ self
+ }
+}
diff --git a/third_party/rust/objc2/src/runtime/message_receiver.rs b/third_party/rust/objc2/src/runtime/message_receiver.rs
@@ -0,0 +1,660 @@
+use core::ptr::NonNull;
+
+use crate::encode::{EncodeArguments, EncodeReturn, RefEncode};
+use crate::runtime::{AnyClass, AnyObject, Sel};
+use crate::Message;
+
+/// Wrap the given closure in `exception::catch` if the `catch-all` feature is
+/// enabled.
+///
+/// This is a macro to help with monomorphization when the feature is
+/// disabled, as well as improving the final stack trace (`#[track_caller]`
+/// doesn't really work on closures).
+#[cfg(not(feature = "catch-all"))]
+macro_rules! conditional_try {
+ (|| $expr:expr) => {
+ $expr
+ };
+}
+
+#[cfg(feature = "catch-all")]
+macro_rules! conditional_try {
+ (|| $expr:expr) => {{
+ let f = core::panic::AssertUnwindSafe(|| $expr);
+ match crate::exception::catch(f) {
+ Ok(r) => r,
+ Err(exception) => {
+ if let Some(exception) = exception {
+ panic!("uncaught {exception:?}\n{}", exception.stack_trace())
+ } else {
+ panic!("uncaught exception nil")
+ }
+ }
+ }
+ }};
+}
+
+// More information on how objc_msgSend works:
+// <https://web.archive.org/web/20200118080513/http://www.friday.com/bbum/2009/12/18/objc_msgsend-part-1-the-road-map/>
+// <https://www.mikeash.com/pyblog/objc_msgsends-new-prototype.html>
+// <https://www.mikeash.com/pyblog/friday-qa-2012-11-16-lets-build-objc_msgsend.html>
+#[cfg(all(target_vendor = "apple", not(feature = "gnustep-1-7")))]
+mod msg_send_primitive {
+ #[allow(unused_imports)]
+ use core::mem;
+
+ #[allow(unused_imports)]
+ use crate::encode::Encoding;
+ use crate::encode::{EncodeArguments, EncodeReturn};
+ use crate::ffi;
+ use crate::runtime::{AnyClass, AnyObject, Imp, Sel};
+
+ /// On the below architectures we can statically find the correct method to
+ /// call from the return type, by looking at its `EncodeReturn` impl.
+ #[allow(clippy::missing_safety_doc)]
+ unsafe trait MsgSendFn: EncodeReturn {
+ const MSG_SEND: Imp;
+ const MSG_SEND_SUPER: Imp;
+ }
+
+ #[cfg(target_arch = "aarch64")]
+ /// `objc_msgSend_stret` is not even available in arm64.
+ ///
+ /// <https://twitter.com/gparker/status/378079715824660480>
+ unsafe impl<T: EncodeReturn> MsgSendFn for T {
+ const MSG_SEND: Imp = ffi::objc_msgSend;
+ const MSG_SEND_SUPER: Imp = ffi::objc_msgSendSuper;
+ }
+
+ #[cfg(target_arch = "arm")]
+ /// Double-word sized fundamental data types don't use stret, but any
+ /// composite type larger than 4 bytes does.
+ ///
+ /// <https://web.archive.org/web/20191016000656/http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042f/IHI0042F_aapcs.pdf>
+ /// <https://developer.arm.com/documentation/ihi0042/latest>
+ /// <https://github.com/llvm/llvm-project/blob/llvmorg-17.0.6/clang/lib/CodeGen/Targets/ARM.cpp#L531>
+ unsafe impl<T: EncodeReturn> MsgSendFn for T {
+ const MSG_SEND: Imp = {
+ if let Encoding::LongLong | Encoding::ULongLong | Encoding::Double = T::ENCODING_RETURN
+ {
+ ffi::objc_msgSend
+ } else if mem::size_of::<T>() <= 4 {
+ ffi::objc_msgSend
+ } else {
+ ffi::objc_msgSend_stret
+ }
+ };
+ const MSG_SEND_SUPER: Imp = {
+ if let Encoding::LongLong | Encoding::ULongLong | Encoding::Double = T::ENCODING_RETURN
+ {
+ ffi::objc_msgSendSuper
+ } else if mem::size_of::<T>() <= 4 {
+ ffi::objc_msgSendSuper
+ } else {
+ ffi::objc_msgSendSuper_stret
+ }
+ };
+ }
+
+ #[cfg(target_arch = "x86")]
+ /// Structures 1 or 2 bytes in size are placed in EAX.
+ /// Structures 4 or 8 bytes in size are placed in: EAX and EDX.
+ /// Structures of other sizes are placed at the address supplied by the caller.
+ ///
+ /// <https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html>
+ /// <https://github.com/llvm/llvm-project/blob/llvmorg-17.0.6/clang/lib/CodeGen/Targets/X86.cpp#L472>
+ unsafe impl<T: EncodeReturn> MsgSendFn for T {
+ const MSG_SEND: Imp = {
+ // See https://github.com/apple-oss-distributions/objc4/blob/objc4-818.2/runtime/message.h#L156-L172
+ if let Encoding::Float | Encoding::Double | Encoding::LongDouble = T::ENCODING_RETURN {
+ ffi::objc_msgSend_fpret
+ } else if let 0 | 1 | 2 | 4 | 8 = mem::size_of::<T>() {
+ ffi::objc_msgSend
+ } else {
+ ffi::objc_msgSend_stret
+ }
+ };
+ const MSG_SEND_SUPER: Imp = {
+ if let 0 | 1 | 2 | 4 | 8 = mem::size_of::<T>() {
+ ffi::objc_msgSendSuper
+ } else {
+ ffi::objc_msgSendSuper_stret
+ }
+ };
+ }
+
+ #[cfg(target_arch = "x86_64")]
+ /// If the size of an object is larger than two eightbytes, it has class
+ /// MEMORY. If the type has class MEMORY, then the caller provides space for
+ /// the return value and passes the address of this storage.
+ ///
+ /// <https://www.uclibc.org/docs/psABI-x86_64.pdf>
+ /// <https://github.com/llvm/llvm-project/blob/llvmorg-17.0.6/clang/lib/CodeGen/Targets/X86.cpp#L2532>
+ unsafe impl<T: EncodeReturn> MsgSendFn for T {
+ const MSG_SEND: Imp = {
+ // See https://github.com/apple-oss-distributions/objc4/blob/objc4-818.2/runtime/message.h#L156-L172
+ if let Encoding::LongDouble = T::ENCODING_RETURN {
+ ffi::objc_msgSend_fpret
+ } else if let Encoding::LongDoubleComplex = T::ENCODING_RETURN {
+ ffi::objc_msgSend_fp2ret
+ } else if mem::size_of::<T>() <= 16 {
+ ffi::objc_msgSend
+ } else {
+ ffi::objc_msgSend_stret
+ }
+ };
+ const MSG_SEND_SUPER: Imp = {
+ if mem::size_of::<T>() <= 16 {
+ ffi::objc_msgSendSuper
+ } else {
+ ffi::objc_msgSendSuper_stret
+ }
+ };
+ }
+
+ #[inline]
+ #[track_caller]
+ pub(crate) unsafe fn send<A: EncodeArguments, R: EncodeReturn>(
+ receiver: *mut AnyObject,
+ sel: Sel,
+ args: A,
+ ) -> R {
+ let msg_send_fn = R::MSG_SEND;
+ // Note: Modern Objective-C compilers have a workaround to ensure that
+ // messages to `nil` with a struct return produces `mem::zeroed()`,
+ // see:
+ // <https://www.sealiesoftware.com/blog/archive/2012/2/29/objc_explain_return_value_of_message_to_nil.html>
+ //
+ // We _could_ technically do something similar, but since we're
+ // disallowing messages to `nil` with `debug_assertions` enabled
+ // anyhow, and since Rust has a much stronger type-system that
+ // disallows NULL/nil in most cases, we won't bother supporting it.
+ unsafe { A::__invoke(msg_send_fn, receiver, sel, args) }
+ }
+
+ #[inline]
+ #[track_caller]
+ pub(crate) unsafe fn send_super<A: EncodeArguments, R: EncodeReturn>(
+ receiver: *mut AnyObject,
+ super_class: &AnyClass,
+ sel: Sel,
+ args: A,
+ ) -> R {
+ let mut sup = ffi::objc_super {
+ receiver,
+ super_class,
+ };
+ let receiver: *mut ffi::objc_super = &mut sup;
+ let receiver = receiver.cast();
+
+ let msg_send_fn = R::MSG_SEND_SUPER;
+ unsafe { A::__invoke(msg_send_fn, receiver, sel, args) }
+ }
+}
+
+#[cfg(feature = "gnustep-1-7")]
+mod msg_send_primitive {
+ use core::mem;
+
+ use crate::encode::{EncodeArguments, EncodeReturn};
+ use crate::ffi;
+ use crate::runtime::{AnyClass, AnyObject, Imp, Sel};
+
+ #[inline]
+ fn unwrap_msg_send_fn(msg_send_fn: Option<Imp>) -> Imp {
+ match msg_send_fn {
+ Some(msg_send_fn) => msg_send_fn,
+ None => {
+ // SAFETY: This will never be NULL, even if the selector is not
+ // found a callable function pointer will still be returned!
+ //
+ // `clang` doesn't insert a NULL check here either.
+ unsafe { core::hint::unreachable_unchecked() }
+ }
+ }
+ }
+
+ #[track_caller]
+ pub(crate) unsafe fn send<A: EncodeArguments, R: EncodeReturn>(
+ receiver: *mut AnyObject,
+ sel: Sel,
+ args: A,
+ ) -> R {
+ // If `receiver` is NULL, objc_msg_lookup will return a standard
+ // C-method taking two arguments, the receiver and the selector.
+ //
+ // Transmuting and calling such a function with multiple parameters is
+ // safe as long as the return value is a primitive (and e.g. not a big
+ // struct or array).
+ //
+ // However, when the return value is a floating point value, the float
+ // will end up as some undefined value, usually NaN, which is
+ // incompatible with Apple's platforms. As such, we insert this extra
+ // NULL check here.
+ if receiver.is_null() {
+ // SAFETY: Caller guarantees that messages to NULL-receivers only
+ // return pointers or primitive values, and a mem::zeroed pointer
+ // / primitive is just a NULL-pointer or a zeroed primitive.
+ return unsafe { mem::zeroed() };
+ }
+
+ let msg_send_fn = unsafe { ffi::objc_msg_lookup(receiver, sel) };
+ let msg_send_fn = unwrap_msg_send_fn(msg_send_fn);
+ unsafe { A::__invoke(msg_send_fn, receiver, sel, args) }
+ }
+
+ #[track_caller]
+ pub(crate) unsafe fn send_super<A: EncodeArguments, R: EncodeReturn>(
+ receiver: *mut AnyObject,
+ super_class: &AnyClass,
+ sel: Sel,
+ args: A,
+ ) -> R {
+ if receiver.is_null() {
+ // SAFETY: Same as in `send`.
+ return unsafe { mem::zeroed() };
+ }
+
+ let sup = ffi::objc_super {
+ receiver,
+ super_class,
+ };
+ let msg_send_fn = unsafe { ffi::objc_msg_lookup_super(&sup, sel) };
+ let msg_send_fn = unwrap_msg_send_fn(msg_send_fn);
+ unsafe { A::__invoke(msg_send_fn, receiver, sel, args) }
+ }
+}
+
+#[cfg(all(not(target_vendor = "apple"), not(feature = "gnustep-1-7")))]
+mod msg_send_primitive {
+ use crate::encode::{EncodeArguments, EncodeReturn};
+ use crate::runtime::{AnyClass, AnyObject, Sel};
+
+ #[track_caller]
+ pub(crate) unsafe fn send<A: EncodeArguments, R: EncodeReturn>(
+ _receiver: *mut AnyObject,
+ _sel: Sel,
+ _args: A,
+ ) -> R {
+ unimplemented!("no runtime chosen")
+ }
+
+ #[track_caller]
+ pub(crate) unsafe fn send_super<A: EncodeArguments, R: EncodeReturn>(
+ _receiver: *mut AnyObject,
+ _superclass: &AnyClass,
+ _sel: Sel,
+ _args: A,
+ ) -> R {
+ unimplemented!("no runtime chosen")
+ }
+}
+
+/// Help with monomorphizing in framework crates
+#[cfg(debug_assertions)]
+#[track_caller]
+fn msg_send_check(
+ obj: Option<&AnyObject>,
+ sel: Sel,
+ args: &[crate::encode::Encoding],
+ ret: &crate::encode::Encoding,
+) {
+ let cls = if let Some(obj) = obj {
+ obj.class()
+ } else {
+ panic_null(sel)
+ };
+
+ msg_send_check_class(cls, sel, args, ret);
+}
+
+#[cfg(debug_assertions)]
+#[track_caller]
+fn msg_send_check_class(
+ cls: &AnyClass,
+ sel: Sel,
+ args: &[crate::encode::Encoding],
+ ret: &crate::encode::Encoding,
+) {
+ if cfg!(feature = "disable-encoding-assertions") {
+ // These checks are disabled.
+ return;
+ }
+
+ use crate::verify::{verify_method_signature, Inner, VerificationError};
+
+ let err = if let Some(method) = cls.instance_method(sel) {
+ if let Err(err) = verify_method_signature(method, args, ret) {
+ err
+ } else {
+ return;
+ }
+ } else {
+ VerificationError::from(Inner::MethodNotFound)
+ };
+
+ panic_verify(cls, sel, &err);
+}
+
+#[cfg(debug_assertions)]
+#[track_caller]
+fn panic_null(sel: Sel) -> ! {
+ panic!("messsaging {sel} to nil")
+}
+
+#[cfg(debug_assertions)]
+#[track_caller]
+fn panic_verify(cls: &AnyClass, sel: Sel, err: &crate::runtime::VerificationError) -> ! {
+ panic!(
+ "invalid message send to {}[{cls} {sel}]: {err}",
+ if cls.is_metaclass() { "+" } else { "-" },
+ )
+}
+
+mod private {
+ pub trait Sealed {}
+}
+
+/// Types that can directly be used as the receiver of Objective-C messages.
+///
+/// Examples include objects pointers, class pointers, and block pointers.
+///
+///
+/// # Safety
+///
+/// This is a sealed trait, and should not need to be implemented. Open an
+/// issue if you know a use-case where this restrition should be lifted!
+pub unsafe trait MessageReceiver: private::Sealed + Sized {
+ #[doc(hidden)]
+ type __Inner: ?Sized + RefEncode;
+
+ #[doc(hidden)]
+ fn __as_raw_receiver(self) -> *mut AnyObject;
+
+ /// Sends a message to the receiver with the given selector and arguments.
+ ///
+ /// This should be used instead of the [`performSelector:`] family of
+ /// methods, as this is both more performant and flexible than that.
+ ///
+ /// The correct version of `objc_msgSend` will be chosen based on the
+ /// return type. For more information, see [the Messaging section in
+ /// Apple's Objective-C Runtime Programming Guide][guide-messaging].
+ ///
+ /// If the selector is known at compile-time, it is recommended to use the
+ /// [`msg_send!`] macro rather than this method.
+ ///
+ /// [`performSelector:`]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418867-performselector?language=objc
+ /// [guide-messaging]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtHowMessagingWorks.html
+ ///
+ ///
+ /// # Safety
+ ///
+ /// This shares the same safety requirements as [`msg_send!`].
+ ///
+ /// The added invariant is that the selector must take the same number of
+ /// arguments as is given.
+ ///
+ /// [`msg_send!`]: crate::msg_send
+ ///
+ ///
+ /// # Example
+ ///
+ /// Call the `copy` method, but using a dynamic selector instead.
+ ///
+ /// ```no_run
+ /// use objc2::rc::Retained;
+ /// use objc2::runtime::MessageReceiver;
+ /// use objc2::sel;
+ /// # use objc2::runtime::NSObject as MyObject;
+ ///
+ /// let obj = MyObject::new();
+ /// // SAFETY: The `copy` method takes no arguments, and returns an object
+ /// let copy: *mut MyObject = unsafe { obj.send_message(sel!(copy), ()) };
+ /// // SAFETY: The `copy` method returns an object with +1 retain count
+ /// let copy = unsafe { Retained::from_raw(copy) }.unwrap();
+ /// ```
+ #[inline]
+ #[track_caller]
+ #[doc(alias = "performSelector")]
+ #[doc(alias = "performSelector:")]
+ #[doc(alias = "performSelector:withObject:")]
+ #[doc(alias = "performSelector:withObject:withObject:")]
+ unsafe fn send_message<A: EncodeArguments, R: EncodeReturn>(self, sel: Sel, args: A) -> R {
+ let receiver = self.__as_raw_receiver();
+ #[cfg(debug_assertions)]
+ {
+ // SAFETY: Caller ensures only valid or NULL pointers.
+ let obj = unsafe { receiver.as_ref() };
+ msg_send_check(obj, sel, A::ENCODINGS, &R::ENCODING_RETURN);
+ }
+
+ // SAFETY: Upheld by caller
+ conditional_try!(|| unsafe { msg_send_primitive::send(receiver, sel, args) })
+ }
+
+ /// Sends a message to a specific superclass with the given selector and
+ /// arguments.
+ ///
+ /// The correct version of `objc_msgSend_super` will be chosen based on the
+ /// return type. For more information, see the section on "Sending
+ /// Messages" in Apple's [documentation][runtime].
+ ///
+ /// If the selector is known at compile-time, it is recommended to use the
+ /// [`msg_send!(super(...), ...)`] macro rather than this method.
+ ///
+ /// [runtime]: https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc
+ ///
+ ///
+ /// # Safety
+ ///
+ /// This shares the same safety requirements as
+ /// [`msg_send!(super(...), ...)`].
+ ///
+ /// The added invariant is that the selector must take the same number of
+ /// arguments as is given.
+ ///
+ /// [`msg_send!(super(...), ...)`]: crate::msg_send
+ #[inline]
+ #[track_caller]
+ unsafe fn send_super_message<A: EncodeArguments, R: EncodeReturn>(
+ self,
+ superclass: &AnyClass,
+ sel: Sel,
+ args: A,
+ ) -> R {
+ let receiver = self.__as_raw_receiver();
+ #[cfg(debug_assertions)]
+ {
+ if receiver.is_null() {
+ panic_null(sel);
+ }
+ msg_send_check_class(superclass, sel, A::ENCODINGS, &R::ENCODING_RETURN);
+ }
+
+ // SAFETY: Upheld by caller
+ conditional_try!(|| unsafe {
+ msg_send_primitive::send_super(receiver, superclass, sel, args)
+ })
+ }
+}
+
+// Note that we implement MessageReceiver for unsized types as well, this is
+// to support `extern type`s in the future, not because we want to allow DSTs.
+
+impl<T: ?Sized + Message> private::Sealed for *const T {}
+unsafe impl<T: ?Sized + Message> MessageReceiver for *const T {
+ type __Inner = T;
+
+ #[inline]
+ fn __as_raw_receiver(self) -> *mut AnyObject {
+ (self as *mut T).cast()
+ }
+}
+
+impl<T: ?Sized + Message> private::Sealed for *mut T {}
+unsafe impl<T: ?Sized + Message> MessageReceiver for *mut T {
+ type __Inner = T;
+
+ #[inline]
+ fn __as_raw_receiver(self) -> *mut AnyObject {
+ self.cast()
+ }
+}
+
+impl<T: ?Sized + Message> private::Sealed for NonNull<T> {}
+unsafe impl<T: ?Sized + Message> MessageReceiver for NonNull<T> {
+ type __Inner = T;
+
+ #[inline]
+ fn __as_raw_receiver(self) -> *mut AnyObject {
+ self.as_ptr().cast()
+ }
+}
+
+impl<T: ?Sized + Message> private::Sealed for &T {}
+unsafe impl<T: ?Sized + Message> MessageReceiver for &T {
+ type __Inner = T;
+
+ #[inline]
+ fn __as_raw_receiver(self) -> *mut AnyObject {
+ let ptr: *const T = self;
+ (ptr as *mut T).cast()
+ }
+}
+
+impl private::Sealed for &mut AnyObject {}
+/// `&mut AnyObject` is allowed as mutable, for easier transition from `objc`,
+/// even though it's basically always incorrect to hold `&mut AnyObject`.
+///
+/// Use `*mut AnyObject` instead if you know for certain you need mutability,
+/// and cannot make do with interior mutability.
+unsafe impl MessageReceiver for &mut AnyObject {
+ type __Inner = AnyObject;
+
+ #[inline]
+ fn __as_raw_receiver(self) -> *mut AnyObject {
+ self
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::ptr;
+
+ use super::*;
+ use crate::msg_send;
+ use crate::rc::{Allocated, Retained};
+ use crate::runtime::NSObject;
+ use crate::test_utils;
+
+ #[allow(unused)]
+ fn test_different_receivers(obj: &mut AnyObject) {
+ unsafe {
+ let x = &mut *obj;
+ let _: () = msg_send![x, mutable1];
+ // `x` is consumed by the above, so this won't work:
+ // let _: () = msg_send![x, mutable2];
+
+ // It is only possible if we reborrow:
+ let _: () = msg_send![&mut *obj, mutable1];
+ let _: () = msg_send![&mut *obj, mutable2];
+
+ // Test NonNull
+ let obj = NonNull::from(obj);
+ let _: () = msg_send![obj, mutable1];
+ let _: () = msg_send![obj, mutable2];
+
+ // And test raw pointers
+ let obj: *mut AnyObject = obj.as_ptr();
+ let _: () = msg_send![obj, mutable1];
+ let _: () = msg_send![obj, mutable2];
+ }
+ }
+
+ #[test]
+ fn test_send_message() {
+ let obj = test_utils::custom_object();
+ let _: () = unsafe { msg_send![&obj, setFoo: 4u32] };
+ let result: u32 = unsafe { msg_send![&obj, foo] };
+ assert_eq!(result, 4);
+ }
+
+ #[test]
+ fn test_send_message_stret() {
+ let obj = test_utils::custom_object();
+ let result: test_utils::CustomStruct = unsafe { msg_send![&obj, customStruct] };
+ let expected = test_utils::CustomStruct {
+ a: 1,
+ b: 2,
+ c: 3,
+ d: 4,
+ };
+ assert_eq!(result, expected);
+ }
+
+ #[test]
+ #[cfg_attr(debug_assertions, should_panic = "messsaging description to nil")]
+ fn test_send_message_nil() {
+ let nil: *mut NSObject = ::core::ptr::null_mut();
+
+ // This result should not be relied on
+ let result: Option<Retained<NSObject>> = unsafe { msg_send![nil, description] };
+ assert!(result.is_none());
+
+ // This result should not be relied on
+ let result: usize = unsafe { msg_send![nil, hash] };
+ assert_eq!(result, 0);
+
+ // This result should not be relied on
+ #[cfg(target_pointer_width = "16")]
+ let result: f32 = 0.0;
+ #[cfg(target_pointer_width = "32")]
+ let result: f32 = unsafe { msg_send![nil, floatValue] };
+ #[cfg(target_pointer_width = "64")]
+ let result: f64 = unsafe { msg_send![nil, doubleValue] };
+ assert_eq!(result, 0.0);
+
+ // This result should not be relied on
+ let result: Option<Retained<NSObject>> =
+ unsafe { msg_send![nil, multiple: 1u32, arguments: 2i8] };
+ assert!(result.is_none());
+
+ // This result should not be relied on
+ let obj = unsafe { Allocated::new(ptr::null_mut()) };
+ let result: Option<Retained<NSObject>> = unsafe { msg_send![obj, init] };
+ assert!(result.is_none());
+ }
+
+ #[test]
+ fn test_send_message_super() {
+ let obj = test_utils::custom_subclass_object();
+ let superclass = test_utils::custom_class();
+ unsafe {
+ let _: () = msg_send![&obj, setFoo: 4u32];
+ let foo: u32 = msg_send![super(&obj, superclass), foo];
+ assert_eq!(foo, 4);
+
+ // The subclass is overridden to return foo + 2
+ let foo: u32 = msg_send![&obj, foo];
+ assert_eq!(foo, 6);
+ }
+ }
+
+ #[test]
+ #[cfg_attr(
+ feature = "gnustep-1-7",
+ ignore = "GNUStep deadlocks here for some reason"
+ )]
+ fn test_send_message_class_super() {
+ let cls = test_utils::custom_subclass();
+ let superclass = test_utils::custom_class();
+ unsafe {
+ let foo: u32 = msg_send![super(cls, superclass.metaclass()), classFoo];
+ assert_eq!(foo, 7);
+
+ // The subclass is overridden to return + 2
+ let foo: u32 = msg_send![cls, classFoo];
+ assert_eq!(foo, 9);
+ }
+ }
+}
diff --git a/third_party/rust/objc2/src/runtime/method_encoding_iter.rs b/third_party/rust/objc2/src/runtime/method_encoding_iter.rs
@@ -0,0 +1,212 @@
+//! Utility for parsing an Objective-C method type encoding.
+//!
+//! TODO: Move this to `objc2-encode` when more stable.
+use core::fmt;
+use core::num::ParseIntError;
+use std::error::Error;
+
+use crate::encode::{Encoding, EncodingBox, ParseError};
+
+#[derive(Debug, PartialEq, Eq)]
+pub(crate) struct MethodEncodingIter<'a> {
+ s: &'a str,
+}
+
+impl<'a> MethodEncodingIter<'a> {
+ pub(crate) fn new(s: &'a str) -> Self {
+ Self { s }
+ }
+
+ pub(crate) fn extract_return(
+ &mut self,
+ ) -> Result<(EncodingBox, Option<isize>), EncodingParseError> {
+ // TODO: Verify stack layout
+ self.next().ok_or(EncodingParseError::MissingReturn)?
+ }
+
+ pub(crate) fn verify_receiver(&mut self) -> Result<(), EncodingParseError> {
+ // TODO: Verify stack layout
+ let (enc, _stack_layout) = self.next().ok_or(EncodingParseError::MissingReceiver)??;
+ if !Encoding::Object.equivalent_to_box(&enc) {
+ return Err(EncodingParseError::InvalidReceiver(enc));
+ }
+ Ok(())
+ }
+
+ pub(crate) fn verify_sel(&mut self) -> Result<(), EncodingParseError> {
+ let (enc, _stack_layout) = self.next().ok_or(EncodingParseError::MissingSel)??;
+ if !Encoding::Sel.equivalent_to_box(&enc) {
+ return Err(EncodingParseError::InvalidSel(enc));
+ }
+ Ok(())
+ }
+
+ fn extract_encoding(&mut self) -> Result<(EncodingBox, Option<isize>), EncodingParseError> {
+ // See also the following other approaches:
+ // objrs: https://gitlab.com/objrs/objrs/-/blob/b4f6598696b3fa622e6fddce7aff281770b0a8c2/src/test.rs
+ // libobjc2: https://github.com/gnustep/libobjc2/blob/v2.1/encoding2.c
+ // objc4: https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-typeencoding.mm
+
+ let encoding = EncodingBox::from_start_of_str(&mut self.s)?;
+ let stack_layout = parse_stack_layout(&mut self.s)?;
+
+ Ok((encoding, stack_layout))
+ }
+}
+
+impl Iterator for MethodEncodingIter<'_> {
+ type Item = Result<(EncodingBox, Option<isize>), EncodingParseError>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.s.is_empty() {
+ return None;
+ }
+ Some(self.extract_encoding())
+ }
+}
+
+// TODO: Is `isize` correct here?
+fn parse_stack_layout(s: &mut &str) -> Result<Option<isize>, ParseIntError> {
+ let rest = s.trim_start_matches(|c: char| c.is_ascii_digit() || c == '-' || c == '+');
+ let stack_layout = &s[..s.len() - rest.len()];
+ *s = rest;
+
+ if stack_layout.is_empty() {
+ return Ok(None);
+ }
+ stack_layout.parse().map(Some)
+}
+
+#[derive(Debug, PartialEq, Eq, Hash)]
+pub(crate) enum EncodingParseError {
+ ParseError(ParseError),
+ InvalidStackLayoutInteger,
+ MissingReturn,
+ MissingReceiver,
+ MissingSel,
+ InvalidReceiver(EncodingBox),
+ InvalidSel(EncodingBox),
+}
+
+impl From<ParseError> for EncodingParseError {
+ fn from(e: ParseError) -> Self {
+ Self::ParseError(e)
+ }
+}
+
+impl From<ParseIntError> for EncodingParseError {
+ fn from(_: ParseIntError) -> Self {
+ Self::InvalidStackLayoutInteger
+ }
+}
+
+impl fmt::Display for EncodingParseError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if !matches!(self, Self::ParseError(_)) {
+ write!(f, "failed parsing encoding: ")?;
+ }
+
+ match self {
+ Self::ParseError(e) => write!(f, "{e}")?,
+ Self::InvalidStackLayoutInteger => write!(f, "invalid integer for stack layout")?,
+ Self::MissingReturn => write!(f, "return type must be present")?,
+ Self::MissingReceiver => write!(f, "receiver type must be present")?,
+ Self::MissingSel => write!(f, "selector type must be present")?,
+ Self::InvalidReceiver(enc) => {
+ write!(f, "receiver encoding must be '@', but it was '{enc}'")?;
+ }
+ Self::InvalidSel(enc) => {
+ write!(f, "selector encoding must be '@', but it was '{enc}'")?;
+ }
+ }
+ write!(f, ". This is likely a bug, please report it!")
+ }
+}
+
+impl Error for EncodingParseError {}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use alloc::boxed::Box;
+ use alloc::vec;
+ use alloc::vec::Vec;
+
+ fn assert_stack_layout(mut types: &str, expected: Option<isize>, rest: &str) {
+ let sl = parse_stack_layout(&mut types).unwrap();
+ assert_eq!(sl, expected);
+ assert_eq!(types, rest);
+ }
+
+ #[test]
+ fn stack_layout_extract() {
+ assert_stack_layout("", None, "");
+ assert_stack_layout("abc", None, "abc");
+ assert_stack_layout("abc12abc", None, "abc12abc");
+ assert_stack_layout("0", Some(0), "");
+ assert_stack_layout("1abc", Some(1), "abc");
+ assert_stack_layout("42def24", Some(42), "def24");
+ assert_stack_layout("1234567890xyz", Some(1234567890), "xyz");
+
+ assert_stack_layout("-1a", Some(-1), "a");
+ assert_stack_layout("-1a", Some(-1), "a");
+
+ // GNU runtime's register parameter hint??
+ assert_stack_layout("+1a", Some(1), "a");
+ }
+
+ fn assert_encoding_extract(s: &str, expected: &[(EncodingBox, Option<isize>)]) {
+ let actual: Vec<_> = MethodEncodingIter::new(s)
+ .collect::<Result<_, _>>()
+ .unwrap_or_else(|e| panic!("{}", e));
+ assert_eq!(&actual, expected);
+ }
+
+ #[test]
+ fn parse_bitfield() {
+ assert_encoding_extract(
+ "@48@0:8Ad16r^*24{bitfield=b64b1}32i48",
+ &[
+ (EncodingBox::Object, Some(48)),
+ (EncodingBox::Object, Some(0)),
+ (EncodingBox::Sel, Some(8)),
+ (EncodingBox::Atomic(Box::new(EncodingBox::Double)), Some(16)),
+ (
+ EncodingBox::Pointer(Box::new(EncodingBox::String)),
+ Some(24),
+ ),
+ (
+ EncodingBox::Struct(
+ "bitfield".into(),
+ vec![
+ EncodingBox::BitField(64, None),
+ EncodingBox::BitField(1, None),
+ ],
+ ),
+ Some(32),
+ ),
+ (EncodingBox::Int, Some(48)),
+ ],
+ );
+ }
+
+ #[test]
+ fn parse_complex() {
+ assert_encoding_extract(
+ "jf16@0:8",
+ &[
+ (EncodingBox::FloatComplex, Some(16)),
+ (EncodingBox::Object, Some(0)),
+ (EncodingBox::Sel, Some(8)),
+ ],
+ );
+ assert_encoding_extract(
+ "jf@:",
+ &[
+ (EncodingBox::FloatComplex, None),
+ (EncodingBox::Object, None),
+ (EncodingBox::Sel, None),
+ ],
+ );
+ }
+}
diff --git a/third_party/rust/objc2/src/runtime/method_implementation.rs b/third_party/rust/objc2/src/runtime/method_implementation.rs
@@ -0,0 +1,115 @@
+use core::mem;
+
+use crate::__macro_helpers::RetainedReturnValue;
+use crate::encode::{EncodeArgument, EncodeArguments, EncodeReturn, RefEncode};
+use crate::rc::Allocated;
+use crate::runtime::{Imp, MessageReceiver, Sel};
+use crate::Message;
+
+mod private {
+ pub trait Sealed {}
+}
+
+/// Types that can be used as the implementation of an Objective-C method.
+///
+/// This is a sealed trait that is implemented for a lot of `extern "C"`
+/// function pointer types.
+//
+// Note: `Sized` is intentionally added to make the trait dyn-incompatible.
+pub trait MethodImplementation: private::Sealed + Sized {
+ /// The callee type of the method.
+ type Callee: ?Sized + RefEncode;
+
+ /// The argument types of the method.
+ type Arguments: EncodeArguments;
+
+ /// The return type of the method.
+ type Return: EncodeReturn;
+
+ #[doc(hidden)]
+ fn __imp(self) -> Imp;
+}
+
+macro_rules! method_impl_inner {
+ ($(($unsafe:ident))? $abi:literal; $($t:ident),*) => {
+ impl<T, R, $($t),*> private::Sealed for $($unsafe)? extern $abi fn(T, Sel $(, $t)*) -> R
+ where
+ T: ?Sized + MessageReceiver,
+ R: EncodeReturn,
+ $($t: EncodeArgument,)*
+ {}
+
+ impl<T, R, $($t),*> MethodImplementation for $($unsafe)? extern $abi fn(T, Sel $(, $t)*) -> R
+ where
+ T: ?Sized + MessageReceiver,
+ R: EncodeReturn,
+ $($t: EncodeArgument,)*
+ {
+ type Callee = T::__Inner;
+ type Arguments = ($($t,)*);
+ type Return = R;
+
+ fn __imp(self) -> Imp {
+ // SAFETY: Transmuting to an `unsafe` function pointer
+ unsafe { mem::transmute(self) }
+ }
+ }
+
+ impl<T, $($t),*> private::Sealed for $($unsafe)? extern $abi fn(Allocated<T>, Sel $(, $t)*) -> RetainedReturnValue
+ where
+ T: ?Sized + Message,
+ $($t: EncodeArgument,)*
+ {}
+
+ #[doc(hidden)]
+ impl<T, $($t),*> MethodImplementation for $($unsafe)? extern $abi fn(Allocated<T>, Sel $(, $t)*) -> RetainedReturnValue
+ where
+ T: ?Sized + Message,
+ $($t: EncodeArgument,)*
+ {
+ type Callee = T;
+ type Arguments = ($($t,)*);
+ type Return = RetainedReturnValue;
+
+ fn __imp(self) -> Imp {
+ // SAFETY: `Allocated<T>` is the same as `NonNull<T>`, except
+ // with the assumption of a +1 calling convention.
+ //
+ // The calling convention is ensured to be upheld by having
+ // `RetainedReturnValue` in the type, since that type is private
+ // and hence only internal macros like `#[method_id]` will be
+ // able to produce it (and that, in turn, only allows it if
+ // the selector is `init` as checked by `MessageReceiveRetained`).
+ unsafe { mem::transmute(self) }
+ }
+ }
+ };
+}
+
+macro_rules! method_impl {
+ ($($t:ident),*) => {
+ method_impl_inner!((unsafe) "C"; $($t),*);
+ method_impl_inner!("C"; $($t),*);
+ method_impl_inner!((unsafe) "C-unwind"; $($t),*);
+ method_impl_inner!("C-unwind"; $($t),*);
+ };
+}
+
+// Up to 16 arguments.
+method_impl!();
+method_impl!(A);
+method_impl!(A, B);
+method_impl!(A, B, C);
+method_impl!(A, B, C, D);
+method_impl!(A, B, C, D, E);
+method_impl!(A, B, C, D, E, F);
+method_impl!(A, B, C, D, E, F, G);
+method_impl!(A, B, C, D, E, F, G, H);
+method_impl!(A, B, C, D, E, F, G, H, I);
+method_impl!(A, B, C, D, E, F, G, H, I, J);
+method_impl!(A, B, C, D, E, F, G, H, I, J, K);
+method_impl!(A, B, C, D, E, F, G, H, I, J, K, L);
+method_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M);
+method_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
+method_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
+method_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
diff --git a/third_party/rust/objc2/src/runtime/mod.rs b/third_party/rust/objc2/src/runtime/mod.rs
@@ -0,0 +1,1911 @@
+//! # Direct runtime bindings.
+//!
+//! This module contains safe(r) bindings to common parts of the Objective-C
+//! runtime. See the [`ffi`][crate::ffi] module for details on the raw
+//! bindings.
+//!
+//!
+//! # Example
+//!
+//! Using features of the runtime to query information about `NSObject`.
+//!
+//! ```
+#![doc = include_str!("../../examples/introspection.rs")]
+//! ```
+#![allow(clippy::missing_panics_doc)]
+
+use alloc::ffi::CString;
+use alloc::vec::Vec;
+use core::ffi::c_char;
+use core::ffi::c_uint;
+use core::ffi::{c_void, CStr};
+use core::fmt;
+use core::hash;
+use core::panic::{RefUnwindSafe, UnwindSafe};
+use core::ptr::{self, NonNull};
+
+// Note: While this is not public, it is still a breaking change to remove,
+// since `objc2-foundation` relies on it.
+#[doc(hidden)]
+pub mod __nsstring;
+mod bool;
+mod define;
+mod malloc;
+mod message_receiver;
+mod method_encoding_iter;
+mod method_implementation;
+mod nsobject;
+mod nsproxy;
+mod nszone;
+mod protocol_object;
+mod retain_release_fast;
+
+pub(crate) use self::method_encoding_iter::{EncodingParseError, MethodEncodingIter};
+pub(crate) use self::retain_release_fast::{objc_release_fast, objc_retain_fast};
+use crate::encode::{Encode, EncodeArguments, EncodeReturn, Encoding, OptionEncode, RefEncode};
+use crate::msg_send;
+use crate::verify::{verify_method_signature, Inner};
+use crate::{ffi, DowncastTarget, Message};
+
+// Note: While this is not public, it is still a breaking change to remove,
+// since `objc2-foundation` relies on it.
+#[doc(hidden)]
+pub use self::nsproxy::NSProxy as __NSProxy;
+
+pub use self::bool::Bool;
+pub use self::define::{ClassBuilder, ProtocolBuilder};
+pub use self::message_receiver::MessageReceiver;
+pub use self::method_implementation::MethodImplementation;
+pub use self::nsobject::{NSObject, NSObjectProtocol};
+pub use self::nszone::NSZone;
+pub use self::protocol_object::{ImplementedBy, ProtocolObject};
+pub use crate::verify::VerificationError;
+
+#[allow(deprecated)]
+pub use crate::ffi::{BOOL, NO, YES};
+
+use self::malloc::{MallocCStr, MallocSlice};
+
+/// We do not want to expose `MallocSlice` to end users, because in the
+/// future, we want to be able to change it to `Box<[T], MallocAllocator>`.
+///
+/// So instead we use an unnameable type.
+macro_rules! MallocSlice {
+ ($t:ty) => {
+ impl std::ops::Deref<Target = [$t]> + AsRef<[$t]> + std::fmt::Debug
+ };
+}
+
+/// Same as `MallocSlice!`.
+macro_rules! MallocCStr {
+ () => {
+ impl std::ops::Deref<Target = CStr> + AsRef<CStr> + std::fmt::Debug
+ };
+}
+
+/// Implement PartialEq, Eq and Hash using pointer semantics; there's not
+/// really a better way to do it for this type
+macro_rules! standard_pointer_impls {
+ ($name:ident) => {
+ impl PartialEq for $name {
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ ptr::eq(self, other)
+ }
+ }
+ impl Eq for $name {}
+ impl hash::Hash for $name {
+ #[inline]
+ fn hash<H: hash::Hasher>(&self, state: &mut H) {
+ let ptr: *const Self = self;
+ ptr.hash(state)
+ }
+ }
+ };
+}
+
+/// A pointer to the start of a method implementation.
+///
+/// The first argument is a pointer to the receiver, the second argument is
+/// the selector, and the rest of the arguments follow.
+///
+///
+/// # Safety
+///
+/// This is a "catch all" type; it must be transmuted to the correct type
+/// before being called!
+///
+/// Also note that this is non-null! If you require an Imp that can be null,
+/// use `Option<Imp>`.
+#[doc(alias = "IMP")]
+pub type Imp = unsafe extern "C-unwind" fn();
+
+/// A method selector.
+///
+/// The Rust equivalent of Objective-C's `SEL _Nonnull` type. You can create
+/// this statically using the [`sel!`] macro.
+///
+/// The main reason the Objective-C runtime uses a custom type for selectors,
+/// as opposed to a plain c-string, is to support efficient comparison - a
+/// a selector is effectively an [interned string], so this makes equiality
+/// comparisons very cheap.
+///
+/// This struct guarantees the null-pointer optimization, namely that
+/// `Option<Sel>` is the same size as `Sel`.
+///
+/// Selectors are immutable.
+///
+/// [`sel!`]: crate::sel
+/// [interned string]: https://en.wikipedia.org/wiki/String_interning
+#[repr(transparent)]
+#[derive(Copy, Clone)]
+#[doc(alias = "SEL")]
+#[doc(alias = "objc_selector")]
+pub struct Sel {
+ ptr: NonNull<c_void>,
+}
+
+// SAFETY: Sel is immutable (and can be retrieved from any thread using the
+// `sel!` macro).
+unsafe impl Sync for Sel {}
+unsafe impl Send for Sel {}
+impl UnwindSafe for Sel {}
+impl RefUnwindSafe for Sel {}
+
+impl Sel {
+ #[inline]
+ #[doc(hidden)]
+ pub const unsafe fn __internal_from_ptr(ptr: *const u8) -> Self {
+ // Used in static selectors.
+ // SAFETY: Upheld by caller.
+ let ptr = unsafe { NonNull::new_unchecked(ptr as *mut c_void) };
+ Self { ptr }
+ }
+
+ #[inline]
+ pub(crate) unsafe fn from_ptr(ptr: *const c_void) -> Option<Self> {
+ // SAFETY: Caller verifies that the pointer is valid.
+ NonNull::new(ptr as *mut c_void).map(|ptr| Self { ptr })
+ }
+
+ #[inline]
+ pub(crate) const fn as_ptr(&self) -> *const c_void {
+ self.ptr.as_ptr()
+ }
+
+ // We explicitly don't do #[track_caller] here, since we expect the error
+ // to never actually happen.
+ pub(crate) unsafe fn register_unchecked(name: *const c_char) -> Self {
+ let ptr = unsafe { ffi::sel_registerName(name) };
+ // SAFETY: `sel_registerName` declares return type as `SEL _Nonnull`,
+ // at least when input is also `_Nonnull` (which it is in our case).
+ //
+ // Looking at the source code, it can fail and will return NULL if
+ // allocating space for the selector failed (which then subsequently
+ // invokes UB by calling `memcpy` with a NULL argument):
+ // <https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-os.h#L1002-L1004>
+ //
+ // I suspect this will be really uncommon in practice, since the
+ // called selector is almost always going to be present in the binary
+ // already; but alas, we'll handle it!
+ ptr.expect("failed allocating selector")
+ }
+
+ /// Registers a selector with the Objective-C runtime.
+ ///
+ /// This is the dynamic version of the [`sel!`] macro, prefer to use that
+ /// when your selector is static.
+ ///
+ /// [`sel!`]: crate::sel
+ ///
+ ///
+ /// # Panics
+ ///
+ /// Panics if the runtime failed allocating space for the selector.
+ #[inline]
+ #[doc(alias = "sel_registerName")]
+ pub fn register(name: &CStr) -> Self {
+ // SAFETY: Input is a non-null, NUL-terminated C-string pointer.
+ unsafe { Self::register_unchecked(name.as_ptr()) }
+ }
+
+ /// Returns the string representation of the selector.
+ #[inline]
+ #[doc(alias = "sel_getName")]
+ pub fn name(self) -> &'static CStr {
+ // SAFETY: Input is non-null selector. Declares return type as
+ // `const char * _Nonnull`, source code agrees.
+ let ptr = unsafe { ffi::sel_getName(self) };
+ // SAFETY: The string is a valid C-style NUL-terminated string, and
+ // has static lifetime since the selector has static lifetime.
+ unsafe { CStr::from_ptr(ptr) }
+ }
+
+ pub(crate) fn number_of_arguments(self) -> usize {
+ self.name()
+ .to_bytes()
+ .iter()
+ .filter(|&&b| b == b':')
+ .count()
+ }
+}
+
+impl PartialEq for Sel {
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ if cfg!(feature = "gnustep-1-7") {
+ // GNUStep implements "typed" selectors, which means their pointer
+ // values sometimes differ; so let's use the runtime-provided
+ // `sel_isEqual`.
+ unsafe { ffi::sel_isEqual(*self, *other).as_bool() }
+ } else {
+ // `ffi::sel_isEqual` uses pointer comparison on Apple (the
+ // documentation explicitly notes this); so as an optimization,
+ // let's do that as well!
+ ptr::eq(self.as_ptr(), other.as_ptr())
+ }
+ }
+}
+
+impl Eq for Sel {}
+
+impl hash::Hash for Sel {
+ #[inline]
+ fn hash<H: hash::Hasher>(&self, state: &mut H) {
+ if cfg!(feature = "gnustep-1-7") {
+ // Note: We hash the name instead of the pointer on GNUStep, since
+ // they're typed.
+ self.name().hash(state);
+ } else {
+ self.as_ptr().hash(state);
+ }
+ }
+}
+
+// SAFETY: `Sel` is FFI compatible, and the encoding is `Sel`.
+unsafe impl Encode for Sel {
+ const ENCODING: Encoding = Encoding::Sel;
+}
+
+unsafe impl OptionEncode for Sel {}
+
+// RefEncode is not implemented for Sel, because there is literally no API
+// that takes &Sel, while the user could get confused and accidentally attempt
+// that.
+
+impl fmt::Display for Sel {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Selectors are basically always UTF-8, so it's _fine_ to do a lossy
+ // conversion here.
+ fmt::Display::fmt(&self.name().to_string_lossy(), f)
+ }
+}
+
+impl fmt::Debug for Sel {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_tuple("Sel").field(&self.name()).finish()
+ }
+}
+
+impl fmt::Pointer for Sel {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Pointer::fmt(&self.ptr, f)
+ }
+}
+
+/// An opaque type that represents an instance variable.
+///
+/// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/ivar?language=objc).
+#[repr(C)]
+#[doc(alias = "objc_ivar")]
+pub struct Ivar {
+ _priv: [u8; 0],
+ _p: ffi::OpaqueData,
+}
+
+// SAFETY: Ivar is immutable (and can be retrieved from AnyClass anyhow).
+unsafe impl Sync for Ivar {}
+unsafe impl Send for Ivar {}
+impl UnwindSafe for Ivar {}
+impl RefUnwindSafe for Ivar {}
+
+impl Ivar {
+ /// Returns the instance variable's name.
+ ///
+ /// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/1418922-ivar_getname?language=objc).
+ #[inline]
+ #[doc(alias = "ivar_getName")]
+ pub fn name(&self) -> &CStr {
+ unsafe { CStr::from_ptr(ffi::ivar_getName(self)) }
+ }
+
+ /// Returns the instance variable's offset from the object base.
+ ///
+ /// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/1418976-ivar_getoffset?language=objc).
+ #[inline]
+ #[doc(alias = "ivar_getOffset")]
+ pub fn offset(&self) -> isize {
+ unsafe { ffi::ivar_getOffset(self) }
+ }
+
+ /// Returns the instance variable's `@encode(type)` string.
+ ///
+ /// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/1418569-ivar_gettypeencoding?language=objc).
+ #[inline]
+ #[doc(alias = "ivar_getTypeEncoding")]
+ pub fn type_encoding(&self) -> &CStr {
+ unsafe { CStr::from_ptr(ffi::ivar_getTypeEncoding(self)) }
+ }
+
+ #[inline]
+ pub(crate) fn debug_assert_encoding(&self, _expected: &Encoding) {
+ #[cfg(all(debug_assertions, not(feature = "disable-encoding-assertions")))]
+ {
+ let encoding = self.type_encoding();
+ let encoding = encoding.to_str().expect("encoding must be UTF-8");
+ assert!(
+ _expected.equivalent_to_str(encoding),
+ "wrong encoding. Tried to retrieve ivar with encoding {encoding}, but the encoding of the given type was {_expected}",
+ );
+ }
+ }
+
+ /// Returns a pointer to the instance variable / ivar on the given object.
+ ///
+ /// This is similar to [`UnsafeCell::get`], see that for more information
+ /// on what is and isn't safe to do.
+ ///
+ /// Usually you will have defined the instance variable yourself with
+ /// [`ClassBuilder::add_ivar`], the type of the ivar `T` must match the
+ /// type used in that.
+ ///
+ /// Library implementors are strongly encouraged to expose a safe
+ /// interface to the ivar.
+ ///
+ /// [`UnsafeCell::get`]: core::cell::UnsafeCell::get
+ /// [`ClassBuilder::add_ivar`]: crate::runtime::ClassBuilder::add_ivar
+ ///
+ ///
+ /// # Panics
+ ///
+ /// Panics when `debug_assertions` are enabled if the type encoding of the
+ /// ivar differs from the type encoding of `T`. This can be disabled with
+ /// the `"disable-encoding-assertions"` Cargo feature flag.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The object must have the given instance variable on it, and it must be
+ /// of type `T`. Any invariants that the object have assumed about the
+ /// value of the instance variable must not be violated.
+ ///
+ /// Note that an object can have multiple instance variables with the same
+ /// name; you must ensure that when the instance variable was retrieved,
+ /// was retrieved from the class that it was defined on. In particular,
+ /// getting a class dynamically using e.g. [`AnyObject::class`], and using
+ /// an instance variable from that here is _not_ sound in general.
+ ///
+ /// No thread synchronization is done on accesses to the variable, so you
+ /// must ensure that any access to the returned pointer do not cause data
+ /// races, and that Rust's mutability rules are not otherwise violated.
+ #[inline]
+ pub unsafe fn load_ptr<T: Encode>(&self, obj: &AnyObject) -> *mut T {
+ self.debug_assert_encoding(&T::ENCODING);
+
+ let ptr = NonNull::from(obj);
+ // SAFETY: That the ivar is valid is ensured by the caller
+ let ptr = unsafe { AnyObject::ivar_at_offset::<T>(ptr, self.offset()) };
+
+ // Safe as *mut T because `self` is `UnsafeCell`
+ ptr.as_ptr()
+ }
+
+ /// Returns a reference to the instance variable with the given name.
+ ///
+ /// See [`Ivar::load_ptr`] for more information.
+ ///
+ ///
+ /// # Panics
+ ///
+ /// Panics when `debug_assertions` are enabled if the type encoding of the
+ /// ivar differs from the type encoding of `T`. This can be disabled with
+ /// the `"disable-encoding-assertions"` Cargo feature flag.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The object must have the given instance variable on it, and it must be
+ /// of type `T`.
+ ///
+ /// No thread synchronization is done, so you must ensure that no other
+ /// thread is concurrently mutating the variable. This requirement can be
+ /// considered upheld if all mutation happens through [`Ivar::load_mut`]
+ /// (since that takes the object mutably).
+ #[inline]
+ pub unsafe fn load<'obj, T: Encode>(&self, obj: &'obj AnyObject) -> &'obj T {
+ // SAFETY: That the ivar is valid as `&T` is ensured by the caller,
+ // and the reference is properly bound to the object.
+ unsafe { self.load_ptr::<T>(obj).as_ref().unwrap_unchecked() }
+ }
+
+ /// Returns a mutable reference to the ivar with the given name.
+ ///
+ /// See [`Ivar::load_ptr`] for more information.
+ ///
+ ///
+ /// # Panics
+ ///
+ /// Panics when `debug_assertions` are enabled if the type encoding of the
+ /// ivar differs from the type encoding of `T`. This can be disabled with
+ /// the `"disable-encoding-assertions"` Cargo feature flag.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The object must have an instance variable with the given name, and it
+ /// must be of type `T`.
+ ///
+ /// This access happens through `&mut`, which means we know it to be the
+ /// only reference, hence you do not need to do any work to ensure that
+ /// data races do not happen.
+ #[inline]
+ pub unsafe fn load_mut<'obj, T: Encode>(&self, obj: &'obj mut AnyObject) -> &'obj mut T {
+ self.debug_assert_encoding(&T::ENCODING);
+
+ let ptr = NonNull::from(obj);
+ // SAFETY: That the ivar is valid is ensured by the caller
+ let mut ptr = unsafe { AnyObject::ivar_at_offset::<T>(ptr, self.offset()) };
+
+ // SAFETY: That the ivar is valid as `&mut T` is ensured by taking an
+ // `&mut` object
+ unsafe { ptr.as_mut() }
+ }
+}
+
+standard_pointer_impls!(Ivar);
+
+impl fmt::Debug for Ivar {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Ivar")
+ .field("name", &self.name())
+ .field("offset", &self.offset())
+ .field("type_encoding", &self.type_encoding())
+ .finish_non_exhaustive()
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, Hash)]
+pub(crate) struct MethodDescription {
+ pub(crate) sel: Sel,
+ pub(crate) types: &'static CStr,
+}
+
+impl MethodDescription {
+ pub(crate) unsafe fn from_raw(raw: ffi::objc_method_description) -> Option<Self> {
+ let sel = raw.name?;
+ if raw.types.is_null() {
+ return None;
+ }
+ // SAFETY: We've checked that the pointer is not NULL, rest is checked
+ // by caller.
+ let types = unsafe { CStr::from_ptr(raw.types) };
+ Some(Self { sel, types })
+ }
+}
+
+/// A type that represents a method in a class definition.
+///
+/// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/method?language=objc).
+#[repr(C)]
+#[doc(alias = "objc_method")]
+pub struct Method {
+ _priv: [u8; 0],
+ _p: ffi::OpaqueData,
+}
+
+// SAFETY: Method is immutable (and can be retrieved from AnyClass anyhow).
+unsafe impl Sync for Method {}
+unsafe impl Send for Method {}
+impl UnwindSafe for Method {}
+impl RefUnwindSafe for Method {}
+
+impl Method {
+ // Note: We don't take `&mut` here, since the operations on methods work
+ // atomically.
+ #[inline]
+ fn as_mut_ptr(&self) -> *mut Self {
+ let ptr: *const Self = self;
+ ptr as _
+ }
+
+ /// Returns the name of self.
+ #[inline]
+ #[doc(alias = "method_getName")]
+ pub fn name(&self) -> Sel {
+ unsafe { ffi::method_getName(self).unwrap() }
+ }
+
+ /// Returns the `Encoding` of self's return type.
+ #[doc(alias = "method_copyReturnType")]
+ pub fn return_type(&self) -> MallocCStr!() {
+ unsafe {
+ let encoding = ffi::method_copyReturnType(self);
+ MallocCStr::from_c_str(encoding)
+ }
+ }
+
+ /// Returns the `Encoding` of a single parameter type of self, or
+ /// [`None`] if self has no parameter at the given index.
+ #[doc(alias = "method_copyArgumentType")]
+ pub fn argument_type(&self, index: usize) -> Option<MallocCStr!()> {
+ unsafe {
+ let encoding = ffi::method_copyArgumentType(self, index as c_uint);
+ NonNull::new(encoding).map(|encoding| MallocCStr::from_c_str(encoding.as_ptr()))
+ }
+ }
+
+ /// An iterator over the method's types.
+ ///
+ /// It is approximately equivalent to:
+ ///
+ /// ```ignore
+ /// let types = method.types();
+ /// assert_eq!(types.next()?, method.return_type());
+ /// for i in 0..method.arguments_count() {
+ /// assert_eq!(types.next()?, method.argument_type(i)?);
+ /// }
+ /// assert!(types.next().is_none());
+ /// ```
+ #[doc(alias = "method_getTypeEncoding")]
+ pub(crate) fn types(&self) -> MethodEncodingIter<'_> {
+ // SAFETY: The method pointer is valid and non-null
+ let cstr = unsafe { ffi::method_getTypeEncoding(self) };
+ if cstr.is_null() {
+ panic!("method type encoding was NULL");
+ }
+ // SAFETY: `method_getTypeEncoding` returns a C-string, and we just
+ // checked that it is non-null.
+ let encoding = unsafe { CStr::from_ptr(cstr) };
+ let s = encoding
+ .to_str()
+ .expect("method type encoding must be UTF-8");
+ MethodEncodingIter::new(s)
+ }
+
+ /// Returns the number of arguments accepted by self.
+ #[inline]
+ #[doc(alias = "method_getNumberOfArguments")]
+ pub fn arguments_count(&self) -> usize {
+ unsafe { ffi::method_getNumberOfArguments(self) as usize }
+ }
+
+ /// Returns the implementation of this method.
+ #[doc(alias = "method_getImplementation")]
+ pub fn implementation(&self) -> Imp {
+ unsafe { ffi::method_getImplementation(self).expect("null IMP") }
+ }
+
+ /// Set the implementation of this method.
+ ///
+ /// Note that any thread may at any point be changing method
+ /// implementations, so if you intend to call the previous method as
+ /// returned by e.g. [`Self::implementation`], beware that that may now be
+ /// stale.
+ ///
+ /// The previous implementation is returned from this function though, so
+ /// you can call that instead.
+ ///
+ /// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/1418707-method_setimplementation?language=objc).
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The given implementation function pointer must:
+ ///
+ /// 1. Have the signature expected by the Objective-C runtime and callers
+ /// of this method.
+ ///
+ /// 2. Be at least as safe as the existing method, i.e. by overriding the
+ /// previous method, it should not be possible for the program to cause
+ /// UB.
+ ///
+ /// A common mistake would be expecting e.g. a pointer to not be null,
+ /// where the null case was handled before.
+ #[doc(alias = "method_setImplementation")]
+ pub unsafe fn set_implementation(&self, imp: Imp) -> Imp {
+ // SAFETY: The new impl is not NULL, and the rest is upheld by the
+ // caller.
+ unsafe { ffi::method_setImplementation(self.as_mut_ptr(), imp).expect("null IMP") }
+ }
+
+ /// Exchange the implementation of two methods.
+ ///
+ /// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/1418769-method_exchangeimplementations?language=objc).
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The two methods must be perfectly compatible, both in signature, and
+ /// in expected (in terms of safety, not necessarily behaviour) input and
+ /// output.
+ ///
+ ///
+ /// # Example
+ ///
+ /// This is an atomic version of the following:
+ ///
+ /// ```
+ /// use objc2::runtime::Method;
+ /// # use objc2::runtime::NSObject;
+ /// # use objc2::sel;
+ /// # use crate::objc2::ClassType;
+ ///
+ /// let m1: &Method;
+ /// let m2: &Method;
+ /// #
+ /// # // Use the same method twice, to avoid actually changing anything
+ /// # m1 = NSObject::class().instance_method(sel!(hash)).unwrap();
+ /// # m2 = NSObject::class().instance_method(sel!(hash)).unwrap();
+ ///
+ /// unsafe {
+ /// let imp = m2.set_implementation(m1.implementation());
+ /// m1.set_implementation(imp);
+ /// }
+ /// ```
+ #[inline]
+ #[doc(alias = "method_exchangeImplementations")]
+ pub unsafe fn exchange_implementation(&self, other: &Self) {
+ // TODO: Consider checking that `self.types()` and `other.types()`
+ // match when debug assertions are enabled?
+
+ // SAFETY: Verified by caller
+ unsafe { ffi::method_exchangeImplementations(self.as_mut_ptr(), other.as_mut_ptr()) }
+ }
+}
+
+standard_pointer_impls!(Method);
+
+impl fmt::Debug for Method {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Method")
+ .field("name", &self.name())
+ .field("types", &self.types())
+ .field("implementation", &self.implementation())
+ .finish_non_exhaustive()
+ }
+}
+
+/// An opaque type that represents an Objective-C class.
+///
+/// This is an opaque type meant to be used behind a shared reference
+/// `&AnyClass`, which is semantically equivalent to `Class _Nonnull`.
+///
+/// A nullable class can be used as `Option<&AnyClass>`.
+///
+/// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/class?language=objc).
+#[repr(C)]
+#[doc(alias = "Class")]
+#[doc(alias = "objc_class")]
+pub struct AnyClass {
+ // `isa` field is deprecated and not available on GNUStep, so we don't
+ // expose it here. Use `class_getSuperclass` instead.
+ inner: AnyObject,
+}
+
+/// Use [`AnyClass`] instead.
+#[deprecated = "renamed to `runtime::AnyClass`"]
+pub type Class = AnyClass;
+
+// SAFETY: AnyClass is immutable (and can be retrieved from any thread using
+// the `class!` macro).
+unsafe impl Sync for AnyClass {}
+unsafe impl Send for AnyClass {}
+impl UnwindSafe for AnyClass {}
+impl RefUnwindSafe for AnyClass {}
+// Note that Unpin is not applicable.
+
+impl AnyClass {
+ /// Returns the class definition of a specified class, or [`None`] if the
+ /// class is not registered with the Objective-C runtime.
+ #[inline]
+ #[doc(alias = "objc_getClass")]
+ pub fn get(name: &CStr) -> Option<&'static Self> {
+ let cls = unsafe { ffi::objc_getClass(name.as_ptr()) };
+ unsafe { cls.as_ref() }
+ }
+
+ // Same as `get`, but ...
+ // fn lookup(name: &CStr) -> Option<&'static Self>;
+
+ /// Obtains the list of registered class definitions.
+ #[doc(alias = "objc_copyClassList")]
+ pub fn classes() -> MallocSlice!(&'static Self) {
+ unsafe {
+ let mut count: c_uint = 0;
+ let classes: *mut &Self = ffi::objc_copyClassList(&mut count).cast();
+ MallocSlice::from_array(classes, count as usize)
+ }
+ }
+
+ /// Returns the total number of registered classes.
+ #[inline]
+ #[doc(alias = "objc_getClassList")]
+ pub fn classes_count() -> usize {
+ unsafe { ffi::objc_getClassList(ptr::null_mut(), 0) as usize }
+ }
+
+ /// # Safety
+ ///
+ /// 1. The class pointer must be valid.
+ /// 2. The string is unbounded, so the caller must bound it.
+ pub(crate) unsafe fn name_raw<'a>(ptr: *const Self) -> &'a CStr {
+ // SAFETY: Caller ensures that the pointer is valid
+ let name = unsafe { ffi::class_getName(ptr) };
+ if name.is_null() {
+ panic!("class name was NULL");
+ }
+ // SAFETY: We've checked that the pointer is not NULL, and
+ // `class_getName` is guaranteed to return a valid C-string.
+ //
+ // That the result is properly bounded is checked by the caller.
+ unsafe { CStr::from_ptr(name) }
+ }
+
+ /// Returns the name of the class.
+ #[inline]
+ #[doc(alias = "class_getName")]
+ pub fn name(&self) -> &CStr {
+ // SAFETY: The pointer is valid, and the return is properly bounded
+ unsafe { Self::name_raw(self) }
+ }
+
+ /// # Safety
+ ///
+ /// 1. The class pointer must be valid.
+ /// 2. The caller must bound the lifetime of the returned class.
+ #[inline]
+ pub(crate) unsafe fn superclass_raw<'a>(ptr: *const Self) -> Option<&'a AnyClass> {
+ // SAFETY: Caller ensures that the pointer is valid
+ let superclass = unsafe { ffi::class_getSuperclass(ptr) };
+ // SAFETY: The result is properly bounded by the caller.
+ unsafe { superclass.as_ref() }
+ }
+
+ /// Returns the superclass of self, or [`None`] if self is a root class.
+ #[inline]
+ #[doc(alias = "class_getSuperclass")]
+ pub fn superclass(&self) -> Option<&AnyClass> {
+ // SAFETY: The pointer is valid, and the return is properly bounded
+ unsafe { Self::superclass_raw(self) }
+ }
+
+ /// Returns the metaclass of self.
+ ///
+ ///
+ /// # Example
+ ///
+ /// Get the metaclass of an object.
+ ///
+ /// ```
+ /// use objc2::runtime::NSObject;
+ /// use objc2::ClassType;
+ ///
+ /// let cls = NSObject::class();
+ /// let metacls = cls.metaclass();
+ ///
+ /// assert_eq!(metacls.name(), c"NSObject");
+ /// ```
+ #[inline]
+ #[doc(alias = "object_getClass")]
+ #[doc(alias = "objc_getMetaClass")] // Same as `AnyClass::get(name).metaclass()`
+ pub fn metaclass(&self) -> &Self {
+ let ptr: *const Self = self;
+ let ptr = unsafe { ffi::object_getClass(ptr.cast()) };
+ unsafe { ptr.as_ref().unwrap_unchecked() }
+ }
+
+ /// Whether the class is a metaclass.
+ ///
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use objc2::runtime::NSObject;
+ /// use objc2::ClassType;
+ ///
+ /// let cls = NSObject::class();
+ /// let metacls = cls.metaclass();
+ ///
+ /// assert!(!cls.is_metaclass());
+ /// assert!(metacls.is_metaclass());
+ /// ```
+ #[inline]
+ #[doc(alias = "class_isMetaClass")]
+ pub fn is_metaclass(&self) -> bool {
+ unsafe { ffi::class_isMetaClass(self).as_bool() }
+ }
+
+ /// Returns the size of instances of self.
+ #[inline]
+ #[doc(alias = "class_getInstanceSize")]
+ pub fn instance_size(&self) -> usize {
+ unsafe { ffi::class_getInstanceSize(self) }
+ }
+
+ /// Returns a specified instance method for self, or [`None`] if self and
+ /// its superclasses do not contain an instance method with the specified
+ /// selector.
+ #[inline]
+ #[doc(alias = "class_getInstanceMethod")]
+ pub fn instance_method(&self, sel: Sel) -> Option<&Method> {
+ unsafe {
+ let method = ffi::class_getInstanceMethod(self, sel);
+ method.as_ref()
+ }
+ }
+
+ /// Returns a specified class method for self, or [`None`] if self and
+ /// its superclasses do not contain a class method with the specified
+ /// selector.
+ ///
+ /// Same as `cls.metaclass().class_method()`.
+ #[inline]
+ #[doc(alias = "class_getClassMethod")]
+ pub fn class_method(&self, sel: Sel) -> Option<&Method> {
+ unsafe {
+ let method = ffi::class_getClassMethod(self, sel);
+ method.as_ref()
+ }
+ }
+
+ /// Returns the ivar for a specified instance variable of self, or
+ /// [`None`] if self has no ivar with the given name.
+ ///
+ /// If the instance variable was not found on the specified class, the
+ /// superclasses are searched.
+ ///
+ /// Attempting to access or modify instance variables of a class that you
+ /// do no control may invoke undefined behaviour.
+ #[inline]
+ #[doc(alias = "class_getInstanceVariable")]
+ pub fn instance_variable(&self, name: &CStr) -> Option<&Ivar> {
+ unsafe {
+ let ivar = ffi::class_getInstanceVariable(self, name.as_ptr());
+ ivar.as_ref()
+ }
+ }
+
+ #[allow(unused)]
+ #[inline]
+ #[doc(alias = "class_getClassVariable")]
+ fn class_variable(&self, name: &CStr) -> Option<&Ivar> {
+ let ivar = unsafe { ffi::class_getClassVariable(self, name.as_ptr()) };
+ // SAFETY: TODO
+ unsafe { ivar.as_ref() }
+ }
+
+ /// Describes the instance methods implemented by self.
+ #[doc(alias = "class_copyMethodList")]
+ pub fn instance_methods(&self) -> MallocSlice!(&Method) {
+ unsafe {
+ let mut count: c_uint = 0;
+ let methods: *mut &Method = ffi::class_copyMethodList(self, &mut count).cast();
+ MallocSlice::from_array(methods, count as usize)
+ }
+ }
+
+ /// Checks whether this class conforms to the specified protocol.
+ #[inline]
+ #[doc(alias = "class_conformsToProtocol")]
+ pub fn conforms_to(&self, proto: &AnyProtocol) -> bool {
+ unsafe { ffi::class_conformsToProtocol(self, proto).as_bool() }
+ }
+
+ /// Get a list of the protocols to which this class conforms.
+ #[doc(alias = "class_copyProtocolList")]
+ pub fn adopted_protocols(&self) -> MallocSlice!(&AnyProtocol) {
+ unsafe {
+ let mut count: c_uint = 0;
+ let protos: *mut &AnyProtocol = ffi::class_copyProtocolList(self, &mut count).cast();
+ MallocSlice::from_array(protos, count as usize)
+ }
+ }
+
+ /// Get a list of instance variables on the class.
+ #[doc(alias = "class_copyIvarList")]
+ pub fn instance_variables(&self) -> MallocSlice!(&Ivar) {
+ unsafe {
+ let mut count: c_uint = 0;
+ let ivars: *mut &Ivar = ffi::class_copyIvarList(self, &mut count).cast();
+ MallocSlice::from_array(ivars, count as usize)
+ }
+ }
+
+ /// Check whether instances of this class respond to the given selector.
+ ///
+ /// This doesn't call `respondsToSelector:`, but works entirely within the
+ /// runtime, which means it'll always be safe to call, but may not return
+ /// exactly what you'd expect if `respondsToSelector:` has been
+ /// overwritten.
+ ///
+ /// That said, it will always return `true` if an instance of the class
+ /// responds to the selector, but may return `false` if they don't
+ /// directly (e.g. does so by using forwarding instead).
+ #[inline]
+ #[doc(alias = "class_respondsToSelector")]
+ pub fn responds_to(&self, sel: Sel) -> bool {
+ // This may call `resolveInstanceMethod:` and `resolveClassMethod:`
+ // SAFETY: The selector is guaranteed non-null.
+ unsafe { ffi::class_respondsToSelector(self, sel).as_bool() }
+ }
+
+ // <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html>
+ // fn property(&self, name: &CStr) -> Option<&Property>;
+ // fn properties(&self) -> MallocSlice!(&Property);
+ // unsafe fn replace_method(&self, name: Sel, imp: Imp, types: &CStr) -> Imp;
+ // unsafe fn replace_property(&self, name: &CStr, attributes: &[ffi::objc_property_attribute_t]);
+ // fn method_imp(&self, name: Sel) -> Imp; // + _stret
+
+ // fn get_version(&self) -> u32;
+ // unsafe fn set_version(&mut self, version: u32);
+
+ /// Verify argument and return types for a given selector.
+ ///
+ /// This will look up the encoding of the method for the given selector
+ /// and return a [`VerificationError`] if any encodings differ for the
+ /// arguments `A` and return type `R`.
+ ///
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use objc2::{class, sel};
+ /// use objc2::runtime::{AnyClass, Bool};
+ /// let cls = class!(NSObject);
+ /// let sel = sel!(isKindOfClass:);
+ /// // Verify that `isKindOfClass:`:
+ /// // - Exists on the class
+ /// // - Takes a class as a parameter
+ /// // - Returns a BOOL
+ /// let result = cls.verify_sel::<(&AnyClass,), Bool>(sel);
+ /// assert!(result.is_ok());
+ /// ```
+ #[allow(clippy::missing_errors_doc)] // Written differently in the docs
+ pub fn verify_sel<A, R>(&self, sel: Sel) -> Result<(), VerificationError>
+ where
+ A: EncodeArguments,
+ R: EncodeReturn,
+ {
+ let method = self.instance_method(sel).ok_or(Inner::MethodNotFound)?;
+ verify_method_signature(method, A::ENCODINGS, &R::ENCODING_RETURN)
+ }
+}
+
+standard_pointer_impls!(AnyClass);
+
+unsafe impl RefEncode for AnyClass {
+ const ENCODING_REF: Encoding = Encoding::Class;
+}
+
+// SAFETY: Classes act as objects, and can be sent messages.
+unsafe impl Message for AnyClass {}
+
+impl fmt::Debug for AnyClass {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("AnyClass")
+ .field("name", &self.name())
+ .finish_non_exhaustive()
+ }
+}
+
+impl fmt::Display for AnyClass {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Classes are usually UTF-8, so it's probably fine to do a lossy
+ // conversion here.
+ fmt::Display::fmt(&self.name().to_string_lossy(), f)
+ }
+}
+
+impl AsRef<Self> for AnyClass {
+ fn as_ref(&self) -> &Self {
+ self
+ }
+}
+
+// This is the same as what Swift allows (`AnyClass` coerces to `AnyObject`).
+impl AsRef<AnyObject> for AnyClass {
+ fn as_ref(&self) -> &AnyObject {
+ &self.inner
+ }
+}
+
+/// An opaque type that represents a protocol in the Objective-C runtime.
+///
+/// See [`ProtocolObject`] for objects that implement a specific protocol.
+//
+// The naming of this follows GNUStep; this struct does not exist in Apple's
+// runtime, there `Protocol` is a type alias of `objc_object`.
+#[repr(C)]
+#[doc(alias = "objc_protocol")]
+pub struct AnyProtocol {
+ inner: AnyObject,
+}
+
+/// Use [`AnyProtocol`] instead.
+#[deprecated = "renamed to `runtime::AnyProtocol`"]
+pub type Protocol = AnyProtocol;
+
+// SAFETY: AnyProtocol is immutable (and can be retrieved from AnyClass anyhow).
+unsafe impl Sync for AnyProtocol {}
+unsafe impl Send for AnyProtocol {}
+impl UnwindSafe for AnyProtocol {}
+impl RefUnwindSafe for AnyProtocol {}
+// Note that Unpin is not applicable.
+
+impl AnyProtocol {
+ /// Returns the protocol definition of a specified protocol, or [`None`]
+ /// if the protocol is not registered with the Objective-C runtime.
+ #[inline]
+ #[doc(alias = "objc_getProtocol")]
+ pub fn get(name: &CStr) -> Option<&'static Self> {
+ unsafe {
+ let proto = ffi::objc_getProtocol(name.as_ptr());
+ proto.cast::<Self>().as_ref()
+ }
+ }
+
+ /// Obtains the list of registered protocol definitions.
+ #[doc(alias = "objc_copyProtocolList")]
+ pub fn protocols() -> MallocSlice!(&'static Self) {
+ unsafe {
+ let mut count: c_uint = 0;
+ let protocols: *mut &Self = ffi::objc_copyProtocolList(&mut count).cast();
+ MallocSlice::from_array(protocols, count as usize)
+ }
+ }
+
+ /// Get a list of the protocols to which this protocol conforms.
+ #[doc(alias = "protocol_copyProtocolList")]
+ pub fn adopted_protocols(&self) -> MallocSlice!(&AnyProtocol) {
+ unsafe {
+ let mut count: c_uint = 0;
+ let protocols: *mut &AnyProtocol =
+ ffi::protocol_copyProtocolList(self, &mut count).cast();
+ MallocSlice::from_array(protocols, count as usize)
+ }
+ }
+
+ /// Checks whether this protocol conforms to the specified protocol.
+ #[inline]
+ #[doc(alias = "protocol_conformsToProtocol")]
+ pub fn conforms_to(&self, proto: &AnyProtocol) -> bool {
+ unsafe { ffi::protocol_conformsToProtocol(self, proto).as_bool() }
+ }
+
+ /// Returns the name of self.
+ #[inline]
+ #[doc(alias = "protocol_getName")]
+ pub fn name(&self) -> &CStr {
+ unsafe { CStr::from_ptr(ffi::protocol_getName(self)) }
+ }
+
+ fn method_descriptions_inner(&self, required: bool, instance: bool) -> Vec<MethodDescription> {
+ let mut count: c_uint = 0;
+ let descriptions = unsafe {
+ ffi::protocol_copyMethodDescriptionList(
+ self,
+ Bool::new(required),
+ Bool::new(instance),
+ &mut count,
+ )
+ };
+ if descriptions.is_null() {
+ return Vec::new();
+ }
+ let descriptions = unsafe { MallocSlice::from_array(descriptions, count as usize) };
+ descriptions
+ .iter()
+ .map(|desc| {
+ unsafe { MethodDescription::from_raw(*desc) }.expect("invalid method description")
+ })
+ .collect()
+ }
+
+ #[allow(dead_code)]
+ #[doc(alias = "protocol_copyMethodDescriptionList")]
+ pub(crate) fn method_descriptions(&self, required: bool) -> Vec<MethodDescription> {
+ self.method_descriptions_inner(required, true)
+ }
+
+ #[allow(dead_code)]
+ #[doc(alias = "protocol_copyMethodDescriptionList")]
+ pub(crate) fn class_method_descriptions(&self, required: bool) -> Vec<MethodDescription> {
+ self.method_descriptions_inner(required, false)
+ }
+}
+
+impl PartialEq for AnyProtocol {
+ /// Check whether the protocols are equal, or conform to each other.
+ #[inline]
+ #[doc(alias = "protocol_isEqual")]
+ fn eq(&self, other: &Self) -> bool {
+ unsafe { ffi::protocol_isEqual(self, other).as_bool() }
+ }
+}
+
+impl Eq for AnyProtocol {}
+
+// Don't implement `Hash` for protocol, it is unclear how that would work
+
+unsafe impl RefEncode for AnyProtocol {
+ // Protocols are objects internally.
+ const ENCODING_REF: Encoding = Encoding::Object;
+}
+
+/// Note that protocols are objects, though sending messages to them is
+/// officially deprecated.
+//
+// SAFETY: Protocols are NSObjects internally (and somewhat publicly, see e.g.
+// `objc/Protocol.h`), and are returned as `Retained` in various places in
+// Foundation. But that's considered deprecated, so we don't implement
+// ClassType for them (even though the "Protocol" class exists).
+unsafe impl Message for AnyProtocol {}
+
+impl fmt::Debug for AnyProtocol {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("AnyProtocol")
+ .field("name", &self.name())
+ .finish_non_exhaustive()
+ }
+}
+
+impl fmt::Display for AnyProtocol {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Protocols are usually UTF-8, so it's probably fine to do a lossy
+ // conversion here.
+ fmt::Display::fmt(&self.name().to_string_lossy(), f)
+ }
+}
+
+impl AsRef<Self> for AnyProtocol {
+ fn as_ref(&self) -> &Self {
+ self
+ }
+}
+
+impl AsRef<AnyObject> for AnyProtocol {
+ fn as_ref(&self) -> &AnyObject {
+ &self.inner
+ }
+}
+
+/// An Objective-C object.
+///
+/// This is slightly different from [`NSObject`] in that it may represent an
+/// instance of an _arbitrary_ Objective-C class (e.g. it does not have to be
+/// a subclass of `NSObject`, so it can represent other root classes like
+/// `NSProxy`).
+///
+/// `Retained<AnyObject>` is equivalent to Objective-C's `id _Nonnull`.
+///
+/// This is an opaque type that contains [`UnsafeCell`], and is similar to
+/// that in that one can safely access and perform interior mutability on this
+/// (both via [`msg_send!`] and through ivars), so long as Rust's mutability
+/// rules are upheld, and that data races are avoided.
+///
+/// Note: This is intentionally neither [`Sync`], [`Send`], [`UnwindSafe`],
+/// [`RefUnwindSafe`] nor [`Unpin`], since that is something that may change
+/// depending on the specific subclass. For example, `NSAutoreleasePool` is
+/// not `Send`, it has to be deallocated on the same thread that it was
+/// created. `NSLock` is not `Send` either.
+///
+/// [`UnsafeCell`]: core::cell::UnsafeCell
+/// [`msg_send!`]: crate::msg_send
+#[doc(alias = "id")]
+#[doc(alias = "objc_object")]
+#[repr(C)]
+pub struct AnyObject {
+ // `isa` field is deprecated, so we don't expose it here.
+ //
+ // Also, we need this to be a zero-sized, so that the compiler doesn't
+ // assume anything about the layout.
+ //
+ // Use `object_getClass` instead.
+ _priv: [u8; 0],
+ _p: ffi::OpaqueData,
+}
+
+/// Use [`AnyObject`] instead.
+#[deprecated = "renamed to `runtime::AnyObject`. Consider using the correct type from the autogenerated `objc2-*` framework crates instead though"]
+pub type Object = AnyObject;
+
+unsafe impl RefEncode for AnyObject {
+ const ENCODING_REF: Encoding = Encoding::Object;
+}
+
+// SAFETY: This is technically slightly wrong, not all objects implement the
+// standard memory management methods. But not having this impl would be too
+// restrictive, so we'll live with it.
+//
+// NOTE: AnyObject actually resolves to the class "Object" internally, but we
+// don't want to expose that publicly, so we only implement Message here, not
+// ClassType.
+unsafe impl Message for AnyObject {}
+
+impl AnyObject {
+ /// Dynamically find the class of this object.
+ ///
+ ///
+ /// # Panics
+ ///
+ /// May panic if the object is invalid (which may be the case for objects
+ /// returned from unavailable `init`/`new` methods).
+ ///
+ ///
+ /// # Example
+ ///
+ /// Check that an instance of `NSObject` has the precise class `NSObject`.
+ ///
+ /// ```
+ /// use objc2::ClassType;
+ /// use objc2::runtime::NSObject;
+ ///
+ /// let obj = NSObject::new();
+ /// assert_eq!(obj.class(), NSObject::class());
+ /// ```
+ #[inline]
+ #[doc(alias = "object_getClass")]
+ pub fn class(&self) -> &'static AnyClass {
+ let ptr = unsafe { ffi::object_getClass(self) };
+ // SAFETY: The pointer is valid, and it is safe as `'static` since
+ // classes are static (can also be verified with the fact that they
+ // can be retrieved via `AnyClass::get(self.class().name())`).
+ let cls = unsafe { ptr.as_ref() };
+
+ // The class _should_ not be NULL, because the docs only say that if
+ // the object is NULL, the class also is; and in practice, certain
+ // invalid objects can contain a NULL isa pointer.
+ cls.unwrap_or_else(|| panic!("invalid object {:?} (had NULL class)", self as *const Self))
+ }
+
+ /// Change the class of the object at runtime.
+ ///
+ /// Returns the object's previous class.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The new class must:
+ ///
+ /// 1. Be a subclass of the object's current class.
+ ///
+ /// 2. The subclass must not add any instance variables - importantly, the
+ /// instance size of old and the new classes must be the same.
+ ///
+ /// 3. Any overridden methods on the new class must be fully compatible
+ /// with the old ones.
+ ///
+ /// Note that in the general case, where arbitrary parts of the program
+ /// may be trying to modify the class of the object concurrently, these
+ /// requirements are not actually possible to uphold.
+ ///
+ /// Since usage of this function is expected to be extremely rare, and
+ /// even more so trying to do it concurrently, it is recommended that you
+ /// verify that the returned class is what you would expect, and if not,
+ /// panic.
+ #[inline]
+ #[doc(alias = "object_setClass")]
+ pub unsafe fn set_class<'s>(this: &Self, cls: &AnyClass) -> &'s AnyClass {
+ let this: *const Self = this;
+ let this = this as *mut Self;
+ let ptr = unsafe { ffi::object_setClass(this, cls) };
+ // SAFETY: The class is not NULL because the object is not NULL.
+ let old_cls = unsafe { ptr.as_ref().unwrap_unchecked() };
+ // TODO: Check the superclass requirement too?
+ debug_assert_eq!(
+ old_cls.instance_size(),
+ cls.instance_size(),
+ "old and new class sizes were not equal; this is UB!"
+ );
+ old_cls
+ }
+
+ /// Offset an object pointer to get a pointer to an ivar.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The offset must be valid for the given type.
+ #[inline]
+ pub(crate) unsafe fn ivar_at_offset<T>(ptr: NonNull<Self>, offset: isize) -> NonNull<T> {
+ // `offset` is given in bytes, so we convert to `u8` and back to `T`
+ let ptr: NonNull<u8> = ptr.cast();
+ let ptr: *mut u8 = ptr.as_ptr();
+ // SAFETY: The offset is valid
+ let ptr: *mut u8 = unsafe { ptr.offset(offset) };
+ // SAFETY: The offset operation is guaranteed to not end up computing
+ // a NULL pointer.
+ let ptr: NonNull<u8> = unsafe { NonNull::new_unchecked(ptr) };
+ let ptr: NonNull<T> = ptr.cast();
+ ptr
+ }
+
+ pub(crate) fn lookup_instance_variable_dynamically(&self, name: &str) -> &'static Ivar {
+ let name = CString::new(name).unwrap();
+ let cls = self.class();
+ cls.instance_variable(&name)
+ .unwrap_or_else(|| panic!("ivar {name:?} not found on class {cls}"))
+ }
+
+ /// Use [`Ivar::load`] instead.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The object must have an instance variable with the given name, and it
+ /// must be of type `T`.
+ ///
+ /// See [`Ivar::load_ptr`] for details surrounding this.
+ #[deprecated = "this is difficult to use correctly, use `Ivar::load` instead."]
+ pub unsafe fn get_ivar<T: Encode>(&self, name: &str) -> &T {
+ let ivar = self.lookup_instance_variable_dynamically(name);
+ // SAFETY: Upheld by caller
+ unsafe { ivar.load::<T>(self) }
+ }
+
+ /// Use [`Ivar::load_mut`] instead.
+ ///
+ ///
+ /// # Safety
+ ///
+ /// The object must have an instance variable with the given name, and it
+ /// must be of type `T`.
+ ///
+ /// See [`Ivar::load_ptr`] for details surrounding this.
+ #[deprecated = "this is difficult to use correctly, use `Ivar::load_mut` instead."]
+ pub unsafe fn get_mut_ivar<T: Encode>(&mut self, name: &str) -> &mut T {
+ let ivar = self.lookup_instance_variable_dynamically(name);
+ // SAFETY: Upheld by caller
+ unsafe { ivar.load_mut::<T>(self) }
+ }
+
+ pub(crate) fn is_kind_of_class(&self, cls: &AnyClass) -> Bool {
+ // SAFETY: The signature is correct.
+ //
+ // Note that `isKindOfClass:` is not available on every object, but it
+ // is still safe to _use_, since the runtime will simply crash if the
+ // selector isn't implemented. This is of course not _ideal_, but it
+ // works for all of Apple's Objective-C classes, and it's what Swift
+ // does.
+ //
+ // In theory, someone could have made a root object, and overwritten
+ // `isKindOfClass:` to do something bogus - but that would conflict
+ // with normal Objective-C code as well, so we will consider such a
+ // thing unsound by construction.
+ unsafe { msg_send![self, isKindOfClass: cls] }
+ }
+
+ /// Attempt to downcast the object to a class of type `T`.
+ ///
+ /// This is the reference-variant. Use [`Retained::downcast`] if you want
+ /// to convert a retained object to another type.
+ ///
+ /// [`Retained::downcast`]: crate::rc::Retained::downcast
+ ///
+ ///
+ /// # Mutable classes
+ ///
+ /// Some classes have immutable and mutable variants, such as `NSString`
+ /// and `NSMutableString`.
+ ///
+ /// When some Objective-C API signature says it gives you an immutable
+ /// class, it generally expects you to not mutate that, even though it may
+ /// technically be mutable "under the hood".
+ ///
+ /// So using this method to convert a `NSString` to a `NSMutableString`,
+ /// while not unsound, is generally frowned upon unless you created the
+ /// string yourself, or the API explicitly documents the string to be
+ /// mutable.
+ ///
+ /// See Apple's [documentation on mutability][apple-mut] and [on
+ /// `isKindOfClass:`][iskindof-doc] for more details.
+ ///
+ /// [iskindof-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418511-iskindofclass?language=objc
+ /// [apple-mut]: https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/ObjectMutability/ObjectMutability.html
+ ///
+ ///
+ /// # Generic classes
+ ///
+ /// Objective-C generics are called "lightweight generics", and that's
+ /// because they aren't exposed in the runtime. This makes it impossible
+ /// to safely downcast to generic collections, so this is disallowed by
+ /// this method.
+ ///
+ /// You can, however, safely downcast to generic collections where all the
+ /// type-parameters are [`AnyObject`].
+ ///
+ ///
+ /// # Panics
+ ///
+ /// This works internally by calling `isKindOfClass:`. That means that the
+ /// object must have the instance method of that name, and an exception
+ /// will be thrown (if CoreFoundation is linked) or the process will abort
+ /// if that is not the case. In the vast majority of cases, you don't need
+ /// to worry about this, since both root objects [`NSObject`] and
+ /// `NSProxy` implement this method.
+ ///
+ ///
+ /// # Examples
+ ///
+ /// Cast an `NSString` back and forth from `NSObject`.
+ ///
+ /// ```
+ /// use objc2::rc::Retained;
+ /// use objc2_foundation::{NSObject, NSString};
+ ///
+ /// let obj: Retained<NSObject> = NSString::new().into_super();
+ /// let string = obj.downcast_ref::<NSString>().unwrap();
+ /// // Or with `downcast`, if we do not need the object afterwards
+ /// let string = obj.downcast::<NSString>().unwrap();
+ /// ```
+ ///
+ /// Try (and fail) to cast an `NSObject` to an `NSString`.
+ ///
+ /// ```
+ /// use objc2_foundation::{NSObject, NSString};
+ ///
+ /// let obj = NSObject::new();
+ /// assert!(obj.downcast_ref::<NSString>().is_none());
+ /// ```
+ ///
+ /// Try to cast to an array of strings.
+ ///
+ /// ```compile_fail,E0277
+ /// use objc2_foundation::{NSArray, NSObject, NSString};
+ ///
+ /// let arr = NSArray::from_retained_slice(&[NSObject::new()]);
+ /// // This is invalid and doesn't type check.
+ /// let arr = arr.downcast_ref::<NSArray<NSString>>();
+ /// ```
+ ///
+ /// This fails to compile, since it would require enumerating over the
+ /// array to ensure that each element is of the desired type, which is a
+ /// performance pitfall.
+ ///
+ /// Downcast when processing each element instead.
+ ///
+ /// ```
+ /// use objc2_foundation::{NSArray, NSObject, NSString};
+ ///
+ /// let arr = NSArray::from_retained_slice(&[NSObject::new()]);
+ ///
+ /// for elem in arr {
+ /// if let Some(data) = elem.downcast_ref::<NSString>() {
+ /// // handle `data`
+ /// }
+ /// }
+ /// ```
+ #[inline]
+ pub fn downcast_ref<T: DowncastTarget>(&self) -> Option<&T> {
+ if self.is_kind_of_class(T::class()).as_bool() {
+ // SAFETY: Just checked that the object is a class of type `T`.
+ //
+ // Generic `T` like `NSArray<NSString>` are ruled out by
+ // `T: DowncastTarget`.
+ Some(unsafe { &*(self as *const Self).cast::<T>() })
+ } else {
+ None
+ }
+ }
+
+ // objc_setAssociatedObject
+ // objc_getAssociatedObject
+ // objc_removeAssociatedObjects
+}
+
+impl fmt::Debug for AnyObject {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let ptr: *const Self = self;
+ write!(f, "<{}: {:p}>", self.class(), ptr)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use alloc::ffi::CString;
+ use alloc::format;
+ use alloc::string::ToString;
+ use core::mem::size_of;
+
+ use super::*;
+ use crate::test_utils;
+ use crate::{class, msg_send, sel, ClassType, ProtocolType};
+
+ // TODO: Remove once c"" strings are in MSRV
+ fn c(s: &str) -> CString {
+ CString::new(s).unwrap()
+ }
+
+ #[test]
+ fn test_selector() {
+ macro_rules! test_sel {
+ ($s:literal, $($tt:tt)+) => {{
+ let sel = sel!($($tt)*);
+ let expected = Sel::register(&c($s));
+ assert_eq!(sel, expected);
+ assert_eq!(sel.name().to_str(), Ok($s));
+ }}
+ }
+ test_sel!("abc", abc);
+ test_sel!("abc:", abc:);
+ test_sel!("abc:def:", abc:def:);
+ test_sel!("abc:def:ghi:", abc:def:ghi:);
+ test_sel!("functionWithControlPoints::::", functionWithControlPoints::::);
+ test_sel!("initWithControlPoints::::", initWithControlPoints::::);
+ test_sel!("setFloatValue::", setFloatValue::);
+ test_sel!("isSupported::", isSupported::);
+ test_sel!("addEventListener:::", addEventListener:::);
+ test_sel!("test::arg::", test::arg::);
+ test_sel!("test::::with::spaces::", test : :: : with : : spaces : :);
+ test_sel!("a::b:", a::b:);
+ }
+
+ #[test]
+ fn test_empty_selector() {
+ let s = c("");
+ let sel = Sel::register(&s);
+ assert_eq!(sel.name(), &*s);
+ let s = c(":");
+ let sel = Sel::register(&s);
+ assert_eq!(sel.name(), &*s);
+ let s = c("::");
+ let sel = Sel::register(&s);
+ assert_eq!(sel.name(), &*s);
+ }
+
+ #[test]
+ fn test_ivar() {
+ let cls = test_utils::custom_class();
+ let ivar = cls.instance_variable(&c("_foo")).unwrap();
+ assert_eq!(ivar.name(), &*c("_foo"));
+ assert!(<u32>::ENCODING.equivalent_to_str(ivar.type_encoding().to_str().unwrap()));
+ assert!(ivar.offset() > 0);
+ assert!(cls.instance_variables().len() > 0);
+ }
+
+ #[test]
+ fn test_instance_method() {
+ let cls = test_utils::custom_class();
+ let sel = Sel::register(&c("foo"));
+ let method = cls.instance_method(sel).unwrap();
+ assert_eq!(method.name().name(), &*c("foo"));
+ assert_eq!(method.arguments_count(), 2);
+
+ assert!(<u32>::ENCODING.equivalent_to_str(method.return_type().to_str().unwrap()));
+ assert!(Sel::ENCODING.equivalent_to_str(method.argument_type(1).unwrap().to_str().unwrap()));
+
+ assert!(cls.instance_methods().contains(&method));
+ }
+
+ #[test]
+ fn test_class_method() {
+ let cls = test_utils::custom_class();
+ let method = cls.class_method(sel!(classFoo)).unwrap();
+ assert_eq!(method.name().name(), &*c("classFoo"));
+ assert_eq!(method.arguments_count(), 2);
+
+ assert!(<u32>::ENCODING.equivalent_to_str(method.return_type().to_str().unwrap()));
+ assert!(Sel::ENCODING.equivalent_to_str(method.argument_type(1).unwrap().to_str().unwrap()));
+
+ assert!(cls.metaclass().instance_methods().contains(&method));
+ }
+
+ #[test]
+ fn test_class() {
+ let cls = test_utils::custom_class();
+ assert_eq!(cls.name(), &*c("CustomObject"));
+ assert!(cls.instance_size() > 0);
+ assert!(cls.superclass().is_none());
+
+ assert!(cls.responds_to(sel!(foo)));
+ assert!(cls.responds_to(sel!(setBar:)));
+ assert!(cls.responds_to(sel!(test::test::)));
+ assert!(!cls.responds_to(sel!(abc)));
+ assert!(!cls.responds_to(sel!(addNumber:toNumber:)));
+
+ assert_eq!(AnyClass::get(cls.name()), Some(cls));
+
+ let metaclass = cls.metaclass();
+ // The metaclass of a root class is a subclass of the root class
+ assert_eq!(metaclass.superclass().unwrap(), cls);
+ assert!(metaclass.responds_to(sel!(addNumber:toNumber:)));
+ assert!(metaclass.responds_to(sel!(test::test::)));
+ // TODO: This is unexpected!
+ assert!(metaclass.responds_to(sel!(foo)));
+
+ let subclass = test_utils::custom_subclass();
+ assert_eq!(subclass.superclass().unwrap(), cls);
+ }
+
+ #[test]
+ fn test_classes_count() {
+ assert!(AnyClass::classes_count() > 0);
+ }
+
+ #[test]
+ fn test_classes() {
+ let classes = AnyClass::classes();
+ assert!(classes.len() > 0);
+ }
+
+ #[test]
+ fn test_protocol() {
+ let proto = test_utils::custom_protocol();
+ assert_eq!(proto.name(), &*c("CustomProtocol"));
+ let class = test_utils::custom_class();
+ assert!(class.conforms_to(proto));
+
+ // The selectors are broken somehow on GNUStep < 2.0
+ if cfg!(any(not(feature = "gnustep-1-7"), feature = "gnustep-2-0")) {
+ let desc = MethodDescription {
+ sel: sel!(setBar:),
+ types: CStr::from_bytes_with_nul(b"v@:i\0").unwrap(),
+ };
+ assert_eq!(&proto.method_descriptions(true), &[desc]);
+ let desc = MethodDescription {
+ sel: sel!(getName),
+ types: CStr::from_bytes_with_nul(b"*@:\0").unwrap(),
+ };
+ assert_eq!(&proto.method_descriptions(false), &[desc]);
+ let desc = MethodDescription {
+ sel: sel!(addNumber:toNumber:),
+ types: CStr::from_bytes_with_nul(b"i@:ii\0").unwrap(),
+ };
+ assert_eq!(&proto.class_method_descriptions(true), &[desc]);
+ }
+ assert_eq!(&proto.class_method_descriptions(false), &[]);
+
+ assert!(class.adopted_protocols().contains(&proto));
+ }
+
+ #[test]
+ fn test_protocol_method() {
+ let class = test_utils::custom_class();
+ let result: i32 = unsafe { msg_send![class, addNumber: 1, toNumber: 2] };
+ assert_eq!(result, 3);
+ }
+
+ #[test]
+ fn class_self() {
+ let cls = NSObject::class();
+ let result: &'static AnyClass = unsafe { msg_send![cls, self] };
+ assert_eq!(cls, result);
+ }
+
+ #[test]
+ fn test_subprotocols() {
+ let sub_proto = test_utils::custom_subprotocol();
+ let super_proto = test_utils::custom_protocol();
+ assert!(sub_proto.conforms_to(super_proto));
+ assert_eq!(sub_proto.adopted_protocols()[0], super_proto);
+ }
+
+ #[test]
+ fn test_protocols() {
+ // Ensure that a protocol has been registered on linux
+ let _ = test_utils::custom_protocol();
+
+ assert!(AnyProtocol::protocols().len() > 0);
+ }
+
+ #[test]
+ fn test_object() {
+ let obj = test_utils::custom_object();
+ let cls = test_utils::custom_class();
+ assert_eq!(obj.class(), cls);
+
+ let ivar = cls.instance_variable(&c("_foo")).unwrap();
+
+ unsafe { *ivar.load_ptr::<u32>(&obj) = 4 };
+ let result = unsafe { *ivar.load::<u32>(&obj) };
+ assert_eq!(result, 4);
+ }
+
+ #[test]
+ fn test_object_ivar_unknown() {
+ let cls = test_utils::custom_class();
+ assert_eq!(cls.instance_variable(&c("unknown")), None);
+ }
+
+ #[test]
+ fn test_no_ivars() {
+ let cls = ClassBuilder::new(&c("NoIvarObject"), NSObject::class())
+ .unwrap()
+ .register();
+ assert_eq!(cls.instance_variables().len(), 0);
+ }
+
+ #[test]
+ #[cfg_attr(
+ all(debug_assertions, not(feature = "disable-encoding-assertions")),
+ should_panic = "wrong encoding. Tried to retrieve ivar with encoding I, but the encoding of the given type was C"
+ )]
+ fn test_object_ivar_wrong_type() {
+ let obj = test_utils::custom_object();
+ let cls = test_utils::custom_class();
+ let ivar = cls.instance_variable(&c("_foo")).unwrap();
+ let _ = unsafe { *ivar.load::<u8>(&obj) };
+ }
+
+ #[test]
+ fn test_encode() {
+ fn assert_enc<T: Encode>(expected: &str) {
+ assert_eq!(&T::ENCODING.to_string(), expected);
+ }
+ assert_enc::<&AnyObject>("@");
+ assert_enc::<*mut AnyObject>("@");
+ assert_enc::<&AnyClass>("#");
+ assert_enc::<Sel>(":");
+ assert_enc::<Option<Sel>>(":");
+ assert_enc::<Imp>("^?");
+ assert_enc::<Option<Imp>>("^?");
+ assert_enc::<&AnyProtocol>("@");
+ }
+
+ #[test]
+ fn test_send_sync() {
+ fn assert_send_sync<T: Send + Sync + ?Sized>() {}
+ assert_send_sync::<Bool>();
+ assert_send_sync::<AnyClass>();
+ assert_send_sync::<Ivar>();
+ assert_send_sync::<Method>();
+ assert_send_sync::<AnyProtocol>();
+ assert_send_sync::<Sel>();
+ }
+
+ #[test]
+ fn test_debug_display() {
+ let sel = sel!(abc:);
+ assert_eq!(format!("{sel}"), "abc:");
+ assert_eq!(format!("{sel:?}"), "Sel(\"abc:\")");
+ let cls = test_utils::custom_class();
+ assert_eq!(format!("{cls}"), "CustomObject");
+ assert_eq!(
+ format!("{cls:?}"),
+ "AnyClass { name: \"CustomObject\", .. }"
+ );
+ let protocol = test_utils::custom_protocol();
+ assert_eq!(format!("{protocol}"), "CustomProtocol");
+ assert_eq!(
+ format!("{protocol:?}"),
+ "AnyProtocol { name: \"CustomProtocol\", .. }"
+ );
+
+ let object = test_utils::custom_object();
+ assert_eq!(
+ format!("{:?}", &*object),
+ format!("CustomObject(<CustomObject: {:p}>)", &*object)
+ );
+ }
+
+ #[test]
+ fn test_multiple_colon() {
+ let class = test_utils::custom_class();
+ let res: i32 = unsafe {
+ MessageReceiver::send_message(class, sel!(test::test::), (1i32, 2i32, 3i32, 4i32))
+ };
+ assert_eq!(res, 10);
+
+ let obj = test_utils::custom_object();
+ let res: i32 = unsafe {
+ MessageReceiver::send_message(&*obj, sel!(test::test::), (1i32, 2i32, 3i32, 4i32))
+ };
+ assert_eq!(res, 24);
+ }
+
+ #[test]
+ fn test_sizes() {
+ assert_eq!(size_of::<Sel>(), size_of::<*const ()>());
+ assert_eq!(size_of::<Sel>(), size_of::<Option<Sel>>());
+
+ // These must be zero-sized until we get extern types, otherwise the
+ // optimizer may invalidly assume something about their layout.
+ assert_eq!(size_of::<AnyClass>(), 0);
+ assert_eq!(size_of::<AnyObject>(), 0);
+ assert_eq!(size_of::<AnyProtocol>(), 0);
+ assert_eq!(size_of::<Ivar>(), 0);
+ assert_eq!(size_of::<Method>(), 0);
+ }
+
+ fn get_ivar_layout(cls: &AnyClass) -> *const u8 {
+ unsafe { ffi::class_getIvarLayout(cls) }
+ }
+
+ #[test]
+ #[cfg_attr(
+ feature = "gnustep-1-7",
+ ignore = "ivar layout is still used on GNUStep"
+ )]
+ fn test_layout_does_not_matter_any_longer() {
+ assert!(get_ivar_layout(class!(NSObject)).is_null());
+ assert!(get_ivar_layout(class!(NSArray)).is_null());
+ assert!(get_ivar_layout(class!(NSException)).is_null());
+ assert!(get_ivar_layout(class!(NSNumber)).is_null());
+ assert!(get_ivar_layout(class!(NSString)).is_null());
+ }
+
+ #[test]
+ fn test_non_utf8_roundtrip() {
+ // Some invalid UTF-8 character
+ let s = CStr::from_bytes_with_nul(b"\x9F\0").unwrap();
+
+ let sel = Sel::register(s);
+ assert_eq!(sel.name(), s);
+ assert_eq!(sel.to_string(), char::REPLACEMENT_CHARACTER.to_string());
+
+ let cls = ClassBuilder::new(s, NSObject::class()).unwrap().register();
+ assert_eq!(cls.name(), s);
+ assert_eq!(cls.to_string(), char::REPLACEMENT_CHARACTER.to_string());
+
+ let cls_runtime = AnyClass::get(s).unwrap();
+ assert_eq!(cls, cls_runtime);
+ }
+
+ #[test]
+ fn class_is_object() {
+ let cls = NSObject::class();
+ let retained = cls.retain();
+ assert_eq!(&*retained, cls);
+
+ let obj: &AnyObject = cls.as_ref();
+ let superclass = obj.class();
+ assert!(superclass.conforms_to(<dyn NSObjectProtocol>::protocol().unwrap()));
+
+ // Classes are NSObject subclasses in the current runtime.
+ let ns_obj = retained.downcast::<NSObject>().unwrap();
+ // Test that we can call NSObject methods on classes.
+ assert_eq!(ns_obj, ns_obj);
+ let _ = ns_obj.retainCount();
+ }
+
+ #[test]
+ fn class_has_infinite_retain_count() {
+ let obj: &AnyObject = NSObject::class().as_ref();
+ let obj = obj.downcast_ref::<NSObject>().unwrap();
+
+ let large_retain = if cfg!(feature = "gnustep-1-7") {
+ u32::MAX as usize
+ } else {
+ usize::MAX
+ };
+
+ assert_eq!(obj.retainCount(), large_retain);
+ let obj2 = obj.retain();
+ assert_eq!(obj.retainCount(), large_retain);
+ drop(obj2);
+ assert_eq!(obj.retainCount(), large_retain);
+ }
+
+ #[test]
+ fn protocol_is_object() {
+ let protocol = <dyn NSObjectProtocol>::protocol().unwrap();
+ let retained = protocol.retain();
+ assert_eq!(&*retained, protocol);
+
+ // Protocols don't implement isKindOfClass: on GNUStep.
+ if cfg!(feature = "gnustep-1-7") {
+ return;
+ }
+
+ // In the old runtime, NSObjectProtocol are not NSObject subclasses.
+ if cfg!(all(target_os = "macos", target_arch = "x86")) {
+ let _ = retained.downcast::<NSObject>().unwrap_err();
+ } else {
+ // But elsewhere they are.
+ let obj = retained.downcast::<NSObject>().unwrap();
+ // Test that we can call NSObject methods on protocols.
+ assert_eq!(obj, obj);
+ let _ = obj.retainCount();
+ }
+ }
+}
diff --git a/third_party/rust/objc2/src/runtime/nsobject.rs b/third_party/rust/objc2/src/runtime/nsobject.rs
@@ -0,0 +1,620 @@
+use core::fmt;
+use core::hash;
+use core::ops::Deref;
+use core::panic::{RefUnwindSafe, UnwindSafe};
+
+use crate::ffi::NSUInteger;
+use crate::rc::{Allocated, DefaultRetained, Retained};
+use crate::runtime::{AnyClass, AnyObject, AnyProtocol, ImplementedBy, ProtocolObject, Sel};
+use crate::{
+ extern_conformance, extern_methods, msg_send, AnyThread, ClassType, DowncastTarget, Message,
+ ProtocolType,
+};
+
+/// The root class of most Objective-C class hierarchies.
+///
+/// This represents the [`NSObject` class][cls]. The name "NSObject" also
+/// refers to a protocol, see [`NSObjectProtocol`] for that.
+///
+/// This class has been defined in `objc` since macOS 10.8, but is also
+/// re-exported under `objc2_foundation::NSObject`, you might want to use that
+/// path instead.
+///
+/// [cls]: https://developer.apple.com/documentation/objectivec/nsobject?language=objc
+#[repr(C)]
+pub struct NSObject {
+ __superclass: AnyObject,
+}
+
+// Would be _super_ nice to have this kind of impl, but that isn't possible.
+// impl Unsize<AnyObject> for NSObject {}
+
+crate::__extern_class_impl_traits! {
+ ()
+ (unsafe impl)
+ (NSObject)
+ (AnyObject)
+}
+
+// We do not want to expose this type publicly, even though it's exposed in
+// the trait impl.
+mod private {
+ #[derive(PartialEq, Eq, Hash)] // Delegate to NSObject
+ pub struct ForDefinedSubclasses(pub(super) super::NSObject);
+}
+
+impl fmt::Debug for private::ForDefinedSubclasses {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Delegate to NSObject
+ fmt::Debug::fmt(&**self, f)
+ }
+}
+
+impl Deref for private::ForDefinedSubclasses {
+ type Target = NSObject;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+// SAFETY: `NSObject` is thread safe by itself, the only reason why it
+// isn't marked `Send` is because its subclasses may not be thread safe.
+//
+// But any subclass of NSObject that the user creates is thread safe, provided
+// that their ivars are thread safe too, and the class is not configured as
+// `MainThreadOnly`.
+//
+// This special casing of NSObject is similar to what Swift does:
+// https://developer.apple.com/documentation/swift/sendable#Sendable-Classes
+unsafe impl Send for private::ForDefinedSubclasses {}
+// SAFETY: Same as above.
+unsafe impl Sync for private::ForDefinedSubclasses {}
+
+// NSObject itself is also unwind safe, and so subclasses should be too,
+// unless they use e.g. interior mutability in their ivars.
+impl UnwindSafe for private::ForDefinedSubclasses {}
+impl RefUnwindSafe for private::ForDefinedSubclasses {}
+
+unsafe impl ClassType for NSObject {
+ type Super = AnyObject;
+ type ThreadKind = dyn AnyThread;
+ const NAME: &'static str = "NSObject";
+
+ #[inline]
+ fn class() -> &'static AnyClass {
+ crate::__class_inner!("NSObject", "NSObject")
+ }
+
+ #[inline]
+ fn as_super(&self) -> &Self::Super {
+ &self.__superclass
+ }
+
+ const __INNER: () = ();
+
+ // Defined subclasses can assume more lenient auto traits.
+ type __SubclassingType = private::ForDefinedSubclasses;
+}
+
+unsafe impl DowncastTarget for NSObject {}
+
+/// The methods that are fundamental to most Objective-C objects.
+///
+/// This represents the [`NSObject` protocol][proto].
+///
+/// You should rarely need to use this for anything other than as a trait
+/// bound in [`extern_protocol!`], to allow your protocol to implement `Debug`
+/// `Hash`, `PartialEq` and `Eq`.
+///
+/// This trait is exported under `objc2_foundation::NSObjectProtocol`, you
+/// probably want to use that path instead.
+///
+/// [proto]: https://developer.apple.com/documentation/objectivec/1418956-nsobject?language=objc
+/// [`extern_protocol!`]: crate::extern_protocol!
+///
+///
+/// # Safety
+///
+/// Like with [other protocols](ProtocolType), the type must represent a class
+/// that implements the `NSObject` protocol.
+#[allow(non_snake_case)] // Follow the naming scheme in framework crates
+pub unsafe trait NSObjectProtocol {
+ /// Check whether the object is equal to an arbitrary other object.
+ ///
+ /// Most objects that implement `NSObjectProtocol` also implements the
+ /// [`PartialEq`] trait. If the objects you are comparing are of the same
+ /// type, you likely want to use that instead.
+ ///
+ /// See [Apple's documentation][apple-doc] for details.
+ ///
+ /// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418795-isequal?language=objc
+ #[doc(alias = "isEqual:")]
+ fn isEqual(&self, other: Option<&AnyObject>) -> bool
+ where
+ Self: Sized + Message,
+ {
+ unsafe { msg_send![self, isEqual: other] }
+ }
+
+ /// An integer that can be used as a table address in a hash table
+ /// structure.
+ ///
+ /// Most objects that implement `NSObjectProtocol` also implements the
+ /// [`Hash`][std::hash::Hash] trait, you likely want to use that instead.
+ ///
+ /// See [Apple's documentation][apple-doc] for details.
+ ///
+ /// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418859-hash?language=objc
+ fn hash(&self) -> NSUInteger
+ where
+ Self: Sized + Message,
+ {
+ unsafe { msg_send![self, hash] }
+ }
+
+ /// Check if the object is an instance of the class, or one of its
+ /// subclasses.
+ ///
+ /// See [`AnyObject::downcast_ref`] or [`Retained::downcast`] if your
+ /// intention is to use this to cast an object to another, and see
+ /// [Apple's documentation][apple-doc] for more details on what you may
+ /// (and what you may not) do with this information in general.
+ ///
+ /// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418511-iskindofclass?language=objc
+ #[doc(alias = "isKindOfClass:")]
+ fn isKindOfClass(&self, cls: &AnyClass) -> bool
+ where
+ Self: Sized + Message,
+ {
+ unsafe { msg_send![self, isKindOfClass: cls] }
+ }
+
+ /// Check if the object is an instance of the class type, or one of its
+ /// subclasses.
+ ///
+ /// See [`isKindOfClass`][Self::isKindOfClass] for details.
+ #[deprecated = "use `isKindOfClass` directly, or cast your objects with `AnyObject::downcast_ref`"]
+ // TODO: Use extern_protocol! once we get rid of this
+ fn is_kind_of<T: ClassType>(&self) -> bool
+ where
+ Self: Sized + Message,
+ {
+ self.isKindOfClass(T::class())
+ }
+
+ /// Check if the object is an instance of a specific class, without
+ /// checking subclasses.
+ ///
+ /// Note that this is rarely what you want, the specific class of an
+ /// object is considered a private implementation detail. Use
+ /// [`isKindOfClass`][Self::isKindOfClass] instead to check whether an
+ /// object is an instance of a given class.
+ ///
+ /// See [Apple's documentation][apple-doc] for more details.
+ ///
+ /// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418766-ismemberofclass?language=objc
+ #[doc(alias = "isMemberOfClass:")]
+ fn isMemberOfClass(&self, cls: &AnyClass) -> bool
+ where
+ Self: Sized + Message,
+ {
+ unsafe { msg_send![self, isMemberOfClass: cls] }
+ }
+
+ /// Check whether the object implements or inherits a method with the
+ /// given selector.
+ ///
+ /// See [Apple's documentation][apple-doc] for more details.
+ ///
+ /// If using this for availability checking, you might want to consider
+ /// using the [`available!`] macro instead, as it is often more
+ /// performant than this runtime check.
+ ///
+ /// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418583-respondstoselector?language=objc
+ /// [`available!`]: crate::available
+ #[doc(alias = "respondsToSelector:")]
+ fn respondsToSelector(&self, aSelector: Sel) -> bool
+ where
+ Self: Sized + Message,
+ {
+ unsafe { msg_send![self, respondsToSelector: aSelector] }
+ }
+
+ /// Check whether the object conforms to a given protocol.
+ ///
+ /// See [Apple's documentation][apple-doc] for details.
+ ///
+ /// [apple-doc]: https://developer.apple.com/documentation/objectivec/nsobject/1418893-conformstoprotocol?language=objc
+ #[doc(alias = "conformsToProtocol:")]
+ fn conformsToProtocol(&self, aProtocol: &AnyProtocol) -> bool
+ where
+ Self: Sized + Message,
+ {
+ unsafe { msg_send![self, conformsToProtocol: aProtocol] }
+ }
+
+ /// A textual representation of the object.
+ ///
+ /// The returned class is `NSString`, but since that is defined in
+ /// `objc2-foundation`, and `NSObjectProtocol` is defined in `objc2`, the
+ /// declared return type is unfortunately restricted to be [`NSObject`].
+ /// It is always safe to cast the return value of this to `NSString`.
+ ///
+ /// You might want to use the [`Debug`][fmt::Debug] impl of the object
+ /// instead, or if the object implements [`Display`][fmt::Display], the
+ /// [`to_string`][std::string::ToString::to_string] method.
+ ///
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use objc2::rc::Retained;
+ /// # use objc2::runtime::{NSObjectProtocol, NSObject, NSObject as NSString};
+ /// # #[cfg(available_in_foundation)]
+ /// use objc2_foundation::{NSObject, NSObjectProtocol, NSString};
+ ///
+ /// # let obj = NSObject::new();
+ /// // SAFETY: Descriptions are always `NSString`.
+ /// let desc: Retained<NSString> = unsafe { Retained::cast_unchecked(obj.description()) };
+ /// println!("{desc:?}");
+ /// ```
+ //
+ // Only safe to override if the user-provided return type is NSString.
+ fn description(&self) -> Retained<NSObject>
+ where
+ Self: Sized + Message,
+ {
+ unsafe { msg_send![self, description] }
+ }
+
+ /// A textual representation of the object to use when debugging.
+ ///
+ /// Like with [`description`][Self::description], the return type of this
+ /// is always `NSString`.
+ ///
+ /// LLVM's po command uses this property to create a textual
+ /// representation of the object. The default implementation returns the
+ /// same value as `description`. Override either to provide custom object
+ /// descriptions.
+ // optional, introduced in macOS 10.8
+ //
+ // Only safe to override if the user-provided return type is NSString.
+ fn debugDescription(&self) -> Retained<NSObject>
+ where
+ Self: Sized + Message,
+ {
+ unsafe { msg_send![self, debugDescription] }
+ }
+
+ /// Check whether the receiver is a subclass of the `NSProxy` root class
+ /// instead of the usual [`NSObject`].
+ ///
+ /// See [Apple's documentation][apple-doc] for details.
+ ///
+ /// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418528-isproxy?language=objc
+ ///
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use objc2::runtime::{NSObject, NSObjectProtocol};
+ ///
+ /// let obj = NSObject::new();
+ /// assert!(!obj.isProxy());
+ /// ```
+ fn isProxy(&self) -> bool
+ where
+ Self: Sized + Message,
+ {
+ unsafe { msg_send![self, isProxy] }
+ }
+
+ /// The reference count of the object.
+ ///
+ /// This can rarely be useful when debugging memory management issues,
+ /// though beware that in most real-world scenarios, your object may be
+ /// retained by several autorelease pools, especially when debug
+ /// assertions are enabled, so this value may not represent what you'd
+ /// expect.
+ ///
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use objc2::ClassType;
+ /// use objc2::runtime::{NSObject, NSObjectProtocol};
+ ///
+ /// let obj = NSObject::new();
+ /// assert_eq!(obj.retainCount(), 1);
+ /// let obj2 = obj.clone();
+ /// assert_eq!(obj.retainCount(), 2);
+ /// drop(obj2);
+ /// assert_eq!(obj.retainCount(), 1);
+ /// ```
+ fn retainCount(&self) -> NSUInteger
+ where
+ Self: Sized + Message,
+ {
+ unsafe { msg_send![self, retainCount] }
+ }
+
+ // retain, release and autorelease below to this protocol.
+}
+
+// SAFETY: Same as in extern_protocol!
+unsafe impl<T> NSObjectProtocol for ProtocolObject<T> where T: ?Sized + NSObjectProtocol {}
+// SAFETY: Same as in extern_protocol!
+unsafe impl ProtocolType for dyn NSObjectProtocol {
+ const NAME: &'static str = "NSObject";
+ const __INNER: () = ();
+}
+// SAFETY: Same as in extern_protocol!
+unsafe impl<T> ImplementedBy<T> for dyn NSObjectProtocol
+where
+ T: ?Sized + Message + NSObjectProtocol,
+{
+ const __INNER: () = ();
+}
+
+// SAFETY: Anything that implements `NSObjectProtocol` and is `Send` is valid
+// to convert to `ProtocolObject<dyn NSObjectProtocol + Send>`.
+unsafe impl<T> ImplementedBy<T> for dyn NSObjectProtocol + Send
+where
+ T: ?Sized + Message + NSObjectProtocol + Send,
+{
+ const __INNER: () = ();
+}
+
+// SAFETY: Anything that implements `NSObjectProtocol` and is `Sync` is valid
+// to convert to `ProtocolObject<dyn NSObjectProtocol + Sync>`.
+unsafe impl<T> ImplementedBy<T> for dyn NSObjectProtocol + Sync
+where
+ T: ?Sized + Message + NSObjectProtocol + Sync,
+{
+ const __INNER: () = ();
+}
+
+// SAFETY: Anything that implements `NSObjectProtocol` and is `Send + Sync` is
+// valid to convert to `ProtocolObject<dyn NSObjectProtocol + Send + Sync>`.
+unsafe impl<T> ImplementedBy<T> for dyn NSObjectProtocol + Send + Sync
+where
+ T: ?Sized + Message + NSObjectProtocol + Send + Sync,
+{
+ const __INNER: () = ();
+}
+
+extern_conformance!(
+ unsafe impl NSObjectProtocol for NSObject {}
+);
+
+#[allow(non_snake_case)] // Follow the naming scheme in framework crates
+impl NSObject {
+ extern_methods!(
+ /// Create a new empty `NSObject`.
+ ///
+ /// This method is a shorthand for calling [`alloc`][AnyThread::alloc]
+ /// and then [`init`][Self::init].
+ #[unsafe(method(new))]
+ #[unsafe(method_family = new)]
+ pub fn new() -> Retained<Self>;
+
+ /// Initialize an already allocated object.
+ ///
+ /// See [Apple's documentation][apple-doc] for details.
+ ///
+ /// [apple-doc]: https://developer.apple.com/documentation/objectivec/nsobject/1418641-init?language=objc
+ ///
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use objc2::runtime::NSObject;
+ /// use objc2::AnyThread;
+ ///
+ /// let obj = NSObject::init(NSObject::alloc());
+ /// ```
+ #[unsafe(method(init))]
+ #[unsafe(method_family = init)]
+ pub fn init(this: Allocated<Self>) -> Retained<Self>;
+
+ #[unsafe(method(doesNotRecognizeSelector:))]
+ #[unsafe(method_family = none)]
+ fn doesNotRecognizeSelector_inner(&self, sel: Sel);
+ );
+
+ /// Handle messages the object doesn’t recognize.
+ ///
+ /// See [Apple's documentation][apple-doc] for details.
+ ///
+ /// [apple-doc]: https://developer.apple.com/documentation/objectivec/nsobject/1418637-doesnotrecognizeselector?language=objc
+ pub fn doesNotRecognizeSelector(&self, sel: Sel) -> ! {
+ self.doesNotRecognizeSelector_inner(sel);
+ unreachable!("doesNotRecognizeSelector: should not return")
+ }
+}
+
+/// Objective-C equality has approximately the same semantics as Rust
+/// equality (although less aptly specified).
+///
+/// At the very least, equality is _expected_ to be symmetric and
+/// transitive, and that's about the best we can do.
+///
+/// See also <https://nshipster.com/equality/>
+impl PartialEq for NSObject {
+ #[inline]
+ #[doc(alias = "isEqual:")]
+ fn eq(&self, other: &Self) -> bool {
+ self.isEqual(Some(other))
+ }
+}
+
+/// Most types' equality is reflexive.
+impl Eq for NSObject {}
+
+/// Hashing in Objective-C has the exact same requirement as in Rust:
+///
+/// > If two objects are equal (as determined by the isEqual: method),
+/// > they must have the same hash value.
+///
+/// See <https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418859-hash>
+impl hash::Hash for NSObject {
+ #[inline]
+ fn hash<H: hash::Hasher>(&self, state: &mut H) {
+ <Self as NSObjectProtocol>::hash(self).hash(state);
+ }
+}
+
+impl fmt::Debug for NSObject {
+ #[inline]
+ #[doc(alias = "description")]
+ #[doc(alias = "debugDescription")]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let obj: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(self);
+ obj.fmt(f)
+ }
+}
+
+impl DefaultRetained for NSObject {
+ #[inline]
+ fn default_retained() -> Retained<Self> {
+ Self::new()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use alloc::format;
+
+ use crate::extern_class;
+ use crate::rc::RcTestObject;
+
+ extern_class!(
+ #[unsafe(super(NSObject))]
+ #[name = "NSObject"]
+ #[derive(Debug, PartialEq, Eq, Hash)]
+ struct FakeSubclass;
+ );
+
+ impl FakeSubclass {
+ fn new() -> Retained<Self> {
+ Retained::downcast(NSObject::new()).unwrap()
+ }
+ }
+
+ #[test]
+ fn test_deref() {
+ let obj: Retained<FakeSubclass> = FakeSubclass::new();
+ let _: &FakeSubclass = &obj;
+ let _: &NSObject = &obj;
+ let _: &AnyObject = &obj;
+ }
+
+ #[test]
+ fn test_as_ref_borrow() {
+ use core::borrow::Borrow;
+
+ fn impls_as_ref_borrow<T: AsRef<U> + Borrow<U> + ?Sized, U: ?Sized>(_: &T) {}
+
+ let obj = FakeSubclass::new();
+ impls_as_ref_borrow::<Retained<FakeSubclass>, FakeSubclass>(&obj);
+ impls_as_ref_borrow::<FakeSubclass, FakeSubclass>(&obj);
+ impls_as_ref_borrow::<NSObject, NSObject>(&obj);
+ impls_as_ref_borrow::<NSObject, AnyObject>(&obj);
+
+ let obj = NSObject::new();
+ impls_as_ref_borrow::<Retained<NSObject>, NSObject>(&obj);
+
+ fn impls_as_ref<T: AsRef<U> + ?Sized, U: ?Sized>(_: &T) {}
+
+ let obj = FakeSubclass::new();
+ impls_as_ref::<Retained<FakeSubclass>, FakeSubclass>(&obj);
+ impls_as_ref::<Retained<FakeSubclass>, NSObject>(&obj);
+ impls_as_ref::<Retained<FakeSubclass>, AnyObject>(&obj);
+ }
+
+ #[test]
+ fn test_equality() {
+ let obj1 = NSObject::new();
+ assert_eq!(obj1, obj1);
+
+ let obj2 = NSObject::new();
+ assert_ne!(obj1, obj2);
+ }
+
+ #[test]
+ fn test_hash() {
+ use core::hash::Hasher;
+ use std::collections::hash_map::DefaultHasher;
+ use std::hash::Hash;
+
+ let obj1 = NSObject::new();
+
+ let mut hashstate1 = DefaultHasher::new();
+ let mut hashstate2 = DefaultHasher::new();
+
+ obj1.hash(&mut hashstate1);
+ obj1.hash(&mut hashstate2);
+
+ assert_eq!(hashstate1.finish(), hashstate2.finish());
+
+ let obj2 = NSObject::new();
+ let mut hashstate2 = DefaultHasher::new();
+ obj2.hash(&mut hashstate2);
+ assert_ne!(hashstate1.finish(), hashstate2.finish());
+ }
+
+ #[test]
+ fn test_debug() {
+ let obj = NSObject::new();
+ let expected = format!("<NSObject: {:p}>", &*obj);
+ assert_eq!(format!("{obj:?}"), expected);
+ }
+
+ #[test]
+ fn test_is_kind_of() {
+ let obj = NSObject::new();
+ assert!(obj.isKindOfClass(NSObject::class()));
+ assert!(!obj.isKindOfClass(RcTestObject::class()));
+
+ let obj = RcTestObject::new();
+ assert!(obj.isKindOfClass(NSObject::class()));
+ assert!(obj.isKindOfClass(RcTestObject::class()));
+ }
+
+ #[test]
+ fn test_retain_same() {
+ let obj1 = NSObject::new();
+ let ptr1 = Retained::as_ptr(&obj1);
+
+ let obj2 = obj1.clone();
+ let ptr2 = Retained::as_ptr(&obj2);
+
+ assert_eq!(ptr1, ptr2);
+ }
+
+ #[test]
+ fn conforms_to_nsobjectprotocol() {
+ let protocol = <dyn NSObjectProtocol>::protocol().unwrap();
+ assert!(NSObject::class().conforms_to(protocol));
+ }
+
+ // Ensure that importing `NSObjectProtocol::hash` does not cause conflicts
+ // when using `Hash::hash` on normal types.
+ mod hash_does_not_overlap_with_normal_hash_method {
+ #[allow(unused_imports)]
+ use crate::runtime::NSObjectProtocol;
+ use std::collections::hash_map::DefaultHasher;
+ use std::hash::Hash;
+
+ #[test]
+ fn inner() {
+ let integer = 5;
+ let mut hasher = DefaultHasher::new();
+ integer.hash(&mut hasher);
+ }
+ }
+}
diff --git a/third_party/rust/objc2/src/runtime/nsproxy.rs b/third_party/rust/objc2/src/runtime/nsproxy.rs
@@ -0,0 +1,80 @@
+//! Defined here instead of in `objc2-foundation` since it's a root object,
+//! and the `extern_class!` macro doesn't support those (yet).
+use core::fmt;
+use core::hash;
+
+use crate::runtime::{AnyClass, AnyObject, NSObjectProtocol, ProtocolObject};
+use crate::{extern_conformance, AnyThread, ClassType, DowncastTarget};
+
+/// An abstract superclass defining an API for objects that act as
+/// stand-ins for other objects or for objects that don’t exist yet.
+///
+/// See [Apple's documentation][apple-doc] for more information.
+///
+/// [apple-doc]: https://developer.apple.com/documentation/foundation/nsproxy?language=objc
+#[repr(C)]
+pub struct NSProxy {
+ __superclass: AnyObject,
+}
+
+crate::__extern_class_impl_traits! {
+ ()
+ (unsafe impl)
+ (NSProxy)
+ (AnyObject)
+}
+
+unsafe impl ClassType for NSProxy {
+ type Super = AnyObject;
+ type ThreadKind = dyn AnyThread;
+ const NAME: &'static str = "NSProxy";
+
+ #[inline]
+ fn class() -> &'static AnyClass {
+ crate::__class_inner!("NSProxy", "NSProxy")
+ }
+
+ #[inline]
+ fn as_super(&self) -> &Self::Super {
+ &self.__superclass
+ }
+
+ const __INNER: () = ();
+
+ // We don't know anything about NSProxy's thread safety, so we don't do
+ // the same workaround for that as we do for NSObject.
+ type __SubclassingType = Self;
+}
+
+unsafe impl DowncastTarget for NSProxy {}
+
+extern_conformance!(
+ unsafe impl NSObjectProtocol for NSProxy {}
+);
+
+impl PartialEq for NSProxy {
+ #[inline]
+ #[doc(alias = "isEqual:")]
+ fn eq(&self, other: &Self) -> bool {
+ self.isEqual(Some(other))
+ }
+}
+
+impl Eq for NSProxy {}
+
+impl hash::Hash for NSProxy {
+ #[inline]
+ fn hash<H: hash::Hasher>(&self, state: &mut H) {
+ <NSProxy as NSObjectProtocol>::hash(self).hash(state);
+ }
+}
+
+impl fmt::Debug for NSProxy {
+ #[inline]
+ #[doc(alias = "description")]
+ #[doc(alias = "debugDescription")]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let obj: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(self);
+ obj.fmt(f)
+ }
+}
diff --git a/third_party/rust/objc2/src/runtime/nszone.rs b/third_party/rust/objc2/src/runtime/nszone.rs
@@ -0,0 +1,95 @@
+use core::fmt;
+use core::panic::{RefUnwindSafe, UnwindSafe};
+
+#[cfg(feature = "gnustep-1-7")]
+use crate::encode::Encode;
+use crate::encode::{Encoding, RefEncode};
+use crate::ffi;
+
+/// A type used to identify and manage memory zones.
+///
+/// Zones are ignored on all newer platforms, you should very rarely need to
+/// use this, but may be useful if you need to implement `copyWithZone:` or
+/// `allocWithZone:`.
+///
+/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nszone?language=objc).
+#[repr(C)]
+pub struct NSZone {
+ _priv: [u8; 0],
+ // Use `OpaqueData` to mark the types as !Send, !Sync and UnsafeCell.
+ _inner: ffi::OpaqueData,
+}
+
+impl fmt::Debug for NSZone {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "<NSZone {self:p}>")
+ }
+}
+
+// Note: We don't know anything about the internals of `NSZone`, so best not
+// to make it `Send` and `Sync` for now.
+
+impl UnwindSafe for NSZone {}
+impl RefUnwindSafe for NSZone {}
+
+unsafe impl RefEncode for NSZone {
+ #[cfg(not(feature = "gnustep-1-7"))]
+ const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Struct("_NSZone", &[]));
+ #[cfg(feature = "gnustep-1-7")]
+ const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Struct(
+ "_NSZone",
+ &[
+ // Functions
+ Encoding::Pointer(&Encoding::Unknown),
+ Encoding::Pointer(&Encoding::Unknown),
+ Encoding::Pointer(&Encoding::Unknown),
+ Encoding::Pointer(&Encoding::Unknown),
+ Encoding::Pointer(&Encoding::Unknown),
+ Encoding::Pointer(&Encoding::Unknown),
+ // Stats
+ Encoding::Pointer(&Encoding::Unknown),
+ // Zone granularity
+ usize::ENCODING,
+ // Name of zone
+ Encoding::Object,
+ // Next zone - note that the contents of this doesn't matter,
+ // since this is nested far enough that the encoding string ends
+ // up ignoring it.
+ Encoding::Pointer(&Encoding::Struct("_NSZone", &[])),
+ ],
+ ));
+}
+
+#[cfg(test)]
+mod tests {
+ use alloc::string::ToString;
+ use core::ptr;
+
+ use super::*;
+ use crate::msg_send;
+ use crate::rc::Allocated;
+ use crate::runtime::NSObject;
+ use crate::ClassType;
+
+ #[test]
+ fn alloc_with_zone() {
+ let zone: *const NSZone = ptr::null();
+ let _obj: Allocated<NSObject> =
+ unsafe { msg_send![NSObject::class(), allocWithZone: zone] };
+ }
+
+ #[test]
+ fn verify_encoding() {
+ let expected = if cfg!(all(feature = "gnustep-1-7", target_pointer_width = "64")) {
+ "^{_NSZone=^?^?^?^?^?^?^?Q@^{_NSZone}}"
+ } else if cfg!(all(
+ feature = "gnustep-1-7",
+ not(target_pointer_width = "64")
+ )) {
+ "^{_NSZone=^?^?^?^?^?^?^?I@^{_NSZone}}"
+ } else {
+ "^{_NSZone=}"
+ };
+ assert_eq!(NSZone::ENCODING_REF.to_string(), expected);
+ }
+}
diff --git a/third_party/rust/objc2/src/runtime/protocol_object.rs b/third_party/rust/objc2/src/runtime/protocol_object.rs
@@ -0,0 +1,413 @@
+use core::fmt;
+use core::hash;
+use core::marker::PhantomData;
+use core::ptr::NonNull;
+
+use crate::encode::{Encoding, RefEncode};
+use crate::rc::{autoreleasepool_leaking, Retained};
+use crate::runtime::__nsstring::nsstring_to_str;
+use crate::runtime::{AnyObject, NSObjectProtocol};
+use crate::Message;
+
+/// An internal helper trait for [`ProtocolObject`].
+///
+///
+/// # Safety
+///
+/// This is meant to be a sealed trait, and should not be implemented outside
+/// of the [`extern_protocol!`] macro.
+///
+/// [`extern_protocol!`]: crate::extern_protocol
+pub unsafe trait ImplementedBy<T: ?Sized + Message> {
+ #[doc(hidden)]
+ const __INNER: ();
+}
+
+/// An object representing any object that implements a specified protocol.
+///
+/// Objective-C has [a feature][protocol-type-checking] where you can write
+/// `id<MyProtocol>`, and then work with the protocol as-if it was an object;
+/// this is very similar to `dyn` traits in Rust!
+///
+/// If we could customize how `dyn Trait` works, then this struct would not
+/// have been necessary; however, `dyn Trait` is a wide pointer with overhead,
+/// which this struct helps avoid.
+///
+/// If the trait `T` inherits [`NSObjectProtocol`], this will implement common
+/// traits like `Debug`, `PartialEq`, `Eq` and `Hash`.
+///
+/// [protocol-type-checking]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProtocols.html#//apple_ref/doc/uid/TP30001163-CH15-TPXREF151
+///
+///
+/// # Example
+///
+/// Convert an object `MyObject` that implements the a protocol `MyProtocol`
+/// into a [`ProtocolObject`] for working with the protocol in a type-erased
+/// way.
+///
+/// ```
+/// use objc2::runtime::ProtocolObject;
+/// use objc2::rc::Retained;
+/// # use objc2::runtime::NSObject as MyObject;
+/// # use objc2::runtime::NSObjectProtocol as MyProtocol;
+///
+/// let obj: Retained<MyObject> = MyObject::new();
+/// let proto: &ProtocolObject<dyn MyProtocol> = ProtocolObject::from_ref(&*obj);
+/// let proto: Retained<ProtocolObject<dyn MyProtocol>> = ProtocolObject::from_retained(obj);
+/// ```
+#[doc(alias = "id")]
+#[repr(C)]
+pub struct ProtocolObject<P: ?Sized> {
+ inner: AnyObject,
+ p: PhantomData<P>,
+}
+
+// SAFETY: `Send` if the underlying trait promises `Send`.
+//
+// E.g. `ProtocolObject<dyn NSObjectProtocol + Send>` is naturally `Send`.
+unsafe impl<P: ?Sized + Send> Send for ProtocolObject<P> {}
+
+// SAFETY: `Sync` if the underlying trait promises `Sync`.
+//
+// E.g. `ProtocolObject<dyn NSObjectProtocol + Sync>` is naturally `Sync`.
+unsafe impl<P: ?Sized + Sync> Sync for ProtocolObject<P> {}
+
+// SAFETY: The type is `#[repr(C)]` and `AnyObject` internally
+unsafe impl<P: ?Sized> RefEncode for ProtocolObject<P> {
+ const ENCODING_REF: Encoding = Encoding::Object;
+}
+
+// SAFETY: The type is `AnyObject` internally, and is mean to be messaged
+// as-if it's an object.
+unsafe impl<P: ?Sized> Message for ProtocolObject<P> {}
+
+impl<P: ?Sized> ProtocolObject<P> {
+ /// Get an immutable type-erased reference from a type implementing a
+ /// protocol.
+ #[inline]
+ pub fn from_ref<T: ?Sized + Message>(obj: &T) -> &Self
+ where
+ P: ImplementedBy<T>,
+ {
+ let ptr: NonNull<T> = NonNull::from(obj);
+ let ptr: NonNull<Self> = ptr.cast();
+ // SAFETY: Implementer ensures that the object conforms to the
+ // protocol; so converting the reference here is safe.
+ unsafe { ptr.as_ref() }
+ }
+
+ /// Get a type-erased object from a type implementing a protocol.
+ #[deprecated = "use `ProtocolObject::from_retained` instead"]
+ #[inline]
+ pub fn from_id<T>(obj: Retained<T>) -> Retained<Self>
+ where
+ P: ImplementedBy<T> + 'static,
+ T: Message + 'static,
+ {
+ Self::from_retained(obj)
+ }
+
+ /// Get a type-erased object from a type implementing a protocol.
+ #[inline]
+ pub fn from_retained<T>(obj: Retained<T>) -> Retained<Self>
+ where
+ P: ImplementedBy<T> + 'static,
+ T: Message + 'static,
+ {
+ // SAFETY:
+ // - The type can be represented as the casted-to type.
+ // - Both types are `'static` (this could maybe be relaxed a bit, but
+ // let's be on the safe side)!
+ unsafe { Retained::cast_unchecked::<Self>(obj) }
+ }
+}
+
+impl<P: ?Sized + NSObjectProtocol> PartialEq for ProtocolObject<P> {
+ #[inline]
+ #[doc(alias = "isEqual:")]
+ fn eq(&self, other: &Self) -> bool {
+ self.isEqual(Some(&other.inner))
+ }
+}
+
+impl<P: ?Sized + NSObjectProtocol> Eq for ProtocolObject<P> {}
+
+impl<P: ?Sized + NSObjectProtocol> hash::Hash for ProtocolObject<P> {
+ #[inline]
+ fn hash<H: hash::Hasher>(&self, state: &mut H) {
+ <Self as NSObjectProtocol>::hash(self).hash(state);
+ }
+}
+
+impl<P: ?Sized + NSObjectProtocol> fmt::Debug for ProtocolObject<P> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let description = self.description();
+
+ // `NSString`s in return types, such as the one in `description`, are
+ // in general _supposed_ to be immutable:
+ // <https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/ObjectMutability/ObjectMutability.html#//apple_ref/doc/uid/TP40010810-CH5-SW65>
+ //
+ // In reality, that isn't actually the case for `NSMutableString`,
+ // the `description` of that just returns itself instead of copying.
+ //
+ // Luckily though, the `UTF8String` returned by mutable objects do not
+ // return a reference to internal data, and instead always allocate
+ // a new autoreleased object (see details in `nsstring_to_str`), so
+ // we do not have to worry about the string being mutated in e.g. a
+ // malicious `Write` implementation in `fmt::Formatter` while we hold
+ // the `&str`.
+
+ // We use a leaking autorelease pool since often the string will be
+ // UTF-8, and in that case the pool will be irrelevant. Also, it
+ // allows us to pass the formatter into the pool (since it may contain
+ // a pool internally that it assumes is current when writing).
+ autoreleasepool_leaking(|pool| {
+ // SAFETY:
+ // - The `description` selector is guaranteed to always return an
+ // instance of `NSString`.
+ // - We control the scope in which the string is alive, so we know
+ // it is not moved outside the current autorelease pool
+ // (`autoreleasepool_leaking` is greatly helping with this,
+ // though by itself does not fully ensure it).
+ let s = unsafe { nsstring_to_str(&description, pool) };
+ fmt::Display::fmt(s, f)
+ })
+ }
+}
+
+impl<P: ?Sized, T> AsRef<ProtocolObject<T>> for ProtocolObject<P>
+where
+ T: ?Sized + ImplementedBy<ProtocolObject<P>>,
+{
+ #[inline]
+ fn as_ref(&self) -> &ProtocolObject<T> {
+ ProtocolObject::from_ref(self)
+ }
+}
+
+// TODO: Maybe implement Borrow?
+
+impl<P: ?Sized + 'static> AsRef<AnyObject> for ProtocolObject<P> {
+ #[inline]
+ fn as_ref(&self) -> &AnyObject {
+ let ptr: NonNull<ProtocolObject<P>> = NonNull::from(self);
+ let ptr: NonNull<AnyObject> = ptr.cast();
+ // SAFETY: All protocol objects are Objective-C objects too.
+ unsafe { ptr.as_ref() }
+ }
+}
+
+#[cfg(test)]
+#[allow(clippy::missing_safety_doc)]
+#[allow(dead_code)]
+mod tests {
+ use alloc::format;
+ use core::ffi::CStr;
+
+ use static_assertions::{assert_impl_all, assert_not_impl_any};
+
+ use super::*;
+ use crate::runtime::{ClassBuilder, NSObject};
+ use crate::{define_class, extern_methods, extern_protocol, msg_send, ClassType};
+
+ extern_protocol!(
+ unsafe trait Foo {
+ #[unsafe(method(foo))]
+ fn foo_class();
+
+ #[unsafe(method(foo))]
+ fn foo_instance(&self);
+ }
+ );
+
+ extern_protocol!(
+ unsafe trait Bar: NSObjectProtocol {
+ #[unsafe(method(bar))]
+ fn bar_class();
+
+ #[unsafe(method(bar))]
+ fn bar_instance(&self);
+ }
+ );
+
+ extern_protocol!(
+ unsafe trait FooBar: Foo + Bar {
+ #[unsafe(method(foobar))]
+ fn foobar_class();
+
+ #[unsafe(method(foobar))]
+ fn foobar_instance(&self);
+ }
+ );
+
+ extern_protocol!(
+ unsafe trait FooFooBar: Foo + FooBar {
+ #[unsafe(method(foofoobar))]
+ fn foofoobar_class();
+
+ #[unsafe(method(foofoobar))]
+ fn foofoobar_instance(&self);
+ }
+ );
+
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[derive(Debug, PartialEq, Eq, Hash)]
+ struct DummyClass;
+
+ unsafe impl NSObjectProtocol for DummyClass {}
+ );
+
+ unsafe impl Foo for DummyClass {}
+ unsafe impl Bar for DummyClass {}
+ unsafe impl FooBar for DummyClass {}
+ // unsafe impl FooFooBar for DummyClass {}
+
+ impl DummyClass {
+ extern_methods!(
+ #[unsafe(method(new))]
+ fn new() -> Retained<Self>;
+ );
+ }
+
+ #[test]
+ fn impl_traits() {
+ assert_impl_all!(NSObject: NSObjectProtocol);
+ assert_impl_all!(ProtocolObject<dyn NSObjectProtocol>: NSObjectProtocol);
+ assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol>: Send, Sync);
+ assert_impl_all!(ProtocolObject<dyn NSObjectProtocol + Send>: NSObjectProtocol, Send);
+ assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol + Send>: Sync);
+ assert_impl_all!(ProtocolObject<dyn NSObjectProtocol + Sync>: NSObjectProtocol, Sync);
+ assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol + Sync>: Send);
+ assert_impl_all!(ProtocolObject<dyn NSObjectProtocol + Send + Sync>: NSObjectProtocol, Send, Sync);
+ assert_not_impl_any!(ProtocolObject<dyn Foo>: NSObjectProtocol);
+ assert_impl_all!(ProtocolObject<dyn Bar>: NSObjectProtocol);
+ assert_impl_all!(ProtocolObject<dyn FooBar>: NSObjectProtocol);
+ assert_impl_all!(ProtocolObject<dyn FooFooBar>: NSObjectProtocol);
+ assert_impl_all!(DummyClass: NSObjectProtocol);
+
+ assert_not_impl_any!(NSObject: Foo);
+ assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol>: Foo);
+ assert_impl_all!(ProtocolObject<dyn Foo>: Foo);
+ assert_not_impl_any!(ProtocolObject<dyn Bar>: Foo);
+ assert_impl_all!(ProtocolObject<dyn FooBar>: Foo);
+ assert_impl_all!(ProtocolObject<dyn FooFooBar>: Foo);
+ assert_impl_all!(DummyClass: Foo);
+
+ assert_not_impl_any!(NSObject: Bar);
+ assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol>: Bar);
+ assert_not_impl_any!(ProtocolObject<dyn Foo>: Bar);
+ assert_impl_all!(ProtocolObject<dyn Bar>: Bar);
+ assert_impl_all!(ProtocolObject<dyn FooBar>: Bar);
+ assert_impl_all!(ProtocolObject<dyn FooFooBar>: Bar);
+ assert_impl_all!(DummyClass: Bar);
+
+ assert_not_impl_any!(NSObject: FooBar);
+ assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol>: FooBar);
+ assert_not_impl_any!(ProtocolObject<dyn Foo>: FooBar);
+ assert_not_impl_any!(ProtocolObject<dyn Bar>: FooBar);
+ assert_impl_all!(ProtocolObject<dyn FooBar>: FooBar);
+ assert_impl_all!(ProtocolObject<dyn FooFooBar>: FooBar);
+ assert_impl_all!(DummyClass: FooBar);
+
+ assert_not_impl_any!(NSObject: FooFooBar);
+ assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol>: FooFooBar);
+ assert_not_impl_any!(ProtocolObject<dyn Foo>: FooFooBar);
+ assert_not_impl_any!(ProtocolObject<dyn Bar>: FooFooBar);
+ assert_not_impl_any!(ProtocolObject<dyn FooBar>: FooFooBar);
+ assert_impl_all!(ProtocolObject<dyn FooFooBar>: FooFooBar);
+ assert_not_impl_any!(DummyClass: FooFooBar);
+ }
+
+ #[test]
+ fn convertible() {
+ let obj = DummyClass::new();
+ let foobar: &ProtocolObject<dyn FooBar> = ProtocolObject::from_ref(&*obj);
+ let foobar: &ProtocolObject<dyn FooBar> = ProtocolObject::from_ref(foobar);
+
+ let _bar: &ProtocolObject<dyn Bar> = ProtocolObject::from_ref(foobar);
+ let bar: &ProtocolObject<dyn Bar> = ProtocolObject::from_ref(&*obj);
+ let bar: &ProtocolObject<dyn Bar> = ProtocolObject::from_ref(bar);
+
+ let _foo: &ProtocolObject<dyn Foo> = ProtocolObject::from_ref(foobar);
+ let foo: &ProtocolObject<dyn Foo> = ProtocolObject::from_ref(&*obj);
+ let _foo: &ProtocolObject<dyn Foo> = ProtocolObject::from_ref(foo);
+
+ let _nsobject: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(foobar);
+ let _nsobject: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(bar);
+ let nsobject: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(&*obj);
+ let _nsobject: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(nsobject);
+ let _: &ProtocolObject<dyn NSObjectProtocol + Send> = ProtocolObject::from_ref(&*obj);
+ let _: &ProtocolObject<dyn NSObjectProtocol + Sync> = ProtocolObject::from_ref(&*obj);
+ let _: &ProtocolObject<dyn NSObjectProtocol + Send + Sync> =
+ ProtocolObject::from_ref(&*obj);
+
+ let _foobar: Retained<ProtocolObject<dyn FooBar>> = ProtocolObject::from_retained(obj);
+ }
+
+ #[test]
+ fn convert_to_anyobj() {
+ let obj = NSObject::new();
+ let obj: Retained<ProtocolObject<dyn NSObjectProtocol>> =
+ ProtocolObject::from_retained(obj);
+ let _obj: &AnyObject = obj.as_ref();
+ }
+
+ #[test]
+ fn test_traits() {
+ use core::hash::Hasher;
+ use std::collections::hash_map::DefaultHasher;
+ use std::hash::Hash;
+
+ let obj = DummyClass::new();
+ let obj2 = DummyClass::new();
+
+ let foobar: &ProtocolObject<dyn FooBar> = ProtocolObject::from_ref(&*obj);
+ let foobar2: &ProtocolObject<dyn FooBar> = ProtocolObject::from_ref(&*obj2);
+
+ assert_eq!(
+ format!("{obj:?}"),
+ format!("DummyClass {{ super: {foobar:?}, ivars: () }}"),
+ );
+ assert_eq!(obj == obj2, foobar == foobar2);
+
+ let mut hashstate_a = DefaultHasher::new();
+ let mut hashstate_b = DefaultHasher::new();
+
+ obj.hash(&mut hashstate_a);
+ <_ as Hash>::hash(foobar, &mut hashstate_b);
+
+ assert_eq!(hashstate_a.finish(), hashstate_b.finish());
+ }
+
+ // We use `debug_assertions` here just because it's something that we know
+ // our CI already tests.
+ extern_protocol!(
+ #[cfg(debug_assertions)]
+ unsafe trait CfgTest {}
+ );
+
+ #[test]
+ #[cfg(debug_assertions)]
+ fn test_meta() {
+ if false {
+ let _protocol = <dyn CfgTest as crate::ProtocolType>::protocol();
+ }
+ }
+
+ #[test]
+ #[cfg_attr(
+ feature = "gnustep-1-7",
+ ignore = "depends on the platform's NSString unicode handling"
+ )]
+ fn debug_non_utf8_classname() {
+ // Some class with invalid UTF-8 character inside
+ let s = CStr::from_bytes_with_nul(b"My\xF0\x90\x80Class\0").unwrap();
+
+ let cls = ClassBuilder::new(s, NSObject::class()).unwrap().register();
+ let obj: Retained<NSObject> = unsafe { msg_send![cls, new] };
+
+ let expected = format!("<My\u{f8ff}êÄClass: {:p}>", &*obj);
+ assert_eq!(format!("{obj:?}"), expected);
+ }
+}
diff --git a/third_party/rust/objc2/src/runtime/retain_release_fast.rs b/third_party/rust/objc2/src/runtime/retain_release_fast.rs
@@ -0,0 +1,248 @@
+//! Optimized versions of `objc_retain` and `objc_release`.
+//!
+//! On macOS 13.0 / iOS 16.0 / tvOS 16.0 / watchOS 9.0, on ARM64, optimized
+//! versions of these two functions that use a different calling convention
+//! than the usual C calling convention, are available.
+//!
+//! Specifically, the expected input register is changed. The output register
+//! is unchanged.
+//!
+//! As an example, if the object is stored in the `x19` register and we need
+//! to release it, we usually end up emitting an extra `mov` to get the object
+//! into the `x0` register first, as expected by the C calling convention:
+//!
+//! ```asm
+//! mov x0, x19
+//! bl _objc_release
+//! ```
+//!
+//! With this optimization though, since the expected register is encoded in
+//! the name of the function instead, we can avoid the move altogether.
+//!
+//! ```asm
+//! bl _objc_release_x19
+//! ```
+//!
+//!
+//!
+//! Safety of our two uses of the `asm!` macro:
+//!
+//! 1. We use the register class `reg`, with the modifier `x`, which on
+//! Aarch64 is defined as `x[0-30]`, see [this][asm-reg-cls].
+//!
+//! The functions are only available in the variants `x[0-15]` and
+//! `x[19-28]` though, see [this][objc4-source], so if the register
+//! allocator ends up using `x16`, `x17`, `x18`, `x29` or `x30`, we will
+//! emit a call to e.g. `objc_retain_x29`, which will fail at link time.
+//!
+//! Of thesee five registers, `x18` is a reserved register on Apple, so the
+//! register allocator won't use it. `x29` is the frame pointer, so that
+//! will also never be used (at least not for storing a pointer to an
+//! object).
+//!
+//! Left are `x16`, `x17` and `x30`, which we prevent from being selected
+//! by specifying `out("x16") _`, `out("x17") _` and `out("x30") _`, which
+//! tricks the register allocator into believing that it cannot use those
+//! registers. This may be slightly worse than optimal, since it also
+//! clobbers the registers, but then again, so does `clobber_abi("C")`, so
+//! it probably won't matter.
+//!
+//! (NOTE: I'm not _entirely_ sure that the register allocator won't select
+//! one of the clobbered registers, but luckily, it is not a safety
+//! requirement either way, it will "just" lead to a link error).
+//!
+//! 2. We use the `clobber_abi("C")` since we're effectively calling a C
+//! C function.
+//!
+//! [asm-reg-cls]: https://doc.rust-lang.org/nightly/reference/inline-assembly.html#register-operands
+//! [objc4-source]: https://github.com/apple-oss-distributions/objc4/blob/objc4-866.9/runtime/objc-abi.h#L442-L498
+use crate::runtime::AnyObject;
+
+/// A potentially faster version of `ffi::objc_retain`.
+///
+///
+/// # Safety
+///
+/// Same as `ffi::objc_retain`.
+#[inline]
+pub(crate) unsafe fn objc_retain_fast(obj: *mut AnyObject) -> *mut AnyObject {
+ #[cfg(all(feature = "unstable-apple-new", target_arch = "aarch64"))]
+ // SAFETY: See the file header.
+ //
+ // As per the ARM64 calling convention, the return value is put in `x0`.
+ //
+ // That the function itself is safe to call is upheld by the caller.
+ //
+ // TODO: Unwinding.
+ unsafe {
+ let result;
+ core::arch::asm!(
+ "bl _objc_retain_{obj:x}",
+ obj = in(reg) obj,
+ lateout("x0") result,
+ out("x16") _,
+ out("x17") _,
+ out("x30") _,
+ clobber_abi("C"),
+ );
+ result
+ }
+
+ #[cfg(not(all(feature = "unstable-apple-new", target_arch = "aarch64")))]
+ // SAFETY: Upheld by caller.
+ unsafe {
+ crate::ffi::objc_retain(obj)
+ }
+}
+
+/// A potentially faster version of `ffi::objc_release`.
+///
+///
+/// # Safety
+///
+/// Same as `ffi::objc_release`.
+#[inline]
+pub(crate) unsafe fn objc_release_fast(obj: *mut AnyObject) {
+ #[cfg(all(feature = "unstable-apple-new", target_arch = "aarch64"))]
+ // SAFETY: See the file header.
+ //
+ // That the function itself is safe to call is upheld by the caller.
+ //
+ // TODO: Unwinding.
+ unsafe {
+ core::arch::asm!(
+ "bl _objc_release_{obj:x}",
+ obj = in(reg) obj,
+ out("x16") _,
+ out("x17") _,
+ out("x30") _,
+ clobber_abi("C"),
+ )
+ }
+
+ #[cfg(not(all(feature = "unstable-apple-new", target_arch = "aarch64")))]
+ // SAFETY: Upheld by caller.
+ unsafe {
+ crate::ffi::objc_release(obj)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::rc::Retained;
+ use crate::runtime::NSObject;
+
+ #[test]
+ fn retain_release_30_times() {
+ #[inline(never)]
+ fn new_obj() -> Retained<NSObject> {
+ NSObject::new()
+ }
+
+ let obj00 = new_obj();
+ let obj01 = new_obj();
+ let obj02 = new_obj();
+ let obj03 = new_obj();
+ let obj04 = new_obj();
+ let obj05 = new_obj();
+ let obj06 = new_obj();
+ let obj07 = new_obj();
+ let obj08 = new_obj();
+ let obj09 = new_obj();
+ let obj10 = new_obj();
+ let obj11 = new_obj();
+ let obj12 = new_obj();
+ let obj13 = new_obj();
+ let obj14 = new_obj();
+ let obj15 = new_obj();
+ let obj16 = new_obj();
+ let obj17 = new_obj();
+ let obj18 = new_obj();
+ let obj19 = new_obj();
+ let obj20 = new_obj();
+ let obj21 = new_obj();
+ let obj22 = new_obj();
+ let obj23 = new_obj();
+ let obj24 = new_obj();
+ let obj25 = new_obj();
+ let obj26 = new_obj();
+ let obj27 = new_obj();
+ let obj28 = new_obj();
+ let obj29 = new_obj();
+ let obj30 = new_obj();
+
+ let _obj00 = obj00.clone();
+ let _obj01 = obj01.clone();
+ let _obj02 = obj02.clone();
+ let _obj03 = obj03.clone();
+ let _obj04 = obj04.clone();
+ let _obj05 = obj05.clone();
+ let _obj06 = obj06.clone();
+ let _obj07 = obj07.clone();
+ let _obj08 = obj08.clone();
+ let _obj09 = obj09.clone();
+ let _obj10 = obj10.clone();
+ let _obj11 = obj11.clone();
+ let _obj12 = obj12.clone();
+ let _obj13 = obj13.clone();
+ let _obj14 = obj14.clone();
+ let _obj15 = obj15.clone();
+ let _obj16 = obj16.clone();
+ let _obj17 = obj17.clone();
+ let _obj18 = obj18.clone();
+ let _obj19 = obj19.clone();
+ let _obj20 = obj20.clone();
+ let _obj21 = obj21.clone();
+ let _obj22 = obj22.clone();
+ let _obj23 = obj23.clone();
+ let _obj24 = obj24.clone();
+ let _obj25 = obj25.clone();
+ let _obj26 = obj26.clone();
+ let _obj27 = obj27.clone();
+ let _obj28 = obj28.clone();
+ let _obj29 = obj29.clone();
+ let _obj30 = obj30.clone();
+ }
+
+ #[test]
+ #[cfg(target_arch = "aarch64")]
+ fn move_to_register_x16() {
+ let obj = NSObject::new();
+ let mut ptr = Retained::into_raw(obj);
+ unsafe { core::arch::asm!("nop", inout("x16") ptr) };
+ let _ = unsafe { Retained::from_raw(ptr) };
+
+ let obj = NSObject::new();
+ let mut ptr = Retained::autorelease_ptr(obj);
+ unsafe { core::arch::asm!("nop", inout("x16") ptr) };
+ let _ = unsafe { Retained::retain(ptr) };
+ }
+
+ #[test]
+ #[cfg(target_arch = "aarch64")]
+ fn move_to_register_x17() {
+ let obj = NSObject::new();
+ let mut ptr = Retained::into_raw(obj);
+ unsafe { core::arch::asm!("nop", inout("x17") ptr) };
+ let _ = unsafe { Retained::from_raw(ptr) };
+
+ let obj = NSObject::new();
+ let mut ptr = Retained::autorelease_ptr(obj);
+ unsafe { core::arch::asm!("nop", inout("x17") ptr) };
+ let _ = unsafe { Retained::retain(ptr) };
+ }
+
+ #[test]
+ #[cfg(target_arch = "aarch64")]
+ fn move_to_register_x30() {
+ let obj = NSObject::new();
+ let mut ptr = Retained::into_raw(obj);
+ unsafe { core::arch::asm!("nop", inout("x30") ptr) };
+ let _ = unsafe { Retained::from_raw(ptr) };
+
+ let obj = NSObject::new();
+ let mut ptr = Retained::autorelease_ptr(obj);
+ unsafe { core::arch::asm!("nop", inout("x30") ptr) };
+ let _ = unsafe { Retained::retain(ptr) };
+ }
+}
diff --git a/third_party/rust/objc2/src/test_utils.rs b/third_party/rust/objc2/src/test_utils.rs
@@ -0,0 +1,261 @@
+use alloc::ffi::CString;
+use core::ffi::c_char;
+use core::ops::Deref;
+use std::sync::Once;
+
+use crate::encode::{Encode, Encoding, RefEncode};
+use crate::rc::Retained;
+use crate::runtime::{AnyClass, AnyObject, AnyProtocol, ClassBuilder, ProtocolBuilder, Sel};
+use crate::{ffi, msg_send, sel, Message};
+
+#[derive(Debug)]
+#[repr(C)]
+pub(crate) struct CustomObject(AnyObject);
+
+unsafe impl RefEncode for CustomObject {
+ const ENCODING_REF: Encoding = Encoding::Object;
+}
+
+unsafe impl Message for CustomObject {}
+
+impl Deref for CustomObject {
+ type Target = AnyObject;
+
+ fn deref(&self) -> &AnyObject {
+ &self.0
+ }
+}
+
+#[derive(Debug, Eq, PartialEq)]
+#[repr(C)]
+pub(crate) struct CustomStruct {
+ pub(crate) a: u64,
+ pub(crate) b: u64,
+ pub(crate) c: u64,
+ pub(crate) d: u64,
+}
+
+unsafe impl Encode for CustomStruct {
+ const ENCODING: Encoding = Encoding::Struct(
+ "CustomStruct",
+ &[u64::ENCODING, u64::ENCODING, u64::ENCODING, u64::ENCODING],
+ );
+}
+
+// TODO: Remove once c"" strings are in MSRV
+fn c(s: &str) -> CString {
+ CString::new(s).unwrap()
+}
+
+pub(crate) fn custom_class() -> &'static AnyClass {
+ static REGISTER_CUSTOM_CLASS: Once = Once::new();
+
+ REGISTER_CUSTOM_CLASS.call_once(|| {
+ // The runtime will call this method, so it has to be implemented
+ extern "C-unwind" fn custom_obj_class_initialize(_this: &AnyClass, _cmd: Sel) {}
+
+ let mut builder = ClassBuilder::root(
+ &c("CustomObject"),
+ custom_obj_class_initialize as extern "C-unwind" fn(_, _),
+ )
+ .unwrap();
+ let proto = custom_protocol();
+
+ builder.add_protocol(proto);
+ builder.add_ivar::<u32>(&c("_foo"));
+
+ unsafe extern "C-unwind" fn custom_obj_release(this: *mut AnyObject, _cmd: Sel) {
+ unsafe {
+ #[allow(deprecated)]
+ ffi::object_dispose(this);
+ }
+ }
+
+ extern "C-unwind" fn custom_obj_set_foo(this: &AnyObject, _cmd: Sel, foo: u32) {
+ let ivar = this.class().instance_variable(&c("_foo")).unwrap();
+ unsafe { *ivar.load_ptr::<u32>(this) = foo }
+ }
+
+ extern "C-unwind" fn custom_obj_get_foo(this: &AnyObject, _cmd: Sel) -> u32 {
+ let ivar = this.class().instance_variable(&c("_foo")).unwrap();
+ unsafe { *ivar.load::<u32>(this) }
+ }
+
+ extern "C-unwind" fn custom_obj_get_foo_reference(this: &AnyObject, _cmd: Sel) -> &u32 {
+ let ivar = this.class().instance_variable(&c("_foo")).unwrap();
+ unsafe { ivar.load::<u32>(this) }
+ }
+
+ extern "C-unwind" fn custom_obj_get_struct(_this: &AnyObject, _cmd: Sel) -> CustomStruct {
+ CustomStruct {
+ a: 1,
+ b: 2,
+ c: 3,
+ d: 4,
+ }
+ }
+
+ extern "C-unwind" fn custom_obj_class_method(_this: &AnyClass, _cmd: Sel) -> u32 {
+ 7
+ }
+
+ extern "C-unwind" fn get_nsinteger(_this: &AnyObject, _cmd: Sel) -> ffi::NSInteger {
+ 5
+ }
+
+ extern "C-unwind" fn custom_obj_set_bar(this: &AnyObject, _cmd: Sel, bar: u32) {
+ let ivar = this.class().instance_variable(&c("_bar")).unwrap();
+ unsafe { *ivar.load_ptr::<u32>(this) = bar }
+ }
+
+ extern "C-unwind" fn custom_obj_add_number_to_number(
+ _this: &AnyClass,
+ _cmd: Sel,
+ fst: i32,
+ snd: i32,
+ ) -> i32 {
+ fst + snd
+ }
+
+ extern "C-unwind" fn custom_obj_multiple_colon(
+ _obj: &AnyObject,
+ _cmd: Sel,
+ arg1: i32,
+ arg2: i32,
+ arg3: i32,
+ arg4: i32,
+ ) -> i32 {
+ arg1 * arg2 * arg3 * arg4
+ }
+
+ extern "C-unwind" fn custom_obj_multiple_colon_class(
+ _cls: &AnyClass,
+ _cmd: Sel,
+ arg1: i32,
+ arg2: i32,
+ arg3: i32,
+ arg4: i32,
+ ) -> i32 {
+ arg1 + arg2 + arg3 + arg4
+ }
+
+ unsafe {
+ // On GNUStep 2.0, it is required to have `dealloc` methods for some reason
+ if cfg!(all(feature = "gnustep-2-0", not(feature = "gnustep-2-1"))) {
+ unsafe extern "C-unwind" fn forward_to_dealloc(this: *mut AnyObject, _cmd: Sel) {
+ unsafe { msg_send![this, dealloc] }
+ }
+
+ let release: unsafe extern "C-unwind" fn(_, _) = forward_to_dealloc;
+ builder.add_method(sel!(release), release);
+
+ let release: unsafe extern "C-unwind" fn(_, _) = custom_obj_release;
+ builder.add_method(sel!(dealloc), release);
+ } else {
+ let release: unsafe extern "C-unwind" fn(_, _) = custom_obj_release;
+ builder.add_method(sel!(release), release);
+ }
+
+ let set_foo: extern "C-unwind" fn(_, _, _) = custom_obj_set_foo;
+ builder.add_method(sel!(setFoo:), set_foo);
+ let get_foo: extern "C-unwind" fn(_, _) -> _ = custom_obj_get_foo;
+ builder.add_method(sel!(foo), get_foo);
+ let get_foo_reference: extern "C-unwind" fn(_, _) -> _ = custom_obj_get_foo_reference;
+ builder.add_method(sel!(fooReference), get_foo_reference);
+ let get_struct: extern "C-unwind" fn(_, _) -> CustomStruct = custom_obj_get_struct;
+ builder.add_method(sel!(customStruct), get_struct);
+ let class_method: extern "C-unwind" fn(_, _) -> _ = custom_obj_class_method;
+ builder.add_class_method(sel!(classFoo), class_method);
+
+ let get_nsinteger: extern "C-unwind" fn(_, _) -> _ = get_nsinteger;
+ builder.add_method(sel!(getNSInteger), get_nsinteger);
+
+ let protocol_instance_method: extern "C-unwind" fn(_, _, _) = custom_obj_set_bar;
+ builder.add_method(sel!(setBar:), protocol_instance_method);
+ let protocol_class_method: extern "C-unwind" fn(_, _, _, _) -> _ =
+ custom_obj_add_number_to_number;
+ builder.add_class_method(sel!(addNumber:toNumber:), protocol_class_method);
+
+ let f: extern "C-unwind" fn(_, _, _, _, _, _) -> _ = custom_obj_multiple_colon;
+ builder.add_method(sel!(test::test::), f);
+ let f: extern "C-unwind" fn(_, _, _, _, _, _) -> _ = custom_obj_multiple_colon_class;
+ builder.add_class_method(sel!(test::test::), f);
+ }
+
+ builder.register();
+ });
+
+ // Can't use `class!` here since `CustomObject` is dynamically created.
+ AnyClass::get(&c("CustomObject")).unwrap()
+}
+
+pub(crate) fn custom_protocol() -> &'static AnyProtocol {
+ static REGISTER_CUSTOM_PROTOCOL: Once = Once::new();
+
+ REGISTER_CUSTOM_PROTOCOL.call_once(|| {
+ let mut builder = ProtocolBuilder::new(&c("CustomProtocol")).unwrap();
+
+ builder.add_method_description::<(i32,), ()>(sel!(setBar:), true);
+ builder.add_method_description::<(), *const c_char>(sel!(getName), false);
+ builder.add_class_method_description::<(i32, i32), i32>(sel!(addNumber:toNumber:), true);
+
+ builder.register();
+ });
+
+ AnyProtocol::get(&c("CustomProtocol")).unwrap()
+}
+
+pub(crate) fn custom_subprotocol() -> &'static AnyProtocol {
+ static REGISTER_CUSTOM_SUBPROTOCOL: Once = Once::new();
+
+ REGISTER_CUSTOM_SUBPROTOCOL.call_once(|| {
+ let super_proto = custom_protocol();
+ let mut builder = ProtocolBuilder::new(&c("CustomSubProtocol")).unwrap();
+
+ builder.add_protocol(super_proto);
+ builder.add_method_description::<(u32,), u32>(sel!(calculateFoo:), true);
+
+ builder.register();
+ });
+
+ AnyProtocol::get(&c("CustomSubProtocol")).unwrap()
+}
+
+pub(crate) fn custom_object() -> Retained<CustomObject> {
+ let ptr: *const AnyClass = custom_class();
+ unsafe { Retained::from_raw(ffi::class_createInstance(ptr, 0).cast()) }.unwrap()
+}
+
+pub(crate) fn custom_subclass() -> &'static AnyClass {
+ static REGISTER_CUSTOM_SUBCLASS: Once = Once::new();
+
+ REGISTER_CUSTOM_SUBCLASS.call_once(|| {
+ let superclass = custom_class();
+ let mut builder = ClassBuilder::new(&c("CustomSubclassObject"), superclass).unwrap();
+
+ extern "C-unwind" fn custom_subclass_get_foo(this: &AnyObject, _cmd: Sel) -> u32 {
+ let foo: u32 = unsafe { msg_send![super(this, custom_class()), foo] };
+ foo + 2
+ }
+
+ extern "C-unwind" fn custom_subclass_class_method(_cls: &AnyClass, _cmd: Sel) -> u32 {
+ 9
+ }
+
+ unsafe {
+ let get_foo: extern "C-unwind" fn(_, _) -> _ = custom_subclass_get_foo;
+ builder.add_method(sel!(foo), get_foo);
+ let class_method: extern "C-unwind" fn(_, _) -> _ = custom_subclass_class_method;
+ builder.add_class_method(sel!(classFoo), class_method);
+ }
+
+ builder.register();
+ });
+
+ AnyClass::get(&c("CustomSubclassObject")).unwrap()
+}
+
+pub(crate) fn custom_subclass_object() -> Retained<CustomObject> {
+ let ptr: *const AnyClass = custom_subclass();
+ unsafe { Retained::from_raw(ffi::class_createInstance(ptr, 0).cast()) }.unwrap()
+}
diff --git a/third_party/rust/objc2/src/top_level_traits.rs b/third_party/rust/objc2/src/top_level_traits.rs
@@ -0,0 +1,595 @@
+use alloc::ffi::CString;
+use core::ptr::NonNull;
+
+use crate::__macro_helpers::defined_ivars::get_initialized_ivar_ptr;
+use crate::encode::RefEncode;
+use crate::rc::{Allocated, Retained};
+use crate::runtime::{AnyClass, AnyProtocol, ProtocolObject};
+use crate::MainThreadMarker;
+
+/// Types that can be sent Objective-C messages.
+///
+/// Implementing this provides [`MessageReceiver`] implementations for common
+/// pointer types and references to the type, which allows using them as the
+/// receiver (first argument) in the [`msg_send!`][`crate::msg_send`] macro.
+///
+/// This trait also allows the object to be used in [`Retained`].
+///
+/// This is a subtrait of [`RefEncode`], meaning the type must also implement
+/// that, almost always with [`RefEncode::ENCODING_REF`] being
+/// [`Encoding::Object`].
+///
+/// This can be implemented for unsized (`!Sized`) types, but the intention is
+/// not to support dynamically sized types like slices, only `extern type`s
+/// (which is currently unstable).
+///
+/// [`MessageReceiver`]: crate::runtime::MessageReceiver
+/// [`Encoding::Object`]: crate::Encoding::Object
+///
+///
+/// # `Drop` interaction
+///
+/// If the inner type implements [`Drop`], that implementation will very
+/// likely not be called, since there is no way to ensure that the Objective-C
+/// runtime will do so. If you need to run some code when the object is
+/// destroyed, implement the `dealloc` method instead.
+///
+/// The [`define_class!`] macro does this for you, but the [`extern_class!`]
+/// macro fundamentally cannot.
+///
+/// [`define_class!`]: crate::define_class
+/// [`extern_class!`]: crate::extern_class
+///
+///
+/// # Safety
+///
+/// The type must represent an Objective-C object, meaning it:
+/// - Must be valid to reinterpret as [`AnyObject`].
+/// - Must be able to be the receiver of an Objective-C message sent with
+/// [`objc_msgSend`] or similar.
+/// - Must respond to the standard memory management `retain`, `release` and
+/// `autorelease` messages.
+/// - Must support weak references. (In the future we should probably make a
+/// new trait for this, for example `NSTextView` only supports weak
+/// references on macOS 10.12 or above).
+///
+/// [`AnyObject`]: crate::runtime::AnyObject
+/// [`objc_msgSend`]: https://developer.apple.com/documentation/objectivec/1456712-objc_msgsend
+///
+///
+/// # Example
+///
+/// ```
+/// use objc2::runtime::NSObject;
+/// use objc2::{Encoding, Message, RefEncode};
+///
+/// #[repr(C)]
+/// struct MyObject {
+/// // This has the exact same layout as `NSObject`
+/// inner: NSObject
+/// }
+///
+/// unsafe impl RefEncode for MyObject {
+/// const ENCODING_REF: Encoding = Encoding::Object;
+/// }
+///
+/// unsafe impl Message for MyObject {}
+///
+/// // `*mut MyObject` and other pointer/reference types to the object can
+/// // now be used in `msg_send!`
+/// //
+/// // And `Retained<MyObject>` can now be constructed.
+/// ```
+///
+/// Implement the trait manually for a class with a lifetime parameter.
+///
+/// ```
+#[doc = include_str!("../examples/class_with_lifetime.rs")]
+/// ```
+pub unsafe trait Message: RefEncode {
+ /// Increment the reference count of the receiver.
+ ///
+ /// This extends the duration in which the receiver is alive by detaching
+ /// it from the lifetime information carried by the reference.
+ ///
+ /// This is similar to using [`Clone` on `Retained<Self>`][clone-id], with
+ /// the addition that it can be used on a plain reference.
+ ///
+ /// If your type may have come from a mutable type like `NSMutableString`,
+ /// you should consider using `NSCopying::copy` instead to avoid carrying
+ /// around a mutable string when you did not intend to.
+ ///
+ /// [clone-id]: crate::rc::Retained#impl-Clone-for-Retained<T>
+ #[inline]
+ #[doc(alias = "objc_retain")]
+ fn retain(&self) -> Retained<Self>
+ where
+ Self: Sized, // Temporary
+ {
+ let ptr: *const Self = self;
+ let ptr: *mut Self = ptr as _;
+ // SAFETY:
+ // - The pointer is valid since it came from `&self`.
+ // - The lifetime of the pointer itself is extended, but any lifetime
+ // that the object may carry is still kept within the type itself.
+ let obj = unsafe { Retained::retain(ptr) };
+ // SAFETY: The pointer came from `&self`, which is always non-null,
+ // and objc_retain always returns the same value.
+ unsafe { obj.unwrap_unchecked() }
+ }
+}
+
+/// Marks types that represent specific classes.
+///
+/// Sometimes it is enough to generically know that a type is messageable,
+/// e.g. [`Retained`] works with any type that implements the [`Message`]
+/// trait. But often, you have an object that you know represents a specific
+/// Objective-C class - this trait allows you to communicate that, as well as
+/// a few properties of the class to the rest of the type-system.
+///
+/// This is implemented for your type by the
+/// [`define_class!`][crate::define_class] and
+/// [`extern_class!`][crate::extern_class] macros.
+///
+///
+/// # Safety
+///
+/// This is meant to be a sealed trait, and should not be implemented outside
+/// of the aforementioned macros. See those for safety preconditions.
+///
+///
+/// # Examples
+///
+/// Use the trait to access the [`AnyClass`] of an object.
+///
+/// ```
+/// use objc2::{ClassType, msg_send};
+/// use objc2::rc::Retained;
+/// # use objc2::runtime::{NSObject as MyObject};
+///
+/// // Get the class of the object.
+/// let cls = <MyObject as ClassType>::class();
+/// // Or, since the trait is in scope.
+/// let cls = MyObject::class();
+///
+/// // We can now access properties of the class.
+/// assert_eq!(cls.name().to_str().unwrap(), MyObject::NAME);
+///
+/// // And we can send messages to the class.
+/// //
+/// // SAFETY:
+/// // - The class is `MyObject`, which can safely be initialized with `new`.
+/// // - The return type is correctly specified.
+/// let obj: Retained<MyObject> = unsafe { msg_send![cls, new] };
+/// ```
+///
+/// Use the trait to allocate a new instance of an object.
+///
+/// ```
+/// use objc2::{msg_send, AnyThread};
+/// use objc2::rc::Retained;
+/// # use objc2::runtime::{NSObject as MyObject};
+///
+/// let obj = MyObject::alloc();
+///
+/// // Now we can call initializers on this newly allocated object.
+/// //
+/// // SAFETY: `MyObject` can safely be initialized with `init`.
+/// let obj: Retained<MyObject> = unsafe { msg_send![obj, init] };
+/// ```
+///
+/// Use the [`extern_class!`][crate::extern_class] macro to implement this
+/// trait for a type.
+///
+/// ```
+/// use objc2::runtime::NSObject;
+/// use objc2::{extern_class, ClassType, AnyThread};
+///
+/// extern_class!(
+/// // SAFETY: The superclass is correctly specified, and the class can be
+/// // safely used from any thread.
+/// #[unsafe(super(NSObject))]
+/// # // For testing purposes
+/// # #[name = "NSObject"]
+/// struct MyClass;
+/// );
+///
+/// let cls = MyClass::class();
+/// let obj = MyClass::alloc();
+/// ```
+//
+// Actual safety preconditions:
+//
+// 1. The type must represent a specific class.
+// 2. [`Self::Super`] must be a superclass of the class (or something that
+// represents any object, like [`AnyObject`][crate::runtime::AnyObject]).
+// 3. [`Self::ThreadKind`] must be correct. It is safe to default to the
+// super class' thread kind, `<Self::Super as ClassType>::ThreadKind`.
+// 4. [`Self::NAME`] must be the name of the class that this type represents.
+// 5. The class returned by [`Self::class`] must be the class that this type
+// represents.
+pub unsafe trait ClassType: Message {
+ /// The superclass of this class.
+ ///
+ /// If you have implemented [`Deref`] for your type, it is highly
+ /// recommended that this is equal to [`Deref::Target`].
+ ///
+ /// This may be [`AnyObject`] if the class is a root class.
+ ///
+ /// [`Deref`]: std::ops::Deref
+ /// [`Deref::Target`]: std::ops::Deref::Target
+ /// [`AnyObject`]: crate::runtime::AnyObject
+ type Super: Message;
+
+ /// Whether the type can be used from any thread, or from only the main
+ /// thread.
+ ///
+ /// One of [`dyn AnyThread`] or [`dyn MainThreadOnly`].
+ ///
+ /// Setting this makes `ClassType` provide an implementation of either
+ /// [`AnyThread`] or [`MainThreadOnly`].
+ ///
+ /// [`dyn AnyThread`]: AnyThread
+ /// [`dyn MainThreadOnly`]: MainThreadOnly
+ type ThreadKind: ?Sized + ThreadKind;
+
+ /// The name of the Objective-C class that this type represents.
+ ///
+ /// `T::NAME` is the `const` version of `T::class().name()`.
+ ///
+ /// This must not contain any NUL bytes.
+ //
+ // TODO: Convert this to CStr next time we do big changes to ClassType.
+ const NAME: &'static str;
+
+ /// Get a reference to the Objective-C class that this type represents.
+ ///
+ /// May register the class with the runtime if it wasn't already.
+ ///
+ ///
+ /// # Panics
+ ///
+ /// This may panic if something went wrong with getting or creating the
+ /// class, e.g. if the program is not properly linked to the framework
+ /// that defines the class.
+ fn class() -> &'static AnyClass;
+
+ /// Get an immutable reference to the superclass.
+ // Note: It'd be safe to provide a default impl using transmute here if
+ // we wanted to!
+ fn as_super(&self) -> &Self::Super;
+
+ #[doc(hidden)]
+ const __INNER: ();
+
+ /// Inner type to use when subclassing with `define_class!`.
+ ///
+ /// This is used by NSObject to control which auto traits are set for
+ /// defined subclasses. Set to `= Self` in all other cases.
+ #[doc(hidden)]
+ type __SubclassingType: ?Sized;
+}
+
+/// Marks class types whose implementation is defined in Rust.
+///
+/// This is used in [`define_class!`], and allows access to the instance
+/// variables that a given type declares, see that macro for details.
+///
+/// [`define_class!`]: crate::define_class
+//
+// Note: We mark this trait as not `unsafe` for better documentation, since
+// implementing it inside `define_class!` is not `unsafe`.
+//
+// Safety is ensured by `__UNSAFE_OFFSETS_CORRECT`.
+pub trait DefinedClass: ClassType {
+ /// A type representing the instance variables that this class carries.
+ type Ivars: Sized;
+
+ // TODO: Add `ivars_ptr(this: NonNull<Self>) -> NonNull<Self::Ivars>`?
+
+ /// Get a reference to the instance variable data that this object
+ /// carries.
+ #[inline]
+ #[track_caller]
+ fn ivars(&self) -> &Self::Ivars
+ where
+ Self: Sized, // Required because of MSRV
+ {
+ let ptr: NonNull<Self> = NonNull::from(self);
+ // SAFETY: The pointer is valid and initialized.
+ let ivars = unsafe { get_initialized_ivar_ptr(ptr) };
+ // SAFETY: The lifetime of the instance variable is tied to the object.
+ unsafe { ivars.as_ref() }
+ }
+
+ #[doc(hidden)]
+ fn __ivars_offset() -> isize;
+
+ #[doc(hidden)]
+ fn __drop_flag_offset() -> isize;
+
+ /// # Safety
+ ///
+ /// The ivar offset and drop flag offsets must be implemented correctly.
+ #[doc(hidden)]
+ const __UNSAFE_OFFSETS_CORRECT: ();
+}
+
+/// Marks types that represent specific protocols.
+///
+/// This is the protocol equivalent of [`ClassType`].
+///
+/// This is implemented automatically by the [`extern_protocol!`] macro for
+/// `dyn T`, where `T` is the protocol.
+///
+/// [`ClassType`]: crate::ClassType
+/// [`extern_protocol!`]: crate::extern_protocol
+///
+///
+/// # Safety
+///
+/// This is meant to be a sealed trait, and should not be implemented outside
+/// of the [`extern_protocol!`] macro.
+///
+///
+/// # Examples
+///
+/// Use the trait to access the [`AnyProtocol`] of different objects.
+///
+/// ```
+/// use objc2::ProtocolType;
+/// use objc2::runtime::NSObjectProtocol;
+/// // Get a protocol object representing the `NSObject` protocol
+/// let protocol = <dyn NSObjectProtocol>::protocol().expect("NSObject to have a protocol");
+/// assert_eq!(<dyn NSObjectProtocol>::NAME, protocol.name().to_str().unwrap());
+/// # // Ensure Foundation links on GNUStep
+/// # let _cls = objc2::class!(NSObject);
+/// ```
+///
+/// Use the [`extern_protocol!`] macro to implement and use this trait.
+///
+/// ```no_run
+/// use objc2::{extern_protocol, ProtocolType};
+///
+/// extern_protocol!(
+/// unsafe trait MyProtocol {}
+/// );
+///
+/// let protocol = <dyn MyProtocol>::protocol();
+/// ```
+pub unsafe trait ProtocolType {
+ /// The name of the Objective-C protocol that this type represents.
+ ///
+ /// This must not contain any NUL bytes.
+ //
+ // TODO: Convert this to CStr next time we do big changes to ProtocolType.
+ const NAME: &'static str;
+
+ /// Get a reference to the Objective-C protocol object that this type
+ /// represents.
+ ///
+ /// May register the protocol with the runtime if it wasn't already.
+ ///
+ /// Note that some protocols [are not registered with the runtime][p-obj],
+ /// depending on various factors. In those cases, this function may return
+ /// `None`.
+ ///
+ /// [p-obj]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProtocols.html#//apple_ref/doc/uid/TP30001163-CH15-TPXREF149
+ ///
+ ///
+ /// # Panics
+ ///
+ /// This may panic if something went wrong with getting or creating the
+ /// protocol, e.g. if the program is not properly linked to the framework
+ /// that defines the protocol.
+ fn protocol() -> Option<&'static AnyProtocol> {
+ get_protocol(Self::NAME)
+ }
+
+ #[doc(hidden)]
+ const __INNER: ();
+}
+
+// Outlined to reduce code size
+fn get_protocol(name: &str) -> Option<&'static AnyProtocol> {
+ let name = CString::new(name).expect("protocol name must be UTF-8");
+ AnyProtocol::get(&name)
+}
+
+// Split into separate traits for better diagnostics
+mod private {
+ pub trait SealedAnyThread {}
+ pub trait SealedMainThreadOnly {}
+ pub trait SealedThreadKind {}
+}
+
+/// Marker trait for classes (and protocols) that are usable from any thread,
+/// i.e. the opposite of [`MainThreadOnly`].
+///
+/// This is mostly an implementation detail to expose the [`alloc`] method
+/// with different signatures depending on whether a class is main thread only
+/// or not. You can safely assume that things are safe to use from any thread,
+/// _unless_ they implement [`MainThreadOnly`], not only if they implement
+/// this trait.
+///
+///
+/// # Safety
+///
+/// This is a sealed trait, and should not need to be implemented; it is
+/// implemented automatically when you implement [`ClassType`].
+//
+// NOTE: Ideally this would be an auto trait that had a negative impl for
+// `MainThreadOnly`, something like:
+//
+// pub unsafe auto trait AnyThread {}
+// pub unsafe trait MainThreadOnly {}
+// impl<T: ?Sized + MainThreadOnly> !AnyThread for T {}
+//
+// This isn't possible in current Rust though, so we'll have to hack it.
+pub unsafe trait AnyThread: private::SealedAnyThread {
+ /// Allocate a new instance of the class.
+ ///
+ /// The return value can be used directly inside [`msg_send!`] to
+ /// initialize the object.
+ ///
+ /// [`msg_send!`]: crate::msg_send
+ #[inline]
+ fn alloc() -> Allocated<Self>
+ where
+ Self: Sized + ClassType,
+ {
+ // SAFETY:
+ // - It is always safe to (attempt to) allocate an object.
+ // - The object is of the correct type, since we've used the class
+ // from `Self::class`.
+ // - The object is safe to `dealloc` on the current thread (due to the
+ // `AnyThread` bound which guarantees it is not `MainThreadOnly`).
+ //
+ // While Xcode's Main Thread Checker doesn't report `alloc` and
+ // `dealloc` as unsafe from other threads, things like `NSView` and
+ // `NSWindow` still do a non-trivial amount of stuff on `dealloc`,
+ // even if the object is freshly `alloc`'d - which is why we disallow
+ // this.
+ //
+ // This also has the nice property that `Allocated<T>` is guaranteed
+ // to be allowed to `init` on the current thread.
+ //
+ // See also `MainThreadMarker::alloc`.
+ unsafe { Allocated::alloc(Self::class()) }
+ }
+}
+
+// The impl here is a bit bad for diagnostics, but required to prevent users
+// implementing the trait themselves.
+impl<'a, T: ?Sized + ClassType<ThreadKind = dyn AnyThread + 'a>> private::SealedAnyThread for T {}
+unsafe impl<'a, T: ?Sized + ClassType<ThreadKind = dyn AnyThread + 'a>> AnyThread for T {}
+
+impl<P: ?Sized> private::SealedAnyThread for ProtocolObject<P> {}
+unsafe impl<P: ?Sized + AnyThread> AnyThread for ProtocolObject<P> {}
+
+/// Marker trait for classes and protocols that are only safe to use on the
+/// main thread.
+///
+/// This is commonly used in GUI code like `AppKit` and `UIKit`, e.g.
+/// `UIWindow` is only usable from the application's main thread because it
+/// accesses global statics like the `UIApplication`.
+///
+/// See [`MainThreadMarker`] for a few more details on this.
+///
+///
+/// # Safety
+///
+/// It is unsound to implement [`Send`] or [`Sync`] together with this.
+///
+/// This is a sealed trait, and should not need to be implemented; it is
+/// implemented automatically when you implement [`ClassType`].
+#[doc(alias = "@MainActor")]
+pub unsafe trait MainThreadOnly: private::SealedMainThreadOnly {
+ /// Get a [`MainThreadMarker`] from the main-thread-only object.
+ ///
+ /// This function exists purely in the type-system, and will succeed at
+ /// runtime (with a safety check when debug assertions are enabled).
+ #[inline]
+ #[cfg_attr(debug_assertions, track_caller)]
+ fn mtm(&self) -> MainThreadMarker {
+ #[cfg(debug_assertions)]
+ assert!(
+ MainThreadMarker::new().is_some(),
+ "the main-thread-only object that we tried to fetch a MainThreadMarker from was somehow not on the main thread",
+ );
+
+ // SAFETY: Objects which are `MainThreadOnly` are guaranteed
+ // `!Send + !Sync` and are only constructible on the main thread.
+ //
+ // Since we hold `&self`, i.e. a reference to such an object, and we
+ // know it cannot possibly be on another thread than the main, we know
+ // that the current thread is the main thread.
+ unsafe { MainThreadMarker::new_unchecked() }
+ }
+
+ /// Allocate a new instance of the class on the main thread.
+ ///
+ ///
+ /// # Example
+ ///
+ /// Create a view on the main thread.
+ ///
+ /// ```
+ /// use objc2::{MainThreadOnly, MainThreadMarker};
+ /// # #[cfg(available_in_app_kit)]
+ /// use objc2_app_kit::NSView;
+ /// use objc2_core_foundation::CGRect;
+ /// #
+ /// # use objc2::rc::{Allocated, Retained};
+ /// #
+ /// # objc2::extern_class!(
+ /// # #[unsafe(super(objc2::runtime::NSObject))]
+ /// # #[thread_kind = MainThreadOnly]
+ /// # #[name = "NSObject"] // For example
+ /// # struct NSView;
+ /// # );
+ /// #
+ /// # impl NSView {
+ /// # fn initWithFrame(this: Allocated<Self>, _frame: CGRect) -> Retained<Self> {
+ /// # // Don't use frame, this is NSObject
+ /// # unsafe { objc2::msg_send![this, init] }
+ /// # }
+ /// # }
+ ///
+ /// # #[cfg(doctests_not_always_run_on_main_thread)]
+ /// let mtm = MainThreadMarker::new().expect("must be on the main thread");
+ /// # let mtm = unsafe { MainThreadMarker::new_unchecked() };
+ ///
+ /// let frame = CGRect::default();
+ /// let view = NSView::initWithFrame(NSView::alloc(mtm), frame);
+ /// ```
+ #[inline]
+ fn alloc(mtm: MainThreadMarker) -> Allocated<Self>
+ where
+ Self: Sized + ClassType,
+ {
+ let _ = mtm;
+ // SAFETY: We hold `MainThreadMarker`, and the class is safe to
+ // allocate on the main thread.
+ unsafe { Allocated::alloc(Self::class()) }
+ }
+}
+
+impl<'a, T: ?Sized + ClassType<ThreadKind = dyn MainThreadOnly + 'a>> private::SealedMainThreadOnly
+ for T
+{
+}
+unsafe impl<'a, T: ?Sized + ClassType<ThreadKind = dyn MainThreadOnly + 'a>> MainThreadOnly for T {}
+
+impl<P: ?Sized> private::SealedMainThreadOnly for ProtocolObject<P> {}
+unsafe impl<P: ?Sized + MainThreadOnly> MainThreadOnly for ProtocolObject<P> {}
+
+/// The allowed values in [`ClassType::ThreadKind`].
+///
+/// One of [`dyn AnyThread`] or [`dyn MainThreadOnly`].
+///
+/// [`dyn AnyThread`]: AnyThread
+/// [`dyn MainThreadOnly`]: MainThreadOnly
+pub trait ThreadKind: private::SealedThreadKind {
+ // To mark `ThreadKind` as dyn-incompatible for now.
+ #[doc(hidden)]
+ const __DYN_INCOMPATIBLE: ();
+}
+
+impl private::SealedThreadKind for dyn AnyThread + '_ {}
+impl ThreadKind for dyn AnyThread + '_ {
+ const __DYN_INCOMPATIBLE: () = ();
+}
+
+impl private::SealedThreadKind for dyn MainThreadOnly + '_ {}
+impl ThreadKind for dyn MainThreadOnly + '_ {
+ const __DYN_INCOMPATIBLE: () = ();
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[allow(unused)]
+ fn dyn_compatible(_: &dyn AnyThread, _: &dyn MainThreadOnly) {}
+}
diff --git a/third_party/rust/objc2/src/topics/about_generated/CHANGELOG.md b/third_party/rust/objc2/src/topics/about_generated/CHANGELOG.md
@@ -0,0 +1,1254 @@
+# Framework crate changelog
+
+Changes to `objc2`'s framework crates will be documented in this file.
+
+The history of these crates are a bit convoluted; the Foundation parts
+originally existed as `objc2-foundation`, but later it was integrated into
+`objc2` under `objc2::foundation`, later again moved into `icrate::Foundation`
+as part of the larger `icrate`, and finally `icrate` was split back out into
+`objc2-foundation` and other smaller crates - hence the confusing versioning.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
+
+## Unreleased - YYYY-MM-DD
+
+### Added
+* Added `IOKit` "serial" submodule.
+* Added various missing APIs.
+
+### Changed
+* Updated SDK from Xcode 16.3 to 16.4.
+
+ View the release notes to learn more details:
+ - [16.4](https://developer.apple.com/documentation/xcode-release-notes/xcode-16_4-release-notes)
+
+ Breaking changes are noted elsewhere in this changelog entry.
+* **BREAKING**: The reply block in `FSVolumeRenameOperations::setVolumeName_replyHandler` now
+ takes a nullable file name.
+* **BREAKING**: The media selection option on `AVAssetVariantQualifier` is now nullable.
+
+### Fixed
+* **BREAKING**: Fix structs with packed alignment by marking them `#[repr(packed(...))]`.
+
+## [0.3.1] - 2025-04-19
+[0.3.1]: https://github.com/madsmtm/objc2/compare/frameworks-0.3.0...frameworks-0.3.1
+
+### Added
+* Marked a bunch of functions safe in:
+ - `CoreFoundation` / `objc2-core-foundation`.
+ - `SystemConfiguration` / `objc2-system-configuration`.
+* A lot of functions in CoreFoundation and similar frameworks have been moved
+ onto the type they refer to as associated functions / methods instead.
+ The old names are kept as deprecated.
+* Added new framework crates:
+ - `AccessorySetupKit` / `objc2-accessory-setup-kit`.
+ - `AppClip` / `objc2-app-clip`.
+ - `ApplicationServices` / `objc2-application-services` (some submodules left out).
+ - `BrowserEngineCore` / `objc2-browser-engine-core`.
+ - `BrowserEngineKit` / `objc2-browser-engine-kit`.
+ - `Carbon` / `objc2-carbon` (empty for now).
+ - `CarPlay` / `objc2-car-play`.
+ - `CFNetwork` / `objc2-cf-network`.
+ - `Cinematic` / `objc2-cinematic`.
+ - `ClockKit` / `objc2-clock-kit`.
+ - `Collaboration` / `objc2-collaboration`.
+ - `CoreAudioKit` / `objc2-audio-kit`.
+ - `CoreHaptics` / `objc2-core-haptics`.
+ - `CoreLocationUI` / `objc2-core-location-ui`.
+ - `CoreMediaIO` / `objc2-core-media-io`.
+ - `CoreNFC` / `objc2-core-nfc`.
+ - `CoreServices` / `objc2-core-services` (CarbonCore sub-framework not yet supported).
+ - `CoreSpotlight` / `objc2-core-spotlight`.
+ - `CoreTelephony` / `objc2-core-telephony`.
+ - `CryptoTokenKit` / `objc2-crypto-token-kit`.
+ - `DeviceDiscoveryExtension` / `objc2-device-discovery-extension`.
+ - `ExecutionPolicy` / `objc2-execution-policy`.
+ - `ExposureNotification` / `objc2-exposure-notification`.
+ - `GameplayKit` / `objc2-gameplay-kit`.
+ - `HealthKitUI` / `objc2-health-kit-ui`.
+ - `IdentityLookupUI` / `objc2-identity-lookup-ui`.
+ - `ImageCaptureCore` / `objc2-image-capture-core`.
+ - `ImageIO` / `objc2-image-io`.
+ - `Intents` / `objc2-intents`.
+ - `IntentsUI` / `objc2-intents-ui`.
+ - `IOBluetooth` / `objc2-io-bluetooth`.
+ - `IOBluetoothUI` / `objc2-io-bluetooth-ui`.
+ - `IOKit` / `objc2-io-kit` (currently only `usb`, `graphics`, `hid` and `hidsystem` submodules).
+ - `IOUSBHost` / `objc2-io-usb-host`.
+ - `iTunesLibrary` / `objc2-itunes-library`.
+ - `JavaScriptCore` / `objc2-java-script-core`.
+ - `LatentSemanticMapping` / `objc2-latent-semantic-mapping`.
+ - `MediaAccessibility` / `objc2-media-accessibility`.
+ - `MediaExtension` / `objc2-media-extension`.
+ - `MediaSetup` / `objc2-media-setup`.
+ - `MediaToolbox` / `objc2-media-toolbox`.
+ - `NotificationCenter` / `objc2-notification-center`.
+ - `OSLog` / `objc2-os-log`.
+ - `ParavirtualizedGraphics` / `objc2-paravirtualized-graphics`.
+ - `PassKit` / `objc2-pass-kit`.
+ - `PDFKit` / `objc2-pdf-kit`.
+ - `PencilKit` / `objc2-pencil-kit`.
+ - `PHASE` / `objc2-phase`.
+ - `PushKit` / `objc2-push-kit`.
+ - `PushToTalk` / `objc2-push-to-talk`.
+ - `Quartz` / `objc2-quartz` (QuartzComposer left out for now).
+ - `QuickLook` / `objc2-quick-look`.
+ - `QuickLookThumbnailing` / `objc2-quick-look-thumbnailing`.
+ - `QuickLookUI` / `objc2-quick-look-ui`.
+ - `ReplayKit` / `objc2-replay-kit`.
+ - `SafariServices` / `objc2-safari-services`.
+ - `SafetyKit` / `objc2-safety-kit`.
+ - `ScreenTime` / `objc2-screen-time`.
+ - `ScriptingBridge` / `objc2-scripting-bridge`.
+ - `SecurityFoundation` / `objc2-security-foundation`.
+ - `SecurityInterface` / `objc2-security-interface`.
+ - `SensorKit` / `objc2-sensor-kit`.
+ - `SharedWithYou` / `objc2-shared-with-you`.
+ - `SharedWithYouCore` / `objc2-shared-with-you-core`.
+ - `ShazamKit` / `objc2-shazam-kit`.
+ - `ThreadNetwork` / `objc2-thread-network`.
+ - `TVMLKit` / `objc2-tv-ml-kit`.
+ - `TVServices` / `objc2-tv-services`.
+ - `TVUIKit` / `objc2-tv-ui-kit`.
+ - `UserNotificationsUI` / `objc2-user-notifications-ui`.
+ - `VideoSubscriberAccount` / `objc2-video-subscriber-account`.
+* Added fallback for `MTLCopyAllDevices` on non-macOS platforms.
+* Added `CFType::retain_count` convenience function.
+* Improved `CFArray` interface:
+ - Added constructors.
+ - Added getters.
+ - Added iteration helpers.
+ - Added a few mutation helpers.
+* Improved `CFDictionary` interface:
+ - Added constructors.
+ - Added getters.
+ - Added mutation methods.
+* Added simple `#define`ed constants.
+* Added `CFString::as_str_unchecked`.
+* Added a few compatibility aliases to help with migrating from the
+ `core-foundation` crate. Import the `Type` trait to use them.
+* Added support for using `dispatch2` types in framework crates.
+* Marked some CoreFoundation and CoreGraphics types as thread-safe.
+* Added a few missing inline functions:
+ - `CFUserNotificationCheckBoxChecked`
+ - `CFUserNotificationSecureTextField`
+ - `CFUserNotificationPopUpSelection`
+* Added `CFRange::new`.
+* **BREAKING**: Added `MainThreadOnly` to the following UIKit traits:
+ - `UICalendarSelectionMultiDateDelegate`
+ - `UICalendarSelectionSingleDateDelegate`
+ - `UICalendarSelectionWeekOfYearDelegate`
+ - `UICalendarViewDelegate`
+ - `UIEditMenuInteractionDelegate`
+ - `UITabBarControllerSidebarDelegate`
+* Added `CFURL` and `NSURL` path conversion methods `from_file_path`,
+ `from_directory_path` and `to_file_path`.
+* Added `CFURL` string conversion method `from_str_unchecked`.
+
+### Changed
+* Updated SDK from Xcode 16.2 to 16.3.
+
+ View the release notes to learn more details:
+ - [16.3](https://developer.apple.com/documentation/xcode-release-notes/xcode-16_3-release-notes)
+
+ Breaking changes are noted elsewhere in this changelog entry.
+* Made `CFArray`, `CFBag`, `CFBinaryHeap`, `CFDictionary`, `CFSet` and their
+ mutable counterparts generic. Most signatures won't use the generic version,
+ but making these generic allows for higher-level abstractions and more
+ ergonomic usage.
+* The `CFBase` feature is now always enabled.
+* Renamed `objc2-io-surface`'s `"ObjC"` feature to `"objc2"`.
+* **BREAKING**: Renamed `GKInviteRecipientResponse` enumeration variants.
+* **BREAKING**: Made the following APIs no longer return an `Option`:
+ - `GKAchievement::player`
+ - `GKScore::player`
+ - `GKTurnBasedExchangeReply::replyDate`
+
+### Fixed
+* **BREAKING**: Fixed the type of `AuthorizationExecuteWithPrivileges`'s
+ `arguments` parameter by changing it from `NonNull<NonNull<c_char>>` to
+ `NonNull<AuthorizationString>`, i.e. `NonNull<*const c_char>`. See #711
+ for more details.
+* Fixed off-by-one error in `NSArray::insert`.
+* **BREAKING**: Fixed nullability of types in `WKWebView::createWebArchiveDataWithCompletionHandler`.
+
+## [0.3.0] - 2025-01-22
+[0.3.0]: https://github.com/madsmtm/objc2/compare/frameworks-0.3.0...frameworks-0.3.0
+
+### Added
+* Added `NSData::to_vec` and `NSData::iter` helper methods.
+* Added `Eq` implementation for `NSValue` and subclasses.
+* Added `CopyingHelper` and `MutableCopyingHelper`, which are used to specify
+ the types returned from `NSCopying` and `NSMutableCopying`.
+* Added `MTLResourceID::from_raw` and `MTLResourceID::as_raw` to allow
+ querying the underlying data.
+* Added new framework crates:
+ - `ARKit` / `objc2-ar-kit`.
+ - `AudioToolbox` / `objc2-audio-toolbox`.
+ - `AVFAudio` / `objc2-avf-audio`.
+ - `AVFoundation` / `objc2-av-foundation`.
+ - `AVRouting` / `objc2-av-routing`.
+ - `CompositorServices` / `objc2-compositor-services`.
+ - `CoreAudioTypes` / `objc2-core-audio-types`.
+ - `CoreAudio` / `objc2-core-audio`.
+ - `ColorSync` / `objc2-color-sync`.
+ - `CoreGraphics` / `objc2-core-graphics`.
+ - `CoreFoundation` / `objc2-core-foundation`.
+ - `CoreMedia` / `objc2-core-media`.
+ - `CoreMIDI` / `objc2-core-midi`.
+ - `CoreText` / `objc2-core-text`.
+ - `CoreVideo` / `objc2-core-video`.
+ - `DiskArbitration` / `objc2-disk-arbitration`.
+ - `EventKitUI` / `objc2-event-kit-ui`.
+ - `HomeKit` / `objc2-home-kit`.
+ - `IOSurface` / `objc2-io-surface`.
+ - `Messages` / `objc2-messages`.
+ - `MessageUI` / `objc2-message-ui`.
+ - `ModelIO` / `objc2-model-io`.
+ - `MetalPerformanceShaders` / `objc2-metal-performance-shaders`.
+ - `MetalPerformanceShadersGraph` / `objc2-metal-performance-shaders-graph`.
+ - `OpenDirectory` / `objc2-open-directory`.
+ - `SceneKit` / `objc2-scene-kit`.
+ - `ScreenSaver` / `objc2-screen-saver`.
+ - `Security` / `objc2-security`.
+ - `SpriteKit` / `objc2-sprite-kit`.
+ - `SystemConfiguration` / `objc2-system-configuration`.
+ - `VideoToolbox` / `objc2-video-toolbox`.
+ - `WatchConnectivity` / `objc2-watch-connectivity`.
+ - `WatchKit` / `objc2-watch-kit`.
+* Added `#[must_use]` attributes where the C headers have them.
+* Added automatic conversion of `Bool` in external function argument and
+ return types (similar to what's done on methods).
+* Added automatic conversion to `Retained` in external function return types
+ (similar to what's done on methods).
+
+### Changed
+* Enabled all Cargo features by default, and removed the previous `"all"`
+ feature for doing this.
+
+ If you're writing a library, you likely want to disable default features,
+ and spell out the desired features explicitly, to avoid large compile-times
+ for your consumers.
+
+ Recommended for **library** crates:
+ ```toml
+ # Before
+ [dependencies]
+ objc2-foundation = { version = "0.2", features = [
+ "NSNotification",
+ "NSString",
+ "NSThread",
+ "NSObject",
+ "NSArray",
+ ] }
+
+ # After
+ [dependencies]
+ # Added `default-features = false`
+ objc2-foundation = { version = "0.3", default-features = false, features = [
+ "NSNotification",
+ "NSString",
+ "NSThread",
+ "NSObject",
+ "NSArray",
+ ] }
+ ```
+
+ Recommended for **binary** crates:
+ ```toml
+ # Before
+ [dependencies]
+ objc2-foundation = { version = "0.2", features = ["all"] }
+
+ # After
+ [dependencies]
+ # Removed "all" feature
+ objc2-foundation = "0.3"
+ ```
+* Moved `MainThreadBound` and `run_on_main` to the `dispatch2` crate.
+* Removed `HasStableHash` requirement on `NSDictionary` and `NSSet` creation
+ methods. This was added in an abundance of caution, but prevents real-world
+ usage of these types, and isn't actually needed for soundness (the
+ documentation mentions the collection being "corrupt" if the hash is
+ changed, but that's not the same as it being unsound).
+* **BREAKING**: Made the following types `InteriorMutable`:
+ - `NSString` and `NSMutableString`.
+ - `NSAttributedString` and `NSMutableAttributedString`.
+ - `NSCharacterSet` and `NSMutableCharacterSet`.
+ - `NSURLRequest` and `NSMutableURLRequest`.
+ - `NSData` and `NSMutableData`.
+ - `NSEnumerator`.
+ - `NSArray` and `NSMutableArray`.
+ - `NSDictionary` and `NSMutableDictionary`.
+ - `NSSet`, `NSMutableSet`, `NSCountedSet`, `NSOrderedSet` and
+ `NSMutableOrderedSet`.
+
+ This means that these can now be `retain`-ed like you would expect, and you
+ no longer need to use `mut` to mutate them, but it also means that they are
+ no longer `Send + Sync`.
+* **BREAKING**: Renamed the `bytes[_mut]` methods on `NSData` to
+ `as[_mut]_bytes_unchecked`, and made them `unsafe`, since the data can no
+ longer ensure that it is not mutated while the bytes are in use.
+* No longer require `Eq + Hash` for `NSDictionary` keys and `NSSet` values,
+ since it was overly restrictive.
+* **BREAKING**: Removed `NSSet` methods `contains`, `is_subset`, `is_superset`
+ and `is_disjoint` that were simple wrappers over the original methods.
+* **BREAKING**: Renamed `from_id_slice` to `from_retained_slice`.
+* **BREAKING**: Renamed `NSString::as_str` to `to_str`, and made it `unsafe`,
+ since we cannot ensure that the given pool is actually the innermost pool.
+* Updated SDK from Xcode 15.4 to 16.2.
+
+ View the release notes to learn more details:
+ - [16.0](https://developer.apple.com/documentation/xcode-release-notes/xcode-16-release-notes)
+ - [16.1](https://developer.apple.com/documentation/xcode-release-notes/xcode-16_1-release-notes)
+ - [16.2](https://developer.apple.com/documentation/xcode-release-notes/xcode-16_2-release-notes)
+
+ Breaking changes are noted elsewhere in this changelog entry.
+* **BREAKING**: `NSWindowSharingReadWrite` was deprecated, and moved from
+ `NSWindowSharingType` to a separate static.
+* **BREAKING**: Moved a few methods on AppKit `NSAttributedString` categories.
+ - `NSAttributedStringKitAdditions` moved to
+ `NSAttributedStringAppKitAdditions`.
+ - `NSMutableAttributedStringKitAdditions` moved to
+ `NSMutableAttributedStringAppKitAdditions`.
+ - `NSAttributedStringDocumentFormats` moved to
+ `NSAttributedStringAppKitDocumentFormats`.
+ - `NSAttributedStringAppKitAttributeFixing` moved to
+* **BREAKING**: Make `"MTLResource"` a sub-protocol of the new `MTLAllocation`.
+ This makes a bunch of things cfg-gated behind `"MTLAllocation"`.
+* **BREAKING**: Cfg-gated `LABiometryType` behind `"LABiometryType"` instead
+ of `"LAContext"`.
+* **BREAKING**: Cfg-gated `HKAudiogramSensitivityPoint` behind
+ `"HKAudiogramSensitivityPoint"` instead of `"HKAudiogramSample"`.
+* **BREAKING**: No longer special-case `NSObjectProtocol` objects. Certain
+ methods that previously used `&NSObject` for convenience now use the more
+ correct `&ProtocolObject<dyn NSObjectProtocol>`.
+* **BREAKING**: Moved `CGFloat`, `CGPoint`, `CGSize` and `CGRect` from
+ `objc2-foundation` to `objc2-core-foundation`.
+* **BREAKING**: The feature flag guarding `SCSensitivityAnalysis` changed.
+* **BREAKING**: `-[NSSavePanel beginSheetForDirectory:file:modalForWindow:modalDelegate:didEndSelector:contextInfo:]`
+ now takes an optional value as the file path.
+* **BREAKING**: No longer automatically enable `std` and `alloc` features of
+ dependencies. If you want a certain framework crate to use `std` or `alloc`
+ features, you cannot rely on a higher-level crate to enable that for you.
+* **BREAKING**: Updated `objc2` dependency to `v0.6.0`.
+* **BREAKING**: Updated `block2` dependency to `v0.6.0`.
+
+### Deprecated
+* Moved `MainThreadMarker` from `objc2-foundation` to `objc2`.
+* Deprecated the free-standing function `objc2_foundation::is_main_thread`.
+
+### Removed
+* **BREAKING**: Removed the deprecated function `MainThreadMarker::run_on_main`.
+* **BREAKING**: Removed dependency on `objc2-link-presentation` from `objc2-ui-kit`.
+
+ `objc2-link-presentation` is going to depend on `objc2-ui-kit` in the future,
+ so this would result in a recursive dependency. This does mean that the
+ following APIs are no longer available:
+ - `UIActivityItemSource::activityViewControllerLinkMetadata`
+ - `UIDocumentProperties::initWithMetadata`
+ - `UIDocumentProperties::metadata`
+ - `UIDocumentProperties::setMetadata`
+* **BREAKING**: Removed a bunch of deprecated methods in CloudKit:
+ - `CKFetchNotificationChangesOperation::initWithPreviousServerChangeToken`
+ - `CKFetchNotificationChangesOperation::previousServerChangeToken`
+ - `CKFetchNotificationChangesOperation::resultsLimit`
+ - `CKFetchNotificationChangesOperation::moreComing`
+ - `CKFetchNotificationChangesOperation::notificationChangedBlock`
+ - `CKMarkNotificationsReadOperation::initWithNotificationIDsToMarkRead`
+ - `CKMarkNotificationsReadOperation::notificationIDs`
+ - `CKMarkNotificationsReadOperation::markNotificationsReadCompletionBlock`
+ - `CKModifyBadgeOperation::initWithBadgeValue`
+ - `CKModifyBadgeOperation::initWithBadgeValue`
+ - `CKModifyBadgeOperation::badgeValue`
+ - `CKModifyBadgeOperation::modifyBadgeCompletionBlock`
+ - `CKModifyBadgeOperation::initWithBadgeValue`
+* **BREAKING**: Removed `SCStreamDelegate::userDidStopStream`.
+- **BREAKING**: Removed `BGContinuedProcessingTaskRequest`.
+
+### Fixed
+* **BREAKING**: Converted function signatures into using `extern "C-unwind"`.
+ This allows Rust and Objective-C unwinding to interoperate.
+* Removed incorrectly declared `BGTask::new` method.
+* **BREAKING**: Marked the following classes and protocols as `MainThreadOnly`:
+ - `ASAuthorizationControllerPresentationContextProviding`,
+ - `ASAuthorizationControllerDelegate`
+ - `ASAuthorizationProviderExtensionAuthorizationRequestHandler`
+ - `ASAccountAuthenticationModificationControllerPresentationContextProviding`
+ - `ASWebAuthenticationPresentationContextProviding`
+ - `EXHostViewControllerDelegate`
+ - `MKMapViewDelegate`
+ - `MTKViewDelegate`
+ - `UIToolTipConfiguration`
+ - `UIToolTipInteractionDelegate`
+ - `UITraitListEnvironment`
+ - `NSOutlineViewDelegate`
+ - `MEComposeSessionHandler`
+ - `MEExtension`
+ - `MEMessageSecurityHandler`
+ - `PHContentEditingController`
+ - `PHLivePhotoViewDelegate`
+ - `NSCollectionView*` classes
+ - `NSCollectionLayout*` classes and protocols
+ - `NSTableColumn`
+ - A bunch of things in `WebKit`.
+* **BREAKING**: Marked methods on the `NSObjectUIAccessibility` category as
+ `MainThreadOnly`.
+* **BREAKING**: Fixed `AnyClass` and `AnyProtocol` mapping. For example
+ `NSStringFromProtocol` now takes `&AnyProtocol`,
+ `NSPasteboard::readObjectsForClasses_options` now takes
+ `&NSArray<AnyClass>`, and `UITrait` is now exposed more correctly.
+* **BREAKING**: Fixed the return type of `NSClassFromString`.
+* Use Clang `-fmodules` for more correct header translation.
+* Fixed the encoding of `NSDecimal`.
+* Fixed pointer `const`-ness.
+* **BREAKING**: Fixed `enum` prefix stripping. Enumerators like
+ `NSWindowSharingType`, `MTLFeatureSet` etc. now have correctly stripped
+ variant names (`NSWindowSharingType::None` instead of
+ `NSWindowSharingType::NSWindowSharingNone`).
+* Added the missing mapping for the `sampleCount` property on
+ `MTLRasterizationRateLayerDescriptor`.
+
+
+## [0.2.2] - 2024-05-21
+[0.2.2]: https://github.com/madsmtm/objc2/compare/frameworks-0.2.1...frameworks-0.2.2
+
+### Fixed
+* Fixed an issue with publishing using an older version of Cargo that didn't
+ handle the `lints.workspace = true` Cargo setup properly.
+
+
+## [0.2.1] - 2024-05-21 (Yanked)
+[0.2.1]: https://github.com/madsmtm/objc2/compare/frameworks-0.2.0...frameworks-0.2.1
+
+### Added
+* `NS_OPTIONS` enums are now `bitflags!`-style enums.
+
+ This means that they have the usual `ops::BitAnd`, `ops::BitOr`, `ops::Not`,
+ etc. implementations that you would expect from an option enum.
+* Added optional support for a few methods depending on types from `libc`.
+* Added new framework crates:
+ - `CoreBluetooth` / `objc2-core-bluetooth`.
+ - `Virtualization` / `objc2-virtualization`.
+ - `Symbols` / `objc2-symbols`.
+ - `UIKit` / `objc2-ui-kit`.
+ - `Accounts` / `objc2-accounts`.
+ - `AppTrackingTransparency` / `objc2-app-tracking-transparency`.
+ - `MLCompute` / `objc2-ml-compute`.
+ - `ContactsUI` / `objc2-contacts-ui`.
+ - `NearbyInteraction` / `objc2-nearby-interaction`.
+ - `ScreenCaptureKit` / `objc2-screen-capture-kit`.
+ - `CoreImage` / `objc2-core-image`.
+ - `CoreMotion` / `objc2-core-motion`.
+ - `MultipeerConnectivity` / `objc2-multipeer-connectivity`.
+ - `NaturalLanguage` / `objc2-natural-language`.
+ - `CoreML` / `objc2-core-ml`.
+ - `Vision` / `objc2-vision`.
+ - `AVKit` / `objc2-av-kit`.
+ - `NetworkExtension` / `objc2-network-extension`.
+ - `SensitiveContentAnalysis` / `objc2-sensitive-content-analysis`.
+ - `PhotosUI` / `objc2-photos-ui`.
+ - `FinderSync` / `objc2-finder-sync`.
+ - `Social` / `objc2-social`.
+* `objc2-quartz-core`: Added `CAMetalDrawable` and `CAMetalLayer`.
+* `objc2-app-kit`: Added methods to access `CALayer` from `NSView`.
+* `objc2-metal`: Added `MTLCounterErrorValue`, `MTLCounterDontSample` and
+ `MTLTextureSwizzleChannelsDefault` constants.
+
+### Changed
+* Updated SDK from Xcode 15.3 to 15.4.
+
+ View the release notes to learn more details:
+ - [15.4](https://developer.apple.com/documentation/xcode-release-notes/xcode-15_4-release-notes)
+
+### Fixed
+* `objc2-metal`: All protocols are now marked as `IsRetainable`.
+
+ This may technically break your code if you had some custom object that
+ implements the protocol, but was marked with `Mutability = Mutable`, but
+ Metal protocols are assumed retainable by Objective-C code, so that'd have
+ been unsound anyway.
+* Add `#[cfg(target_os = "...")]` when a platform-specific dependency is used.
+
+ This improves iOS support, as crates like `objc2-app-kit` are no longer
+ enabled on this platform.
+* Fix dependency feature flags (e.g. `block2`) not enabling the matching
+ features in dependencies (e.g. not enabling `objc2-foundation/block2`).
+
+### Removed
+* `objc2-metal`: Removed internal `__MTLPackedFloat3` and made `MTLPackedFloat3` public.
+* `objc2-core-data`: Removed broken GNUStep support.
+* `objc2-quartz-core`: Removed broken GNUStep support.
+
+
+## [0.2.0] - 2024-04-17
+[0.2.0]: https://github.com/madsmtm/objc2/compare/frameworks-0.2.0-icrate-0.1.2...frameworks-0.2.0
+
+### Added
+* Added `NSObject` categories, notably those used by key-value coding and
+ observing.
+* Added a few statics that were previously omitted (notably a few
+ `NSWindowLevel` constants).
+
+### Changed
+* Updated SDK from Xcode 15.0.1 to 15.3.
+
+ View the release notes to learn more details:
+ - [15.1](https://developer.apple.com/documentation/xcode-release-notes/xcode-15_1-release-notes)
+ - [15.2](https://developer.apple.com/documentation/xcode-release-notes/xcode-15_2-release-notes)
+ - [15.3](https://developer.apple.com/documentation/xcode-release-notes/xcode-15_3-release-notes)
+* **BREAKING**: Changed how categories are handled; now, when a library has
+ defined methods on a class defined in a different framework, a helper trait
+ is output with the methods, instead of the methods being implemented
+ directly on the type.
+* **BREAKING**: Changed how enums are handled; now a newtype is generated for
+ each enum, with the enum variants as constants on that newtype, instead of
+ cluttering the top-level namespace.
+* **BREAKING**: Split `icrate` into multiple smaller crates all prefixed with
+ `objc2-`, and changed how feature flags work.
+
+ Feature flags are now based on the header file name where the item is
+ defined, instead of being based on the class name itself.
+
+ These two things should decrease compilation times by quite a lot, at the
+ cost of having to specify more precisely what you need in your `Cargo.toml`.
+
+ An example migration can be seen in the following:
+
+ ```toml
+ # Before
+ [dependencies.icrate]
+ version = "0.1.0"
+ features = [
+ "Foundation",
+ "Foundation_NSNotification",
+ "Foundation_NSString",
+ "Foundation_NSThread",
+ "Foundation_NSArray",
+ "Foundation_NSMutableArray",
+ "AppKit",
+ "AppKit_NSApplication",
+ ]
+
+ # After
+ # Moved to `objc2-foundation` and `objc2-app-kit` crates.
+ [dependencies]
+ # Removed `Foundation_NSMutableArray`, it is included via `NSArray`.
+ # Added `NSObject` as the `NSCopying` protocol comes from there.
+ objc2-foundation = { version = "0.2", features = ["NSNotification", "NSString", "NSThread", "NSObject", "NSArray"] }
+ # Added `NSResponder` as it's required by `NSApplication`.
+ # Added `NSRunningApplication` as a lot of application constants come from here.
+ objc2-app-kit = { version = "0.2", features = ["NSResponder", "NSApplication", "NSRunningApplication"] }
+ ```
+* Marked `NSView::isFlipped`, `NSView::convertRect_toView`,
+ `NSWindow::convertRectToScreen` and `NSWindow::convertPointFromScreen` as
+ safe.
+* Renamed the `block` and `objective-c` feature flags to `block2` and `objc2`.
+* **BREAKING**: Updated `block2` to `v0.5`.
+
+### Deprecated
+* Deprecated `Foundation::MainThreadMarker::run_on_main`, use the new
+ free-standing function `objc2_foundation::run_on_main` instead.
+
+### Removed
+* Removed private functionality in the `Speech` framework. This was never
+ intended to be exposed by Apple.
+
+ This is technically a breaking change, but will be allowed in a minor
+ version since it isn't supported by Apple.
+
+### Fixed
+* Fixed the `QuartzCore` and `Photos` frameworks not being loaded correctly.
+* Fixed a few feature gates on methods showing up unnecessarily.
+* **BREAKING**: Added `MainThreadMarker` parameter to a few methods where it
+ was erroneously missing.
+* **BREAKING**: Fix the type of `GKMatchProperties` and `NSWindow`'s
+ `restorationClass`.
+* **BREAKING**: Fixed feature flags not being emitted on typedefs, enums,
+ functions and statics.
+
+
+## [icrate 0.1.2] - 2023-04-18
+[icrate 0.1.2]: https://github.com/madsmtm/objc2/compare/frameworks-0.2.0-icrate-0.1.1...frameworks-0.2.0-icrate-0.1.2
+
+## Fixed
+* Fixed "multiple applicable items in scope" error when upgrading `objc2` to `v0.5.1`.
+
+
+## [icrate 0.1.1] - 2023-04-17
+[icrate 0.1.1]: https://github.com/madsmtm/objc2/compare/frameworks-0.2.0-icrate-0.1.0...frameworks-0.2.0-icrate-0.1.1
+
+### Deprecated
+* Deprecated the `icrate` crate, it has been split into multiple smaller crates.
+
+
+## [icrate 0.1.0] - 2023-12-23
+[icrate 0.1.0]: https://github.com/madsmtm/objc2/compare/frameworks-0.2.0-icrate-0.0.4...frameworks-0.2.0-icrate-0.1.0
+
+### Added
+* Added `MainThreadMarker` `From` implementation for `MainThreadOnly` types.
+* Added `Send` and `Sync` implementations for a bunch more types (same as the
+ ones Swift marks as `@Sendable`).
+* Made some common methods in `AppKit` safe.
+* Added missing `NSCopying` and `NSMutableCopying` zone methods.
+* Added `Eq` and `Ord` implementations for `NSNumber`, since its
+ handling of floating point values allows it.
+* Added `NS[Mutable]Dictionary::from_id_slice` and
+ `NS[Mutable]Dictionary::from_slice`.
+* Added `NSMutableDictionary::insert` and `NSMutableSet::insert` which can
+ be more efficient than the previous insertion methods.
+
+### Changed
+* Updated SDK from Xcode 14.2 to 15.0.1.
+
+ View the release notes to learn more details:
+ - [14.3](https://developer.apple.com/documentation/xcode-release-notes/xcode-14_3-release-notes)
+ - [14.3.1](https://developer.apple.com/documentation/xcode-release-notes/xcode-14_3_1-release-notes)
+ - [15.0](https://developer.apple.com/documentation/xcode-release-notes/xcode-15-release-notes)
+ - [15.0.1](https://developer.apple.com/documentation/xcode-release-notes/xcode-15_0_1-release-notes)
+
+ Breaking changes are noted elsewhere in this changelog entry.
+* Moved the `ns_string!` macro to `icrate::Foundation::ns_string`. The old
+ location in the crate root is deprecated.
+* **BREAKING**: The following two methods on
+ `MTLAccelerationStructureCommandEncoder` now take a nullable scratch buffer:
+ - `refitAccelerationStructure_descriptor_destination_scratchBuffer_scratchBufferOffset`
+ - `refitAccelerationStructure_descriptor_destination_scratchBuffer_scratchBufferOffset_options`
+* **BREAKING**: Marked UI-related classes as `MainThreadOnly`, and UI-related
+ protocols as `IsMainThreadOnly`.
+
+ This means that they can now only be constructed, retrieved and used on the
+ main thread, meaning you usually have to acquire a `MainThreadMarker` first.
+
+ ```rust, ignore
+ // Before
+ let app = unsafe { NSApplication::sharedApplication() };
+ let view = unsafe { NSView::initWithFrame(NSView::alloc(), frame) };
+ // Do something with `app` and `view`
+
+ // After
+ let mtm = MainThreadMarker::new().unwrap();
+ let app = unsafe { NSApplication::sharedApplication(mtm) };
+ let view = unsafe { NSView::initWithFrame(mtm.alloc(), frame) };
+ // Do something with `app` and `view`
+ ```
+* **BREAKING**: Changed the `NSApp` static to be a function taking `MainThreadMarker`.
+* **BREAKING**: Renamed `NS[Mutable]Dictionary::from_keys_and_objects` to
+ `NS[Mutable]Dictionary::from_vec`.
+* **BREAKING**: Renamed `NSMutableDictionary::insert` and
+ `NSMutableSet::insert` to `insert_id`.
+* **BREAKING**: `CWWiFiClient::interfaceNames` has been renamed to `CWWiFiClient::interfaceNames_class`.
+* **BREAKING**: Updated `objc2` to `v0.5.0`.
+* **BREAKING**: Updated `block2` to `v0.4.0`.
+
+### Removed
+* **BREAKING**: Removed the `MainThreadMarker` argument from the closure
+ passed to `MainThreadBound::get_on_main`.
+* **BREAKING**: Removed `Foundation::CopyHelper` since it is superseded by
+ `objc2::mutability::CounterpartOrSelf`.
+* **BREAKING**: Removed the following APIs, as they are no longer available in macOS 14 / iOS 17:
+ - `NSFileProviderDomain::volumeUUID`
+ - `CLBeaconIdentityConstraint::UUID`
+ - `CLBeaconIdentityConstraint::major`
+ - `CLBeaconIdentityConstraint::minor`
+ - `ASIdentifierManager::clearAdvertisingIdentifier`
+* Removed private `MetricKit::_MXSignpostMetricsSnapshot` function.
+
+### Fixed
+* **BREAKING**: Added `Eq + Hash` requirement on most `NSDictionary` and
+ `NSSet` methods, thereby making sure that the types are actually correct
+ to use in such hashing collections.
+* **BREAKING**: Added `HasStableHash` requirement on `NSDictionary` and
+ `NSSet` creation methods, fixing a long-standing soundess issue.
+* Fixed the protocol names of `NSAccessibilityElementProtocol`,
+ `NSTextAttachmentCellProtocol` and `NSFileProviderItemProtocol`.
+* **BREAKING**: Generic types no longer strictly require `Message` (although
+ most of their trait implementations still require that).
+* **BREAKING**: Removed a workaround that made the `NSCopying` and
+ `NSMutableCopying` protocols not act as regular protocols (many methods used
+ `AnyObject` instead of the correct `ProtocolObject<dyn NSCopying>`).
+* Update the minimum deployment target, which adds a few missing protocol
+ implementations and methods for `NSPopover` and `NSLayoutAnchor`.
+* **BREAKING**: `CKSystemSharingUIObserver` and `CKLocationSortDescriptor` are no longer marked thread safe.
+* **BREAKING**: `NSColor::ignoresAlpha` now requires a main thread marker.
+
+
+## [icrate 0.0.4] - 2023-07-31
+[icrate 0.0.4]: https://github.com/madsmtm/objc2/compare/frameworks-0.2.0-icrate-0.0.3...frameworks-0.2.0-icrate-0.0.4
+
+### Changed
+* **BREAKING**: Updated `block2` to `v0.3.0`.
+
+### Fixed
+* Documentation on docs.rs.
+
+
+## [icrate 0.0.3] - 2023-06-20
+[icrate 0.0.3]: https://github.com/madsmtm/objc2/compare/frameworks-0.2.0-icrate-0.0.2...frameworks-0.2.0-icrate-0.0.3
+
+### Added
+* Added the following frameworks:
+ - `HealthKit`
+ - `MediaPlayer`
+ - `MetricKit`
+ - `PhotoKit`
+* Added `NSCopying` and `NSMutableCopying` implementations for the classes
+ that implement those protocols.
+* Added the following methods:
+ - `NSArray::get_retained`
+ - `NSArray::first_retained`
+ - `NSArray::last_retained`
+ - `NSSet::get_retained`
+ - `NSSet::to_vec`
+ - `NSSet::to_vec_retained`
+ - `NSDictionary::get_retained`
+ - `NSDictionary::keys_retained`
+ - `NSDictionary::values_retained`
+* Added `MainThreadMarker::alloc` for allocating objects that need to be so on
+ the main thread.
+* Added automatically generated `new`/`init` methods for all types.
+* Added `FromIterator` impls for various collection types.
+
+### Changed
+* **BREAKING**: Renamed the `from_slice` method on `NSArray`, `NSSet`,
+ `NSMutableArray` and `NSMutableSet` to `from_id_slice`, and provided a new
+ `from_slice` method that takes `&[&T]` instead.
+* **BREAKING**: Changed `NSMutableArray::replace` to return an `Result` in
+ case the index was out of bounds.
+* **BREAKING**: Changed `NSMutableArray::remove` to return an `Option` in case
+ the index was out of bounds.
+* **BREAKING**: Removed ownership parameter from generic types, since the
+ ownership/mutability information is now stored in `ClassType::Mutability`.
+* **BREAKING**: Renamed `NSMutableCopying::mutable_copy` to `::mutableCopy`.
+* **BREAKING**: The default value for `NSUUID` was changed from a nil UUID to
+ a new random UUID.
+* **BREAKING**: Changed how iteration works.
+
+ Instead of the single `NSFastEnumerator`, we now have concrete types
+ `array::Iter`, `array::IterMut`, `array::IterRetained` and
+ `array::IntoIter`, which allows iterating over `NSArray` in different ways.
+
+ Combined with proper `IntoIterator` implementations for collection types,
+ you can now do:
+ ```rust, ignore
+ let mut array: Id<NSMutableArray<T>> = ...;
+
+ for item in &array {
+ // item: &T
+ }
+
+ // If T: IsMutable
+ for item in &mut array {
+ // item: &mut T
+ }
+
+ // If T: IsIdCloneable
+ for item in array.iter_retained() {
+ // item: Id<T>
+ }
+
+ for item in array {
+ // item: Id<T>
+ }
+ ```
+
+ (similar functionality exist for `NSSet` and `NSDictionary`).
+* **BREAKING**: Renamed `NSDictionary` methods:
+ - `keys` -> `keys_vec`.
+ - `values` -> `values_vec`.
+ - `values_mut` -> `values_vec_mut`.
+ - `keys_and_objects` -> `to_vecs`.
+ - `iter_keys` -> `keys`.
+ - `iter_values` -> `values`.
+* **BREAKING**: `NSDictionary::keys_retained` and
+ `NSDictionary::values_retained` now return an iterator instead.
+* **BREAKING**: Updated `objc2` to `v0.4.0`.
+* **BREAKING**: Updated `block2` to `v0.2.0`.
+
+### Removed
+* **BREAKING**: Removed various redundant `NSProxy` methods.
+* **BREAKING**: Removed `NSArray::to_shared_vec` and `NSArray::into_vec`, use
+ `NSArray::to_vec` or `NSArray::to_vec_retained` instead.
+* **BREAKING**: Removed associated types from `NSCopying` and
+ `NSMutableCopying`, that information is now specified in
+ `ClassType::Mutability` instead.
+* **BREAKING**: Removed a few `init` methods on subclasses that were declared
+ on categories on their superclass. These should be re-added at some point.
+
+### Fixed
+* Soundness issues with enumeration / iteration over collection types.
+
+
+## [icrate 0.0.2] - 2023-02-07
+[icrate 0.0.2]: https://github.com/madsmtm/objc2/compare/frameworks-0.2.0-icrate-0.0.1...frameworks-0.2.0-icrate-0.0.2
+
+### Added
+* Added the following frameworks:
+ - `Accessibility`
+ - `AdServices`
+ - `AdSupport`
+ - `AutomaticAssessmentConfiguration`
+ - `Automator`
+ - `BackgroundAssets`
+ - `BackgroundTasks`
+ - `BusinessChat`
+ - `CallKit`
+ - `ClassKit`
+ - `CloudKit`
+ - `Contacts`
+ - `CoreLocation`
+ - `DataDetection`
+ - `DeviceCheck`
+ - `EventKit`
+ - `ExceptionHandling`
+ - `ExtensionKit`
+ - `ExternalAccessory`
+ - `FileProvider`
+ - `FileProviderUI`
+ - `GameController`
+ - `GameKit`
+ - `IdentityLookup`
+ - `InputMethodKit`
+ - `LinkPresentation`
+ - `LocalAuthentication`
+ - `LocalAuthenticationEmbeddedUI`
+ - `MailKit`
+ - `MapKit`
+ - `Metal`
+ - `MetalFX`
+ - `MetalKit`
+ - `OSAKit`
+ - `CoreAnimation` (also known as `QuartzCore`)
+ - `SoundAnalysis`
+ - `Speech`
+ - `StoreKit`
+ - `UniformTypeIdentifiers`
+ - `UserNotifications`
+ - `WebKit`
+* Updated the SDK version from XCode `14.0.1` to `14.2`.
+ - See differences [here](https://sdk.news/macOS-13.0/).
+* Added `Foundation::MainThreadBound` helper struct for things that are only
+ accessible from the main thread.
+* Added `#[deprecated]` annotations to the autogenerated output.
+* Added disambiguation for duplicated methods (e.g. `NSThread::threadPriority`
+ vs. `NSThread::threadPriority_class`).
+* Properly implemented protocols for defined classes.
+
+### Changed
+* Cfg-gated static `ns_string!` functionality behind the
+ `unstable-static-nsstring` cargo feature.
+* Autogenerated method parameters are now in snake-case, for better IDE
+ support.
+* **BREAKING**: Cfg-gate all classes, and everything that references said
+ classes.
+
+ This means that to use e.g. `Foundation::NSThread::name`, you have to enable
+ the `Foundation_NSThread` and `Foundation_NSString` cargo features.
+* **BREAKING**: Updated `objc2` to `v0.3.0-beta.5`.
+* **BREAKING**: Updated `block2` to `v0.2.0-alpha.8`.
+
+### Removed
+* **BREAKING**: The optional `uuid` integration, since one might want to use
+ `icrate` internally in that crate in the future, and that would break.
+* **BREAKING**: Removed `NSNib::instantiateWithOwner_topLevelObjects`,
+ `NSBundle::loadNibNamed_owner_topLevelObjects` and `NSFreeMapTable` since
+ they had weird memory management.
+
+### Fixed
+* Ensure we never hit a memory management issue again.
+* **BREAKING**: Fixed a few `*mut` pointers that should've been `*const`.
+* **BREAKING**: Fixed a few generic ownership parameters that defaulted to
+ `Shared`.
+* Removed a few instances of `TodoProtocols`.
+* Fixed type-encoding of a few `struct`s.
+* Fixed `NSProxy` trait methods.
+* **BREAKING**: Fixed type in methods that worked with out-parameters.
+
+
+## [icrate 0.0.1] - 2022-12-24
+[icrate 0.0.1]: https://github.com/madsmtm/objc2/compare/frameworks-0.2.0-objc2-0.3.0-beta.3...frameworks-0.2.0-icrate-0.0.1
+
+### Added
+* Added `NSString::write_to_file`.
+* Added `NSLock` class and `NSLocking` protocol.
+* Added autogenerated implementations of the following frameworks:
+ - `AppKit`
+ - `AuthenticationServices`
+ - `CoreData`
+ - `Foundation`
+
+### Changed
+* **BREAKING**: Moved from `objc2::foundation` into `icrate::Foundation`.
+* **BREAKING**: Changed the following methods:
+ - `NSString`
+ - `concat` -> `stringByAppendingString`
+ - `join_path` -> `stringByAppendingPathComponent`
+ - `has_prefix` -> `hasPrefix`
+ - `has_suffix` -> `hasSuffix`
+ - `NSMutableString`
+ - `from_nsstring` -> `stringWithString`
+ - `with_capacity` -> `stringWithCapacity`
+ - `push_nsstring` -> `appendString`
+ - `replace` -> `setString`
+ - `NSAttributedString`
+ - `init_with_attributes` -> `unsafe initWithString_attributes`
+ - `init_with_string` -> `initWithString`
+ - `new_with_attributes` -> `unsafe new_with_attributes`
+ - `len_utf16` -> `length`
+ - `NSMutableAttributedString`
+ - `replace` -> `setAttributedString`
+ - `NSBundle`
+ - `main` -> `mainBundle`
+ - `info` -> `infoDictionary`
+ - `NSDictionary`
+ - `keys_array` -> `allKeys`
+ - `into_values_array` -> `allValues`
+ - `NSMutableDictionary`
+ - `clear` -> `removeAllObjects`
+ - `NSMutableArray`
+ - `clear` -> `removeAllObjects`
+ - `NSMutableSet`
+ - `clear` -> `removeAllObjects`
+ - `NSError`
+ - `user_info` -> `userInfo`
+ - `localized_description` -> `localizedDescription`
+ - `NSException`
+ - `user_info` -> `userInfo`
+ - `NSMutableData`
+ - `from_data` -> `dataWithData`
+ - `with_capacity` -> `dataWithCapacity`
+ - `set_len` -> `setLength`
+ - `NSUUID`
+ - `new_v4` -> `UUID`
+ - `string` -> `UUIDString`
+ - `NSThread`
+ - `current` -> `currentThread`
+ - `main` -> `mainThread`
+ - `is_main` -> `isMainThread`
+ - `NSProcessInfo`
+ - `process_info` -> `processInfo`
+* **BREAKING**: Make `NSComparisonResult` work like all other enums.
+* **BREAKING**: Changed `NSDictionary` to be `Shared` by default.
+* **BREAKING** (TEMPORARY): Renamed `NSEnumerator`, `NSFastEnumeration` and
+ `NSFastEnumerator` until the story around them are properly figured out.
+* **BREAKING**: Make `NSArray::objects_in_range` return an `Option` (it was
+ unsound before).
+
+### Fixed
+* Fixed `NSZone` not specifying a `#[repr(...)]`.
+
+
+## [objc2::foundation 0.3.0-beta.3] - 2022-09-01
+[objc2::foundation 0.3.0-beta.3]: https://github.com/madsmtm/objc2/compare/frameworks-0.2.0-objc2-0.3.0-beta.2...frameworks-0.2.0-objc2-0.3.0-beta.3
+
+### Added
+* Added `NSSet`.
+* Added `NSMutableSet`.
+* Added `NSMutableDictionary`.
+* Added `NSNotFound`.
+* Added `NSBundle`.
+* Added `NSTimeInterval`.
+* Added `NSString::len_utf16` and `NSAttributedString::len_utf16`.
+* Added `NSString::concat` and `NSString::join_path`.
+* Added `CGSize`, `CGPoint` and `CGRect` (aliases to equivalent `NS`-types
+ that helps readability).
+
+### Changed
+* **BREAKING**: `NSSize::new` no longer requires it's arguments to be
+ non-negative. Use `NSSize::abs` or `NSRect::standardize` if the API you're
+ binding to requires a non-negative size.
+
+
+## [objc2::foundation 0.3.0-beta.2] - 2022-08-28
+[objc2::foundation 0.3.0-beta.2]: https://github.com/madsmtm/objc2/compare/frameworks-0.2.0-alpha.6...frameworks-0.2.0-objc2-0.3.0-beta.2
+
+### Added
+* Added `NSNumber`.
+* Added `NSError`.
+* Implement `UnwindSafe` and `RefUnwindSafe` for all objects.
+* Implemented `IntoIterator` for references to `NSArray`, `NSMutableArray`,
+ `NSData` and `NSMutableData`.
+* Implemented `Extend` for `NSMutableArray`.
+* Add extra `Extend<&u8>` impl for `NSMutableData`.
+* Added function `NSValue::contains_encoding` for determining if the encoding
+ of the `NSValue` matches the encoding of the given type.
+* Added functions `get_range`, `get_point`, `get_size` and `get_rect` to
+ `NSValue` to help safely returning various types it will commonly contain.
+* `NSArray` and `NSMutableArray` now have sensible defaults for the ownership
+ of the objects they contain.
+
+### Changed
+* **BREAKING**: Moved from external crate `objc2_foundation` into
+ `objc2::foundation`.
+* **BREAKING**: Made `NSValue` not generic any more. While we loose some
+ type-safety from this, it makes `NSValue` much more useful in the real
+ world!
+* **BREAKING**: Made `NSArray::new` generic over ownership.
+* **BREAKING**: Made `NSObject::is_kind_of` take a generic `T: ClassType`
+ instead of a `runtime::Class`.
+
+### Fixed
+* Made `Debug` impls for all objects print something useful.
+
+### Removed
+* `NSObject::hash_code`, `NSObject::is_equal` and `NSObject::description` in
+ favour of having the trait implementations `Hash`, `PartiqalEq` and `Debug`.
+
+
+## [0.2.0-alpha.6] - 2022-07-19
+[0.2.0-alpha.6]: https://github.com/madsmtm/objc2/compare/frameworks-0.2.0-alpha.5...frameworks-0.2.0-alpha.6
+
+### Added
+* Added `MainThreadMarker` to help with designing APIs where a method is only
+ safe to call on the main thread.
+* Added `NSException` object.
+* Added `extern_class!` macro to help with defining interfaces to classes.
+ Further changelog for this can be found in `CHANGELOG.md`.
+* Added `declare_class!` macro to help with declaring custom classes.
+ Further changelog for this can be found in `CHANGELOG.md`.
+* Expose the `objc2` version that this uses in the crate root.
+* Added `NSZone`.
+
+### Changed
+* Changed a few `Debug` impls.
+
+
+## [0.2.0-alpha.5] - 2022-06-13
+[0.2.0-alpha.5]: https://github.com/madsmtm/objc2/compare/frameworks-0.2.0-alpha.4...frameworks-0.2.0-alpha.5
+
+### Added
+* Objects now `Deref` to their superclasses. E.g. `NSMutableArray` derefs to
+ `NSArray`, which derefs to `NSObject`, which derefs to `Object`.
+
+ This allows more ergonomic usage.
+* Implement `PartialOrd` and `Ord` for `NSString` and `NSRange`.
+* Added `NSString::has_prefix` and `NSString::has_suffix`.
+* Added `NSRange` methods `new`, `is_empty`, `contains` and `end`.
+* Added `NSThread` object.
+* Added `is_multi_threaded` and `is_main_thread` helper functions.
+* Added `NSProcessInfo` object.
+* Added `NSMutableData` methods `from_data`, `with_capacity` and `push`.
+* Added `io::Write` and `iter::Extend` implementation for `NSMutableData`.
+* Added `NSUUID` object.
+* Added `NSMutableString` object.
+* Added basic `NSAttributedString` object.
+* Added basic `NSMutableAttributedString` object.
+* Added `NSInteger` and `NSUInteger` (type aliases to `isize` and `usize`).
+* Added `CGFloat`.
+* Added `NSPoint`.
+* Added `NSSize`.
+* Added `NSRect`.
+* Implement `Borrow` and `BorrowMut` for all objects.
+* Implement `ToOwned` for copyable types.
+
+### Changed
+* **BREAKING**: Removed the following helper traits in favor of inherent
+ methods on the objects themselves:
+ - `INSMutableArray`
+ - `INSArray`
+ - `INSMutableData`
+ - `INSData`
+ - `INSDictionary`
+ - `INSString`
+ - `INSValue`
+ - `INSObject`
+
+ This changed because objects now deref to their superclasses.
+* **BREAKING**: Relaxed a lot of bounds from `INSObject` to `Message`. At some
+ point in the future a new trait will be introduced which remedies this
+ change.
+* **BREAKING**: Removed the `I` prefix from:
+ - `INSCopying` (now `NSCopying`)
+ - `INSMutableCopying` (now `NSMutableCopying`)
+ - `INSFastEnumeration` (now `NSFastEnumeration`)
+* **BREAKING**: Renamed `NSMutableData::append` to `extend_from_slice`.
+
+
+## [0.2.0-alpha.4] - 2022-01-03
+[0.2.0-alpha.4]: https://github.com/madsmtm/objc2/compare/frameworks-0.2.0-alpha.3...frameworks-0.2.0-alpha.4
+
+### Added
+* Implement `PartialOrd` and `Ord` for `NSComparisonResult` and `NSValue`.
+* Implement `fmt::Display` for `NSValue`.
+* Implement `DefaultId` for relevant objects.
+* Implement `AsRef` and `Index` for `NSData` and `NSMutableData`.
+* Implement `AsMut` and `IndexMut` for `NSMutableData`.
+
+### Changed
+* **BREAKING**: Renamed `INSFastEnumeration::enumerator` to
+ `INSFastEnumeration::iter_fast`.
+
+### Removed
+* **BREAKING**: Removed `Deref` and `DerefMut` from `NSData` and
+ `NSMutableData`, since these invoke a non-trivial amount of code, and could
+ lead to hard-to-diagnose performance issues.
+
+
+## [0.2.0-alpha.3] - 2021-12-22
+[0.2.0-alpha.3]: https://github.com/madsmtm/objc2/compare/frameworks-0.2.0-alpha.2...frameworks-0.2.0-alpha.3
+
+### Added
+* **BREAKING**: Added associated `Ownership` type to `NSCopying`.
+* **BREAKING**: Added associated `Ownership` type to `INSData`.
+* **BREAKING**: Added associated `Ownership` type to `INSArray`.
+* Added common trait impls (`PartialEq`, `Eq`, `Hash` and `Debug`) to
+ `NSValue`, `NSDictionary`, `NSArray` and `NSMutableArray`.
+
+### Changed
+* **BREAKING**: Made some creation methods a bit less generic (e.g.
+ `INSDictionary::from_keys_and_objects` now always returns `Id<_, Shared>`).
+* Relax bounds on generic `INSObject` impls.
+
+### Removed
+* **BREAKING**: Removed associated `Ownership` type from `INSObject`; instead,
+ it is present on the types that actually need it (for example `NSCopying`).
+* **BREAKING**: Removed `Sized` bound on `INSObject`.
+
+### Fixed
+* Soundness issue with `NSValue`, `NSDictionary`, `NSArray` and
+ `NSMutableArray` not specifying a `#[repr(...)]`.
+* **BREAKING**: `NSObject` is no longer `Send` and `Sync` (because its
+ subclasses may not be).
+
+
+## [0.2.0-alpha.2] - 2021-11-22
+[0.2.0-alpha.2]: https://github.com/madsmtm/objc2/compare/frameworks-0.2.0-alpha.1...frameworks-0.2.0-alpha.2
+
+### Added
+* **BREAKING**: Added associated `Ownership` type to `INSObject` to specify
+ whether the type can be mutated or not. `NSString` is a prime example of a
+ type that you may never get a `Owned/&mut` reference to, since it is very
+ easy to create two `NSString`s with the same underlying allocation.
+* Added helper `is_empty` methods.
+* Added `INSArray::first_mut` and `INSArray::last_mut`.
+
+### Changed
+* **BREAKING**: Renamed a lot of methods to better match Rusts naming scheme:
+ - `INSArray`
+ - `count` -> `len`
+ - `object_at` -> `get`
+ - `mut_object_at` -> `get_mut`
+ - `shared_object_at` -> `get_retained`
+ - `first_object` -> `first`
+ - `last_object` -> `last`
+ - `object_enumerator` -> `iter`
+ - `INSMutableArray`
+ - `add_object` -> `push`
+ - `insert_object_at` -> `insert`
+ - `replace_object_at` -> `replace`
+ - `remove_object_at` -> `remove`
+ - `remove_last_object` -> `pop`
+ - `remove_all_objects` -> `clear`
+ - `INSDictionary`
+ - `count` -> `len`
+ - `object_for` -> `get`
+ - `key_enumerator` -> `iter_keys`
+ - `object_enumerator` -> `iter_values`
+ - `INSValue`
+ - `value` -> `get`
+ - `from_value` -> `new`
+ - `NSComparisonResult`
+ - `from_ordering` -> `from`
+ - `as_ordering` -> `into`
+ - `NSRange`
+ - `from_range` -> `from`
+ - `as_range` -> `into`
+* Use `SliceId` for better performance when creating arrays and dictionaries.
+
+### Removed
+* **BREAKING**: Removed the `object_struct!` macro. It may be re-added in
+ another form in the future.
+* **BREAKING**: Removed `NSMutableSharedArray<T>` and `NSSharedArray<T>` type
+ aliases. Use `NSMutableArray<T, Shared>` and `NSArray<T, Shared>` instead.
+* **BREAKING**: Removed `Any / 'static` bound on `INSObject`. This allows
+ implementing it for objects that contain lifetimes from the outer scope.
+
+### Fixed
+* **BREAKING**: Marked `INS...` traits as `unsafe` to implement.
+* **BREAKING**: Removed `new` method from `INSObject` since some classes don't
+ want this called. It has been re-added to other `INS...` traits on a case by
+ case basis (in particular not `NSValue`).
+* **BREAKING**: `INSString::as_str` now takes an a reference to
+ `objc2::rc::AutoreleasePool`. This ensure that the returned `&str` is only
+ used while the current autorelease pool is valid.
+* Fixed `NSData::from_vec` on GNUStep.
+
+
+## [0.2.0-alpha.1] - 2021-10-28
+[0.2.0-alpha.1]: https://github.com/madsmtm/objc2/compare/frameworks-0.2.0-alpha.0...frameworks-0.2.0-alpha.1
+
+### Added
+* Implement new `RefEncode` trait for objects.
+* Implement `Encode` for `NSComparisonResult` and `NSFastEnumerationState`.
+* Implement `RefEncode` for objects and `NSFastEnumerationState`.
+
+### Changed
+* **BREAKING**: Uses `Id` from `objc2::rc` module instead of `objc_id` crate.
+* **BREAKING**: `INSValue::encoding` now returns `&str` instead of `Encoding`.
+
+### Fixed
+* Use proper `#[repr(C)]` structs to represent Objective-C objects.
+* `INSString::from_str` on GNUStep (`UTF8_ENCODING` was the wrong type).
+
+
+## [0.2.0-alpha.0] - 2021-08-29
+[0.2.0-alpha.0]: https://github.com/madsmtm/objc2/compare/frameworks-0.1.1...frameworks-0.2.0-alpha.0
+
+Note: This is the version that is, as of this writing, available on the
+`master` branch in the original `objc-foundation` project.
+
+### Added
+* Implement `Display` for `NSString`.
+* Make `INSObject::class` faster using the `objc::class!` macro.
+
+### Changed
+* **BREAKING**: Forked the project, the crate name is now `objc2-foundation`.
+
+### Fixed
+* Fixed types in various calls to `objc::msg_send!` for better verification.
+
+
+## [0.1.1] (`objc2-foundation` crate) - 2016-06-19
+[0.1.1]: https://github.com/madsmtm/objc2/compare/frameworks-0.1.0...frameworks-0.1.1
+
+### Fixed
+* An issue with passing functions (instead of function pointers) in
+ `INSMutableArray::sort_by`.
+
+
+## [0.1.0] (`objc2-foundation` crate) - 2016-03-20
+[0.1.0]: https://github.com/madsmtm/objc2/compare/frameworks-0.0.4...frameworks-0.1.0
+
+### Changed
+* Update `objc` to `v0.2`.
+* Update `objc_id` to `v0.1`.
+
+
+## [0.0.4] (`objc2-foundation` crate) - 2015-12-09
+[0.0.4]: https://github.com/madsmtm/objc2/compare/frameworks-0.0.3...frameworks-0.0.4
+
+### Removed
+* `libc` dependency.
+
+
+## [0.0.3] (`objc2-foundation` crate) - 2015-11-07
+[0.0.3]: https://github.com/madsmtm/objc2/compare/frameworks-0.0.2...frameworks-0.0.3
+
+### Added
+* `object_struct!` macro.
+
+### Changed
+* `libc` version can both be `0.1` and `0.2`.
+
+
+## [0.0.2] (`objc2-foundation` crate) - 2015-09-03
+[0.0.2]: https://github.com/madsmtm/objc2/compare/frameworks-0.0.1...frameworks-0.0.2
+
+### Added
+* `Any` bound on `INSObject`, because of a change in `objc` `v0.1.6`.
+
+
+## [0.0.1] (`objc2-foundation` crate) - 2015-06-13
+[0.0.1]: https://github.com/madsmtm/objc2/releases/tag/frameworks-0.0.1
+
+Initial release.
diff --git a/third_party/rust/objc2/src/topics/about_generated/README.md b/third_party/rust/objc2/src/topics/about_generated/README.md
@@ -0,0 +1,20 @@
+# About the generated framework crates
+
+The `objc2` project provides (mostly) autogenerated interfaces to Apple's
+Objective-C frameworks like AppKit, Foundation, Metal, WebKit, and so on.
+
+The bindings currently contain some documentation from the headers, but you
+should view [Apple's canonical developer documentation][apple-doc-index] for
+detailed information about each API (there are [some plans][#309] for
+importing that documentation, but it's difficult).
+
+These crates uses `objc2` to declare the external interface to the
+Objective-C classes and protocols. It is highly recommended that you read
+the documentation here for details on how the Objective-C interop works.
+
+They also use `block2::Block` in the public API, check out the documentation
+for the [`block2`] crate for how to call such methods using a closure.
+
+[apple-doc-index]: https://developer.apple.com/documentation/technologies
+[#309]: https://github.com/madsmtm/objc2/issues/309
+[`block2`]: https://docs.rs/block2
diff --git a/third_party/rust/objc2/src/topics/about_generated/cargo_features.md b/third_party/rust/objc2/src/topics/about_generated/cargo_features.md
@@ -0,0 +1,62 @@
+# Cargo features and long compile-times in framework crates
+
+Each framework crate has a set of Cargo features that control which parts of
+it that is enabled. These are split into two categories; file and dependency
+features.
+
+**Most features are enabled by default** for ease of use for newcomers / in
+small hobby projects, but if you're developing a library for others to use, it
+is recommended that you disable them by adding `default-features = false`
+[to your `Cargo.toml`][cargo-dep-features].
+
+This can vastly help reduce the compile-time of the framework crates.
+
+[cargo-dep-features]: https://doc.rust-lang.org/cargo/reference/features.html#dependency-features
+
+
+## File features
+
+Each framework C header corresponds to one Cargo feature, and everything that
+was declared inside of that header is locked behind that Cargo feature.
+
+As an example, let's use `MetalKit`. This framework has four public C headers,
+`MTKDefines.h`, `MTKModel.h`, `MTKTextureLoader.h` and `MTKView.h`. This in
+turn means we get four Cargo features in `objc2-metal-kit`, `MTKDefines`,
+`MTKModel`, `MTKTextureLoader` and `MTKView`, that enables the functionality
+exposed by each of those headers, as well as any required dependency features
+(e.g. `MTKModel.h` uses `MTLDevice`, so `objc2-metal/MTLDevice` is enabled for
+you).
+
+
+## Dependency features
+
+As you can see above, frameworks rarely stand alone, instead they often have
+some sort of dependency on other frameworks. Some of these dependencies are
+considered required, and enabled by default (often `objc2-foundation`), while
+for some it makes sense to allow control of whether the dependency is enabled.
+
+Let's keep using `MetalKit` as the example. By default, `objc2-metal-kit` will
+import the dependencies `objc2`, `objc2-foundation` and `objc2-metal`, since
+those are central to how the `MetalKit` works.
+
+But it also has an optional dependency on `objc2-app-kit` and `block2`, since
+those are not always required; in this case, `objc2-app-kit` is only needed if
+you want to use the `MTKView` class, so it would be wasteful to enable the
+dependency if you didn't need that class.
+
+Such optional dependencies can be enabled with Cargo features of the same name
+as the dependency.
+
+
+### Linking and minimum version compatibility
+
+A few select dependency features are **not** enabled by default, as these
+would raise the minimum supported version of your application. An example is
+the `objc2-uniform-type-identifiers` dependency of the `objc2-app-kit` crate,
+which was first introduced in macOS 11.0. If you want to use capabilities from
+that framework in `objc2-app-kit`, you have to enable the feature manually.
+
+(Note that this _could_ also have been worked around by the user with the
+`-weak_framework UniformTypeIdentifiers` linker flag, but that's not very
+discoverable, so `objc2` chooses to default to making your application
+portable across the full set of versions that Rust supports).
diff --git a/third_party/rust/objc2/src/topics/about_generated/deref.md b/third_party/rust/objc2/src/topics/about_generated/deref.md
@@ -0,0 +1,23 @@
+# Use of `Deref`
+
+Framework crates uses the [`Deref`] trait in a bit special way: All objects
+deref to their superclasses. For example, `NSMutableArray` derefs to
+`NSArray`, which in turn derefs to `NSObject`.
+
+Note that this is explicitly recommended against in [the
+documentation][`Deref`] and [the Rust Design patterns
+book][anti-pattern-deref] (see those links for details).
+
+Due to Objective-C objects only ever being accessible behind pointers in
+the first place, the problems stated there are less severe, and having the
+implementation just means that everything is much nicer when you actually
+want to use the objects!
+
+All objects also implement [`AsRef`] to their superclass, and can be used in
+[`Retained::into_super`], so if you favour explicit conversion, that is a
+possibility too.
+
+[`Deref`]: std::ops::Deref
+[`ClassType`]: crate::ClassType
+[anti-pattern-deref]: https://rust-unofficial.github.io/patterns/anti_patterns/deref.html
+[`Retained::into_super`]: crate::rc::Retained::into_super
diff --git a/third_party/rust/objc2/src/topics/about_generated/list.rs b/third_party/rust/objc2/src/topics/about_generated/list.rs
@@ -0,0 +1,16 @@
+//! # List of framework crates
+//!
+//! The following is a full list of all supported Apple frameworks, and the
+//! corresponding Rust crate.
+//!
+#![doc = include_str!("list_data.md")]
+//!
+//! ## Unsupported
+//!
+//! Unsupported frameworks are listed below, feel free to [open an issue] if a
+//! framework that you need isn't supported, or if you disagree with the given
+//! reasoning.
+//!
+//! [open an issue]: https://github.com/madsmtm/objc2/issues/new
+//!
+#![doc = include_str!("list_unsupported.md")]
diff --git a/third_party/rust/objc2/src/topics/about_generated/list_data.md b/third_party/rust/objc2/src/topics/about_generated/list_data.md
@@ -0,0 +1,182 @@
+| Framework | Crate | Docs.rs |
+| --- | --- | --- |
+| `ARKit` | [`objc2-ar-kit`](https://crates.io/crates/objc2-ar-kit) | [](https://docs.rs/objc2-ar-kit/) |
+| `AVFAudio` | [`objc2-avf-audio`](https://crates.io/crates/objc2-avf-audio) | [](https://docs.rs/objc2-avf-audio/) |
+| `AVFoundation` | [`objc2-av-foundation`](https://crates.io/crates/objc2-av-foundation) | [](https://docs.rs/objc2-av-foundation/) |
+| `AVKit` | [`objc2-av-kit`](https://crates.io/crates/objc2-av-kit) | [](https://docs.rs/objc2-av-kit/) |
+| `AVRouting` | [`objc2-av-routing`](https://crates.io/crates/objc2-av-routing) | [](https://docs.rs/objc2-av-routing/) |
+| `Accessibility` | [`objc2-accessibility`](https://crates.io/crates/objc2-accessibility) | [](https://docs.rs/objc2-accessibility/) |
+| `AccessorySetupKit` | [`objc2-accessory-setup-kit`](https://crates.io/crates/objc2-accessory-setup-kit) | [](https://docs.rs/objc2-accessory-setup-kit/) |
+| `Accounts` | [`objc2-accounts`](https://crates.io/crates/objc2-accounts) | [](https://docs.rs/objc2-accounts/) |
+| `AdServices` | [`objc2-ad-services`](https://crates.io/crates/objc2-ad-services) | [](https://docs.rs/objc2-ad-services/) |
+| `AdSupport` | [`objc2-ad-support`](https://crates.io/crates/objc2-ad-support) | [](https://docs.rs/objc2-ad-support/) |
+| `AppClip` | [`objc2-app-clip`](https://crates.io/crates/objc2-app-clip) | [](https://docs.rs/objc2-app-clip/) |
+| `AppKit` | [`objc2-app-kit`](https://crates.io/crates/objc2-app-kit) | [](https://docs.rs/objc2-app-kit/) |
+| `AppTrackingTransparency` | [`objc2-app-tracking-transparency`](https://crates.io/crates/objc2-app-tracking-transparency) | [](https://docs.rs/objc2-app-tracking-transparency/) |
+| `ApplicationServices` | [`objc2-application-services`](https://crates.io/crates/objc2-application-services) | [](https://docs.rs/objc2-application-services/) |
+| `AudioToolbox` | [`objc2-audio-toolbox`](https://crates.io/crates/objc2-audio-toolbox) | [](https://docs.rs/objc2-audio-toolbox/) |
+| `AuthenticationServices` | [`objc2-authentication-services`](https://crates.io/crates/objc2-authentication-services) | [](https://docs.rs/objc2-authentication-services/) |
+| `AutomaticAssessmentConfiguration` | [`objc2-automatic-assessment-configuration`](https://crates.io/crates/objc2-automatic-assessment-configuration) | [](https://docs.rs/objc2-automatic-assessment-configuration/) |
+| `Automator` | [`objc2-automator`](https://crates.io/crates/objc2-automator) | [](https://docs.rs/objc2-automator/) |
+| `BackgroundAssets` | [`objc2-background-assets`](https://crates.io/crates/objc2-background-assets) | [](https://docs.rs/objc2-background-assets/) |
+| `BackgroundTasks` | [`objc2-background-tasks`](https://crates.io/crates/objc2-background-tasks) | [](https://docs.rs/objc2-background-tasks/) |
+| `BrowserEngineCore` | [`objc2-browser-engine-core`](https://crates.io/crates/objc2-browser-engine-core) | [](https://docs.rs/objc2-browser-engine-core/) |
+| `BrowserEngineKit` | [`objc2-browser-engine-kit`](https://crates.io/crates/objc2-browser-engine-kit) | [](https://docs.rs/objc2-browser-engine-kit/) |
+| `BusinessChat` | [`objc2-business-chat`](https://crates.io/crates/objc2-business-chat) | [](https://docs.rs/objc2-business-chat/) |
+| `CFNetwork` | [`objc2-cf-network`](https://crates.io/crates/objc2-cf-network) | [](https://docs.rs/objc2-cf-network/) |
+| `CallKit` | [`objc2-call-kit`](https://crates.io/crates/objc2-call-kit) | [](https://docs.rs/objc2-call-kit/) |
+| `CarPlay` | [`objc2-car-play`](https://crates.io/crates/objc2-car-play) | [](https://docs.rs/objc2-car-play/) |
+| `Carbon` | [`objc2-carbon`](https://crates.io/crates/objc2-carbon) | [](https://docs.rs/objc2-carbon/) |
+| `Cinematic` | [`objc2-cinematic`](https://crates.io/crates/objc2-cinematic) | [](https://docs.rs/objc2-cinematic/) |
+| `ClassKit` | [`objc2-class-kit`](https://crates.io/crates/objc2-class-kit) | [](https://docs.rs/objc2-class-kit/) |
+| `ClockKit` | [`objc2-clock-kit`](https://crates.io/crates/objc2-clock-kit) | [](https://docs.rs/objc2-clock-kit/) |
+| `CloudKit` | [`objc2-cloud-kit`](https://crates.io/crates/objc2-cloud-kit) | [](https://docs.rs/objc2-cloud-kit/) |
+| `Collaboration` | [`objc2-collaboration`](https://crates.io/crates/objc2-collaboration) | [](https://docs.rs/objc2-collaboration/) |
+| `ColorSync` | [`objc2-color-sync`](https://crates.io/crates/objc2-color-sync) | [](https://docs.rs/objc2-color-sync/) |
+| `CompositorServices` | [`objc2-compositor-services`](https://crates.io/crates/objc2-compositor-services) | [](https://docs.rs/objc2-compositor-services/) |
+| `Contacts` | [`objc2-contacts`](https://crates.io/crates/objc2-contacts) | [](https://docs.rs/objc2-contacts/) |
+| `ContactsUI` | [`objc2-contacts-ui`](https://crates.io/crates/objc2-contacts-ui) | [](https://docs.rs/objc2-contacts-ui/) |
+| `CoreAudio` | [`objc2-core-audio`](https://crates.io/crates/objc2-core-audio) | [](https://docs.rs/objc2-core-audio/) |
+| `CoreAudioKit` | [`objc2-core-audio-kit`](https://crates.io/crates/objc2-core-audio-kit) | [](https://docs.rs/objc2-core-audio-kit/) |
+| `CoreAudioTypes` | [`objc2-core-audio-types`](https://crates.io/crates/objc2-core-audio-types) | [](https://docs.rs/objc2-core-audio-types/) |
+| `CoreBluetooth` | [`objc2-core-bluetooth`](https://crates.io/crates/objc2-core-bluetooth) | [](https://docs.rs/objc2-core-bluetooth/) |
+| `CoreData` | [`objc2-core-data`](https://crates.io/crates/objc2-core-data) | [](https://docs.rs/objc2-core-data/) |
+| `CoreFoundation` | [`objc2-core-foundation`](https://crates.io/crates/objc2-core-foundation) | [](https://docs.rs/objc2-core-foundation/) |
+| `CoreGraphics` | [`objc2-core-graphics`](https://crates.io/crates/objc2-core-graphics) | [](https://docs.rs/objc2-core-graphics/) |
+| `CoreHaptics` | [`objc2-core-haptics`](https://crates.io/crates/objc2-core-haptics) | [](https://docs.rs/objc2-core-haptics/) |
+| `CoreImage` | [`objc2-core-image`](https://crates.io/crates/objc2-core-image) | [](https://docs.rs/objc2-core-image/) |
+| `CoreLocation` | [`objc2-core-location`](https://crates.io/crates/objc2-core-location) | [](https://docs.rs/objc2-core-location/) |
+| `CoreLocationUI` | [`objc2-core-location-ui`](https://crates.io/crates/objc2-core-location-ui) | [](https://docs.rs/objc2-core-location-ui/) |
+| `CoreMIDI` | [`objc2-core-midi`](https://crates.io/crates/objc2-core-midi) | [](https://docs.rs/objc2-core-midi/) |
+| `CoreML` | [`objc2-core-ml`](https://crates.io/crates/objc2-core-ml) | [](https://docs.rs/objc2-core-ml/) |
+| `CoreMedia` | [`objc2-core-media`](https://crates.io/crates/objc2-core-media) | [](https://docs.rs/objc2-core-media/) |
+| `CoreMediaIO` | [`objc2-core-media-io`](https://crates.io/crates/objc2-core-media-io) | [](https://docs.rs/objc2-core-media-io/) |
+| `CoreMotion` | [`objc2-core-motion`](https://crates.io/crates/objc2-core-motion) | [](https://docs.rs/objc2-core-motion/) |
+| `CoreNFC` | [`objc2-core-nfc`](https://crates.io/crates/objc2-core-nfc) | [](https://docs.rs/objc2-core-nfc/) |
+| `CoreServices` | [`objc2-core-services`](https://crates.io/crates/objc2-core-services) | [](https://docs.rs/objc2-core-services/) |
+| `CoreSpotlight` | [`objc2-core-spotlight`](https://crates.io/crates/objc2-core-spotlight) | [](https://docs.rs/objc2-core-spotlight/) |
+| `CoreTelephony` | [`objc2-core-telephony`](https://crates.io/crates/objc2-core-telephony) | [](https://docs.rs/objc2-core-telephony/) |
+| `CoreText` | [`objc2-core-text`](https://crates.io/crates/objc2-core-text) | [](https://docs.rs/objc2-core-text/) |
+| `CoreVideo` | [`objc2-core-video`](https://crates.io/crates/objc2-core-video) | [](https://docs.rs/objc2-core-video/) |
+| `CoreWLAN` | [`objc2-core-wlan`](https://crates.io/crates/objc2-core-wlan) | [](https://docs.rs/objc2-core-wlan/) |
+| `CryptoTokenKit` | [`objc2-crypto-token-kit`](https://crates.io/crates/objc2-crypto-token-kit) | [](https://docs.rs/objc2-crypto-token-kit/) |
+| `DataDetection` | [`objc2-data-detection`](https://crates.io/crates/objc2-data-detection) | [](https://docs.rs/objc2-data-detection/) |
+| `DeviceCheck` | [`objc2-device-check`](https://crates.io/crates/objc2-device-check) | [](https://docs.rs/objc2-device-check/) |
+| `DeviceDiscoveryExtension` | [`objc2-device-discovery-extension`](https://crates.io/crates/objc2-device-discovery-extension) | [](https://docs.rs/objc2-device-discovery-extension/) |
+| `DiskArbitration` | [`objc2-disk-arbitration`](https://crates.io/crates/objc2-disk-arbitration) | [](https://docs.rs/objc2-disk-arbitration/) |
+| `EventKit` | [`objc2-event-kit`](https://crates.io/crates/objc2-event-kit) | [](https://docs.rs/objc2-event-kit/) |
+| `EventKitUI` | [`objc2-event-kit-ui`](https://crates.io/crates/objc2-event-kit-ui) | [](https://docs.rs/objc2-event-kit-ui/) |
+| `ExceptionHandling` | [`objc2-exception-handling`](https://crates.io/crates/objc2-exception-handling) | [](https://docs.rs/objc2-exception-handling/) |
+| `ExecutionPolicy` | [`objc2-execution-policy`](https://crates.io/crates/objc2-execution-policy) | [](https://docs.rs/objc2-execution-policy/) |
+| `ExposureNotification` | [`objc2-exposure-notification`](https://crates.io/crates/objc2-exposure-notification) | [](https://docs.rs/objc2-exposure-notification/) |
+| `ExtensionKit` | [`objc2-extension-kit`](https://crates.io/crates/objc2-extension-kit) | [](https://docs.rs/objc2-extension-kit/) |
+| `ExternalAccessory` | [`objc2-external-accessory`](https://crates.io/crates/objc2-external-accessory) | [](https://docs.rs/objc2-external-accessory/) |
+| `FSKit` | [`objc2-fs-kit`](https://crates.io/crates/objc2-fs-kit) | [](https://docs.rs/objc2-fs-kit/) |
+| `FileProvider` | [`objc2-file-provider`](https://crates.io/crates/objc2-file-provider) | [](https://docs.rs/objc2-file-provider/) |
+| `FileProviderUI` | [`objc2-file-provider-ui`](https://crates.io/crates/objc2-file-provider-ui) | [](https://docs.rs/objc2-file-provider-ui/) |
+| `FinderSync` | [`objc2-finder-sync`](https://crates.io/crates/objc2-finder-sync) | [](https://docs.rs/objc2-finder-sync/) |
+| `Foundation` | [`objc2-foundation`](https://crates.io/crates/objc2-foundation) | [](https://docs.rs/objc2-foundation/) |
+| `GameController` | [`objc2-game-controller`](https://crates.io/crates/objc2-game-controller) | [](https://docs.rs/objc2-game-controller/) |
+| `GameKit` | [`objc2-game-kit`](https://crates.io/crates/objc2-game-kit) | [](https://docs.rs/objc2-game-kit/) |
+| `GameplayKit` | [`objc2-gameplay-kit`](https://crates.io/crates/objc2-gameplay-kit) | [](https://docs.rs/objc2-gameplay-kit/) |
+| `HealthKit` | [`objc2-health-kit`](https://crates.io/crates/objc2-health-kit) | [](https://docs.rs/objc2-health-kit/) |
+| `HealthKitUI` | [`objc2-health-kit-ui`](https://crates.io/crates/objc2-health-kit-ui) | [](https://docs.rs/objc2-health-kit-ui/) |
+| `HomeKit` | [`objc2-home-kit`](https://crates.io/crates/objc2-home-kit) | [](https://docs.rs/objc2-home-kit/) |
+| `IOBluetooth` | [`objc2-io-bluetooth`](https://crates.io/crates/objc2-io-bluetooth) | [](https://docs.rs/objc2-io-bluetooth/) |
+| `IOBluetoothUI` | [`objc2-io-bluetooth-ui`](https://crates.io/crates/objc2-io-bluetooth-ui) | [](https://docs.rs/objc2-io-bluetooth-ui/) |
+| `IOKit` | [`objc2-io-kit`](https://crates.io/crates/objc2-io-kit) | [](https://docs.rs/objc2-io-kit/) |
+| `IOSurface` | [`objc2-io-surface`](https://crates.io/crates/objc2-io-surface) | [](https://docs.rs/objc2-io-surface/) |
+| `IOUSBHost` | [`objc2-io-usb-host`](https://crates.io/crates/objc2-io-usb-host) | [](https://docs.rs/objc2-io-usb-host/) |
+| `IdentityLookup` | [`objc2-identity-lookup`](https://crates.io/crates/objc2-identity-lookup) | [](https://docs.rs/objc2-identity-lookup/) |
+| `IdentityLookupUI` | [`objc2-identity-lookup-ui`](https://crates.io/crates/objc2-identity-lookup-ui) | [](https://docs.rs/objc2-identity-lookup-ui/) |
+| `ImageCaptureCore` | [`objc2-image-capture-core`](https://crates.io/crates/objc2-image-capture-core) | [](https://docs.rs/objc2-image-capture-core/) |
+| `ImageIO` | [`objc2-image-io`](https://crates.io/crates/objc2-image-io) | [](https://docs.rs/objc2-image-io/) |
+| `InputMethodKit` | [`objc2-input-method-kit`](https://crates.io/crates/objc2-input-method-kit) | [](https://docs.rs/objc2-input-method-kit/) |
+| `Intents` | [`objc2-intents`](https://crates.io/crates/objc2-intents) | [](https://docs.rs/objc2-intents/) |
+| `IntentsUI` | [`objc2-intents-ui`](https://crates.io/crates/objc2-intents-ui) | [](https://docs.rs/objc2-intents-ui/) |
+| `JavaScriptCore` | [`objc2-javascript-core`](https://crates.io/crates/objc2-javascript-core) | [](https://docs.rs/objc2-javascript-core/) |
+| `LatentSemanticMapping` | [`objc2-latent-semantic-mapping`](https://crates.io/crates/objc2-latent-semantic-mapping) | [](https://docs.rs/objc2-latent-semantic-mapping/) |
+| `LinkPresentation` | [`objc2-link-presentation`](https://crates.io/crates/objc2-link-presentation) | [](https://docs.rs/objc2-link-presentation/) |
+| `LocalAuthentication` | [`objc2-local-authentication`](https://crates.io/crates/objc2-local-authentication) | [](https://docs.rs/objc2-local-authentication/) |
+| `LocalAuthenticationEmbeddedUI` | [`objc2-local-authentication-embedded-ui`](https://crates.io/crates/objc2-local-authentication-embedded-ui) | [](https://docs.rs/objc2-local-authentication-embedded-ui/) |
+| `MLCompute` | [`objc2-ml-compute`](https://crates.io/crates/objc2-ml-compute) | [](https://docs.rs/objc2-ml-compute/) |
+| `MailKit` | [`objc2-mail-kit`](https://crates.io/crates/objc2-mail-kit) | [](https://docs.rs/objc2-mail-kit/) |
+| `MapKit` | [`objc2-map-kit`](https://crates.io/crates/objc2-map-kit) | [](https://docs.rs/objc2-map-kit/) |
+| `MediaAccessibility` | [`objc2-media-accessibility`](https://crates.io/crates/objc2-media-accessibility) | [](https://docs.rs/objc2-media-accessibility/) |
+| `MediaExtension` | [`objc2-media-extension`](https://crates.io/crates/objc2-media-extension) | [](https://docs.rs/objc2-media-extension/) |
+| `MediaPlayer` | [`objc2-media-player`](https://crates.io/crates/objc2-media-player) | [](https://docs.rs/objc2-media-player/) |
+| `MediaSetup` | [`objc2-media-setup`](https://crates.io/crates/objc2-media-setup) | [](https://docs.rs/objc2-media-setup/) |
+| `MediaToolbox` | [`objc2-media-toolbox`](https://crates.io/crates/objc2-media-toolbox) | [](https://docs.rs/objc2-media-toolbox/) |
+| `MessageUI` | [`objc2-message-ui`](https://crates.io/crates/objc2-message-ui) | [](https://docs.rs/objc2-message-ui/) |
+| `Messages` | [`objc2-messages`](https://crates.io/crates/objc2-messages) | [](https://docs.rs/objc2-messages/) |
+| `Metal` | [`objc2-metal`](https://crates.io/crates/objc2-metal) | [](https://docs.rs/objc2-metal/) |
+| `MetalFX` | [`objc2-metal-fx`](https://crates.io/crates/objc2-metal-fx) | [](https://docs.rs/objc2-metal-fx/) |
+| `MetalKit` | [`objc2-metal-kit`](https://crates.io/crates/objc2-metal-kit) | [](https://docs.rs/objc2-metal-kit/) |
+| `MetalPerformanceShaders` | [`objc2-metal-performance-shaders`](https://crates.io/crates/objc2-metal-performance-shaders) | [](https://docs.rs/objc2-metal-performance-shaders/) |
+| `MetalPerformanceShadersGraph` | [`objc2-metal-performance-shaders-graph`](https://crates.io/crates/objc2-metal-performance-shaders-graph) | [](https://docs.rs/objc2-metal-performance-shaders-graph/) |
+| `MetricKit` | [`objc2-metric-kit`](https://crates.io/crates/objc2-metric-kit) | [](https://docs.rs/objc2-metric-kit/) |
+| `ModelIO` | [`objc2-model-io`](https://crates.io/crates/objc2-model-io) | [](https://docs.rs/objc2-model-io/) |
+| `MultipeerConnectivity` | [`objc2-multipeer-connectivity`](https://crates.io/crates/objc2-multipeer-connectivity) | [](https://docs.rs/objc2-multipeer-connectivity/) |
+| `NaturalLanguage` | [`objc2-natural-language`](https://crates.io/crates/objc2-natural-language) | [](https://docs.rs/objc2-natural-language/) |
+| `NearbyInteraction` | [`objc2-nearby-interaction`](https://crates.io/crates/objc2-nearby-interaction) | [](https://docs.rs/objc2-nearby-interaction/) |
+| `NetworkExtension` | [`objc2-network-extension`](https://crates.io/crates/objc2-network-extension) | [](https://docs.rs/objc2-network-extension/) |
+| `NotificationCenter` | [`objc2-notification-center`](https://crates.io/crates/objc2-notification-center) | [](https://docs.rs/objc2-notification-center/) |
+| `OSAKit` | [`objc2-osa-kit`](https://crates.io/crates/objc2-osa-kit) | [](https://docs.rs/objc2-osa-kit/) |
+| `OSLog` | [`objc2-os-log`](https://crates.io/crates/objc2-os-log) | [](https://docs.rs/objc2-os-log/) |
+| `OpenDirectory` | [`objc2-open-directory`](https://crates.io/crates/objc2-open-directory) | [](https://docs.rs/objc2-open-directory/) |
+| `PDFKit` | [`objc2-pdf-kit`](https://crates.io/crates/objc2-pdf-kit) | [](https://docs.rs/objc2-pdf-kit/) |
+| `PHASE` | [`objc2-phase`](https://crates.io/crates/objc2-phase) | [](https://docs.rs/objc2-phase/) |
+| `ParavirtualizedGraphics` | [`objc2-paravirtualized-graphics`](https://crates.io/crates/objc2-paravirtualized-graphics) | [](https://docs.rs/objc2-paravirtualized-graphics/) |
+| `PassKit` | [`objc2-pass-kit`](https://crates.io/crates/objc2-pass-kit) | [](https://docs.rs/objc2-pass-kit/) |
+| `PencilKit` | [`objc2-pencil-kit`](https://crates.io/crates/objc2-pencil-kit) | [](https://docs.rs/objc2-pencil-kit/) |
+| `Photos` | [`objc2-photos`](https://crates.io/crates/objc2-photos) | [](https://docs.rs/objc2-photos/) |
+| `PhotosUI` | [`objc2-photos-ui`](https://crates.io/crates/objc2-photos-ui) | [](https://docs.rs/objc2-photos-ui/) |
+| `PreferencePanes` | [`objc2-preference-panes`](https://crates.io/crates/objc2-preference-panes) | [](https://docs.rs/objc2-preference-panes/) |
+| `PushKit` | [`objc2-push-kit`](https://crates.io/crates/objc2-push-kit) | [](https://docs.rs/objc2-push-kit/) |
+| `PushToTalk` | [`objc2-push-to-talk`](https://crates.io/crates/objc2-push-to-talk) | [](https://docs.rs/objc2-push-to-talk/) |
+| `Quartz` | [`objc2-quartz`](https://crates.io/crates/objc2-quartz) | [](https://docs.rs/objc2-quartz/) |
+| `QuartzCore` | [`objc2-quartz-core`](https://crates.io/crates/objc2-quartz-core) | [](https://docs.rs/objc2-quartz-core/) |
+| `QuickLook` | [`objc2-quick-look`](https://crates.io/crates/objc2-quick-look) | [](https://docs.rs/objc2-quick-look/) |
+| `QuickLookThumbnailing` | [`objc2-quick-look-thumbnailing`](https://crates.io/crates/objc2-quick-look-thumbnailing) | [](https://docs.rs/objc2-quick-look-thumbnailing/) |
+| `QuickLookUI` | [`objc2-quick-look-ui`](https://crates.io/crates/objc2-quick-look-ui) | [](https://docs.rs/objc2-quick-look-ui/) |
+| `ReplayKit` | [`objc2-replay-kit`](https://crates.io/crates/objc2-replay-kit) | [](https://docs.rs/objc2-replay-kit/) |
+| `SafariServices` | [`objc2-safari-services`](https://crates.io/crates/objc2-safari-services) | [](https://docs.rs/objc2-safari-services/) |
+| `SafetyKit` | [`objc2-safety-kit`](https://crates.io/crates/objc2-safety-kit) | [](https://docs.rs/objc2-safety-kit/) |
+| `SceneKit` | [`objc2-scene-kit`](https://crates.io/crates/objc2-scene-kit) | [](https://docs.rs/objc2-scene-kit/) |
+| `ScreenCaptureKit` | [`objc2-screen-capture-kit`](https://crates.io/crates/objc2-screen-capture-kit) | [](https://docs.rs/objc2-screen-capture-kit/) |
+| `ScreenSaver` | [`objc2-screen-saver`](https://crates.io/crates/objc2-screen-saver) | [](https://docs.rs/objc2-screen-saver/) |
+| `ScreenTime` | [`objc2-screen-time`](https://crates.io/crates/objc2-screen-time) | [](https://docs.rs/objc2-screen-time/) |
+| `ScriptingBridge` | [`objc2-scripting-bridge`](https://crates.io/crates/objc2-scripting-bridge) | [](https://docs.rs/objc2-scripting-bridge/) |
+| `Security` | [`objc2-security`](https://crates.io/crates/objc2-security) | [](https://docs.rs/objc2-security/) |
+| `SecurityFoundation` | [`objc2-security-foundation`](https://crates.io/crates/objc2-security-foundation) | [](https://docs.rs/objc2-security-foundation/) |
+| `SecurityInterface` | [`objc2-security-interface`](https://crates.io/crates/objc2-security-interface) | [](https://docs.rs/objc2-security-interface/) |
+| `SensitiveContentAnalysis` | [`objc2-sensitive-content-analysis`](https://crates.io/crates/objc2-sensitive-content-analysis) | [](https://docs.rs/objc2-sensitive-content-analysis/) |
+| `SensorKit` | [`objc2-sensor-kit`](https://crates.io/crates/objc2-sensor-kit) | [](https://docs.rs/objc2-sensor-kit/) |
+| `ServiceManagement` | [`objc2-service-management`](https://crates.io/crates/objc2-service-management) | [](https://docs.rs/objc2-service-management/) |
+| `SharedWithYou` | [`objc2-shared-with-you`](https://crates.io/crates/objc2-shared-with-you) | [](https://docs.rs/objc2-shared-with-you/) |
+| `SharedWithYouCore` | [`objc2-shared-with-you-core`](https://crates.io/crates/objc2-shared-with-you-core) | [](https://docs.rs/objc2-shared-with-you-core/) |
+| `ShazamKit` | [`objc2-shazam-kit`](https://crates.io/crates/objc2-shazam-kit) | [](https://docs.rs/objc2-shazam-kit/) |
+| `Social` | [`objc2-social`](https://crates.io/crates/objc2-social) | [](https://docs.rs/objc2-social/) |
+| `SoundAnalysis` | [`objc2-sound-analysis`](https://crates.io/crates/objc2-sound-analysis) | [](https://docs.rs/objc2-sound-analysis/) |
+| `Speech` | [`objc2-speech`](https://crates.io/crates/objc2-speech) | [](https://docs.rs/objc2-speech/) |
+| `SpriteKit` | [`objc2-sprite-kit`](https://crates.io/crates/objc2-sprite-kit) | [](https://docs.rs/objc2-sprite-kit/) |
+| `StoreKit` | [`objc2-store-kit`](https://crates.io/crates/objc2-store-kit) | [](https://docs.rs/objc2-store-kit/) |
+| `Symbols` | [`objc2-symbols`](https://crates.io/crates/objc2-symbols) | [](https://docs.rs/objc2-symbols/) |
+| `SystemConfiguration` | [`objc2-system-configuration`](https://crates.io/crates/objc2-system-configuration) | [](https://docs.rs/objc2-system-configuration/) |
+| `SystemExtensions` | [`objc2-system-extensions`](https://crates.io/crates/objc2-system-extensions) | [](https://docs.rs/objc2-system-extensions/) |
+| `TVMLKit` | [`objc2-tv-ml-kit`](https://crates.io/crates/objc2-tv-ml-kit) | [](https://docs.rs/objc2-tv-ml-kit/) |
+| `TVServices` | [`objc2-tv-services`](https://crates.io/crates/objc2-tv-services) | [](https://docs.rs/objc2-tv-services/) |
+| `TVUIKit` | [`objc2-tv-ui-kit`](https://crates.io/crates/objc2-tv-ui-kit) | [](https://docs.rs/objc2-tv-ui-kit/) |
+| `ThreadNetwork` | [`objc2-thread-network`](https://crates.io/crates/objc2-thread-network) | [](https://docs.rs/objc2-thread-network/) |
+| `UIKit` | [`objc2-ui-kit`](https://crates.io/crates/objc2-ui-kit) | [](https://docs.rs/objc2-ui-kit/) |
+| `UniformTypeIdentifiers` | [`objc2-uniform-type-identifiers`](https://crates.io/crates/objc2-uniform-type-identifiers) | [](https://docs.rs/objc2-uniform-type-identifiers/) |
+| `UserNotifications` | [`objc2-user-notifications`](https://crates.io/crates/objc2-user-notifications) | [](https://docs.rs/objc2-user-notifications/) |
+| `UserNotificationsUI` | [`objc2-user-notifications-ui`](https://crates.io/crates/objc2-user-notifications-ui) | [](https://docs.rs/objc2-user-notifications-ui/) |
+| `VideoSubscriberAccount` | [`objc2-video-subscriber-account`](https://crates.io/crates/objc2-video-subscriber-account) | [](https://docs.rs/objc2-video-subscriber-account/) |
+| `VideoToolbox` | [`objc2-video-toolbox`](https://crates.io/crates/objc2-video-toolbox) | [](https://docs.rs/objc2-video-toolbox/) |
+| `Virtualization` | [`objc2-virtualization`](https://crates.io/crates/objc2-virtualization) | [](https://docs.rs/objc2-virtualization/) |
+| `Vision` | [`objc2-vision`](https://crates.io/crates/objc2-vision) | [](https://docs.rs/objc2-vision/) |
+| `WatchConnectivity` | [`objc2-watch-connectivity`](https://crates.io/crates/objc2-watch-connectivity) | [](https://docs.rs/objc2-watch-connectivity/) |
+| `WatchKit` | [`objc2-watch-kit`](https://crates.io/crates/objc2-watch-kit) | [](https://docs.rs/objc2-watch-kit/) |
+| `WebKit` | [`objc2-web-kit`](https://crates.io/crates/objc2-web-kit) | [](https://docs.rs/objc2-web-kit/) |
+| `iTunesLibrary` | [`objc2-itunes-library`](https://crates.io/crates/objc2-itunes-library) | [](https://docs.rs/objc2-itunes-library/) |
diff --git a/third_party/rust/objc2/src/topics/about_generated/list_unsupported.md b/third_party/rust/objc2/src/topics/about_generated/list_unsupported.md
@@ -0,0 +1,114 @@
+| Framework | Why is this unsupported? |
+| --- | --- |
+| `AGL` | OpenGL-specific, use Metal instead. |
+| `Accelerate` | Very C-centric, hard for us to map. |
+| `ActivityKit` | Swift-only. |
+| `AdAttributionKit` | Swift-only. |
+| `AddressBook` | Deprecated, use Contacts instead. |
+| `AddressBookUI` | Deprecated, use Contacts instead. |
+| `AppIntents` | Swift-only. |
+| `AppleScriptKit` | Basically empty nowadays. |
+| `AppleScriptObjC` | Basically empty nowadays. |
+| `AssetsLibrary` | Deprecated, use PhotoKit instead. |
+| `Assignables` | Swift-only. |
+| `AudioUnit` | Deprecated, use AudioToolbox instead. |
+| `AudioVideoBridging` | Deprecated, use AVKit/AVFoundation instead (maybe?). |
+| `AutomatedDeviceEnrollment` | Swift-only. |
+| `BrowserKit` | TODO. |
+| `CalendarStore` | Very deprecated. |
+| `CarKey` | Swift-only. |
+| `Charts` | Swift-only. |
+| `Cocoa` | Basically empty nowadays, use AppKit. |
+| `Combine` | Swift-only. |
+| `ContactProvider` | Mostly Swift-only. |
+| `CoreDisplay` | Basically empty. |
+| `CoreHID` | Swift-only. |
+| `CoreMIDIServer` | Very deprecated. |
+| `CoreTransferable` | Swift-only. |
+| `CreateML` | Swift-only. |
+| `CreateMLComponents` | Swift-only. |
+| `CryptoKit` | Swift-only. |
+| `DVDPlayback` | Deprecated, use AVKit/AVFoundation instead. |
+| `DeveloperToolsSupport` | Swift-only. |
+| `DeviceActivity` | Swift-only. |
+| `DeviceDiscoveryUI` | Needs Network first. |
+| `DirectoryService` | Deprecated, use OpenDirectory instead. |
+| `DiscRecording` | Deprecated, use AVKit/AVFoundation instead. |
+| `DiscRecordingUI` | Deprecated, use AVKit/AVFoundation instead. |
+| `DockKit` | Swift-only. |
+| `DriverKit` | Uses C++ classes. |
+| `ExtensionFoundation` | Swift-only. |
+| `FamilyControls` | Swift-only. |
+| `FinanceKit` | Swift-only. |
+| `FinanceKitUI` | Swift-only. |
+| `ForceFeedback` | Very C-centric and old. |
+| `GLKit` | OpenGL-specific, use Metal instead. |
+| `GLUT` | OpenGL-specific, use Metal instead. |
+| `GSS` | Very C-centric and old. |
+| `GroupActivities` | Swift-only. |
+| `Hypervisor` | Very low-level, consider crates like `applevisor` instead. |
+| `ICADevices` | Deprecated, use ImageCaptureCore instead. |
+| `ImagePlayground` | Swift-only. |
+| `InstallerPlugins` | Deprecated. |
+| `InstantMessage` | Deprecated in macOS 10.9. |
+| `JavaNativeFoundation` | Basically empty. |
+| `JavaRuntimeSupport` | Probably not really interesting.. |
+| `JournalingSuggestions` | Swift-only. |
+| `Kerberos` | Too low-level. |
+| `Kernel` | Too low-level. |
+| `KernelManagement` | Basically empty. |
+| `LDAP` | Basically empty. |
+| `LightweightCodeRequirements` | Swift-only. |
+| `LiveCommunicationKit` | Swift-only. |
+| `LockedCameraCapture` | Swift-only. |
+| `ManagedApp` | Swift-only. |
+| `ManagedAppDistribution` | Swift-only. |
+| `ManagedSettings` | Swift-only. |
+| `ManagedSettingsUI` | Swift-only. |
+| `MarketplaceKit` | Swift-only. |
+| `Matter` | Mostly available [here](https://github.com/project-chip/connectedhomeip). |
+| `MatterSupport` | Swift-only. |
+| `MediaLibrary` | Deprecated, use PhotoKit instead. |
+| `Message` | Basically empty. |
+| `MobileCoreServices` | Deprecated, use CoreServices + UniformTypeIdentifiers instead. |
+| `MusicKit` | Swift-only. |
+| `NetFS` | Deprecated, use macFUSE or FSKit instead (probably). |
+| `Network` | TODO, see [#646](https://github.com/madsmtm/objc2/issues/646). |
+| `OpenAL` | Very C-centric, use newer Audio frameworks instead. |
+| `OpenCL` | Very C-centric and old. |
+| `OpenGL` | OpenGL-specific, use Metal instead. |
+| `OpenGLES` | OpenGL-specific, use Metal instead. |
+| `PCSC` | Too low-level, consider crates like `pcsc` instead. |
+| `ProximityReader` | Swift-only. |
+| `ProximityReaderStub` | Basically empty. |
+| `QTKit` | No headers present in Xcode's SDK. |
+| `RealityFoundation` | Swift-only. |
+| `RealityKit` | Swift-only. |
+| `RoomPlan` | Swift-only. |
+| `Ruby` | Very C-centric and old. |
+| `SecureElementCredential` | Swift-only. |
+| `SecurityUI` | TODO. |
+| `StickerFoundation` | Basically empty. |
+| `StickerKit` | Basically empty. |
+| `SwiftData` | Swift-only. |
+| `SwiftUI` | Swift-only. |
+| `SwiftUICore` | Swift-only. |
+| `SyncServices` | Deprecated. |
+| `System` | Deprecated wrapper over libSystem.dylib. |
+| `TWAIN` | Very C-centric and old. |
+| `TabletopKit` | Swift-only. |
+| `TabularData` | Swift-only. |
+| `Tcl` | Very C-centric and old. |
+| `TipKit` | Swift-only. |
+| `Tk` | Very C-centric and old. |
+| `Translation` | Swift-only. |
+| `TranslationUIProvider` | Swift-only. |
+| `Twitter` | Deprecated, use Social instead. |
+| `VideoDecodeAcceleration` | Very C-centric and old. |
+| `VisionKit` | Swift-only. |
+| `WeatherKit` | Swift-only. |
+| `WidgetKit` | Mostly Swift-only. |
+| `WorkoutKit` | Swift-only. |
+| `iAd` | Disabled on server side, use AdServices instead. |
+| `vecLib` | Very C-centric and old. |
+| `vmnet` | Very C-centric and old. |
diff --git a/third_party/rust/objc2/src/topics/about_generated/mod.rs b/third_party/rust/objc2/src/topics/about_generated/mod.rs
@@ -0,0 +1,12 @@
+#![doc = include_str!("README.md")]
+
+#[doc = include_str!("deref.md")]
+pub mod deref {}
+
+pub mod list;
+
+#[doc = include_str!("CHANGELOG.md")]
+pub mod changelog {}
+
+#[doc = include_str!("cargo_features.md")]
+pub mod cargo_features {}
diff --git a/third_party/rust/objc2/src/topics/alternatives.md b/third_party/rust/objc2/src/topics/alternatives.md
@@ -0,0 +1,60 @@
+# Alternatives to `objc2`
+
+`objc2` is quite strict in its approach to interfacing with Objective-C, and encourages writing everything in Rust. This is, however, not always ideal, as our support for Objective-C will always be worse than that of the Objective-C compiler itself.
+
+But there is another way: If you are willing to write a bit of Objective-C glue-code yourself, you can write a C wrapper, and interface with that instead of directly interoperating with Objective-C bindings.
+
+
+## Example
+
+Let's say you would like to know the user's current locale. This could be done using a C binding like the following:
+
+```objective-c
+// src/helper.m
+#import <Foundation/Foundation.h>
+#import <string.h>
+
+const char* copy_current_locale_identifier(void) {
+ NSString* identifier = [[NSLocale currentLocale] localeIdentifier];
+ const char* s = [identifier UTF8String];
+ return strdup(s);
+}
+```
+
+Which is compiled in your `build.rs` (uses the `cc` crate).
+
+```rust, ignore
+// build.rs
+fn main() {
+ println!("cargo::rerun-if-changed=build.rs");
+
+ let mut builder = cc::Build::new();
+ builder.flag("-xobjective-c");
+ builder.flag("-fobjc-arc");
+ builder.flag("-fmodules");
+
+ builder.file("src/helper.m");
+ println!("cargo::rerun-if-changed=src/helper.m");
+
+ builder.compile("libhelper.a");
+}
+```
+
+And then exposed to Rust like this:
+
+```rust, ignore
+// src/lib.rs
+use libc::free;
+use std::ffi::{c_char, c_void, CStr, CString};
+
+extern "C-unwind" {
+ fn copy_current_locale_identifier() -> *const c_char;
+}
+
+pub fn current_locale() -> CString {
+ let ptr = unsafe { copy_current_locale_identifier() };
+ let ret = unsafe { CStr::from_ptr(ptr).to_owned() };
+ unsafe { free(ptr as *mut c_void) };
+ ret
+}
+```
diff --git a/third_party/rust/objc2/src/topics/crate_interop.md b/third_party/rust/objc2/src/topics/crate_interop.md
@@ -0,0 +1,143 @@
+# Interop with other crates
+
+The `objc2` project provides bindings for a lot of frameworks, including many existing popular framework crates. A few examples are given in the table below, see the full list [here][all-crates].
+
+| Existing crate | `objc2` crate |
+| -------------------------------------------------------------- | -------------------------------------------------------------------------- |
+| [`core-foundation`](https://docs.rs/core-foundation) | [`objc2-core-foundation`](https://docs.rs/objc2-core-foundation) |
+| [`core-graphics`](https://docs.rs/core-graphics) | [`objc2-core-graphics`](https://docs.rs/objc2-core-graphics) |
+| [`security-framework`](https://docs.rs/security-framework) | [`objc2-security`](https://docs.rs/objc2-security) |
+| [`system-configuration`](https://docs.rs/system-configuration) | [`objc2-system-configuration`](https://docs.rs/objc2-system-configuration) |
+| [`cocoa-foundation`](https://docs.rs/cocoa-foundation) | [`objc2-foundation`](https://docs.rs/objc2-foundation) |
+| [`cocoa`](https://docs.rs/cocoa) | [`objc2-app-kit`](https://docs.rs/objc2-app-kit) |
+| [`coremidi`](https://docs.rs/coremidi) | [`objc2-core-midi`](https://docs.rs/objc2-core-midi) |
+| [`core-video-sys`](https://docs.rs/core-video-sys) | [`objc2-core-video`](https://docs.rs/objc2-core-video) |
+
+While it is recommended that you use the `objc2` crates, you may still in certain cases want to use the existing crates. This can be a bit cumbersome, since `objc2` and [`block2`] impose certain requirements on the types involved, see below.
+
+[all-crates]: crate::topics::about_generated::list
+[`block2`]: https://docs.rs/block2/latest/block2/
+
+
+## Implementing `Encode` for a newtype wrapper
+
+Due to Rust's orphan rules, `core-foundation` types do not implement `Encode`, and as such cannot be passed to/from methods and in blocks by default.
+
+We're (slowly) working on fixing this, see [servo/core-foundation-rs#628], but in the meantime, you can use a newtype wrapper around the `CF...Ref`, and implement [`Encode`] for that wrapper.
+
+[servo/core-foundation-rs#628]: https://github.com/servo/core-foundation-rs/pull/628
+[`Encode`]: crate::encode::Encode
+
+
+### Examples
+
+Declaring an external function to [`CFRunLoopObserverCreateWithHandler`](https://developer.apple.com/documentation/corefoundation/1542816-cfrunloopobservercreatewithhandl?language=objc), which uses blocks and `block2`, and as such require its parameters to implement `Encode`.
+
+```rust
+use std::ptr;
+
+use block2::{RcBlock, Block};
+use core_foundation::base::{Boolean, CFAllocatorRef, CFIndex, CFOptionFlags, TCFType};
+use core_foundation::runloop::{CFRunLoopActivity, CFRunLoopObserver, CFRunLoopObserverRef, kCFRunLoopAllActivities};
+use objc2::encode::{Encode, Encoding};
+
+#[repr(transparent)]
+struct CFRunLoopObserverRefWrapper(CFRunLoopObserverRef);
+
+// SAFETY: `CFRunLoopObserverRefWrapper` is `#[repr(transparent)]` over
+// `CFRunLoopObserverRef`, which is a typedef to `struct __CFRunLoopObserver *`.
+unsafe impl Encode for CFRunLoopObserverRefWrapper {
+ const ENCODING: Encoding = Encoding::Pointer(&Encoding::Struct("__CFRunLoopObserver", &[]));
+}
+
+extern "C" {
+ fn CFRunLoopObserverCreateWithHandler(
+ allocator: CFAllocatorRef,
+ activities: CFOptionFlags,
+ repeats: Boolean,
+ order: CFIndex,
+ block: &Block<dyn Fn(CFRunLoopObserverRefWrapper, CFRunLoopActivity)>
+ ) -> CFRunLoopObserverRef;
+}
+
+let block = RcBlock::new(|observer: CFRunLoopObserverRefWrapper, activity| {
+ // Extract `CFRunLoopObserverRef` from `CFRunLoopObserverRefWrapper`
+ let observer = observer.0;
+});
+
+let observer = unsafe {
+ CFRunLoopObserver::wrap_under_create_rule(CFRunLoopObserverCreateWithHandler(
+ ptr::null(),
+ kCFRunLoopAllActivities,
+ false as Boolean,
+ 0,
+ &block,
+ ))
+};
+#
+# assert!(CFRunLoopObserverRefWrapper::ENCODING.equivalent_to_str("^{__CFRunLoopObserver=}"));
+```
+
+An alternative, if you don't want to go through the trouble of creating a newtype, is to use the `"relax-void-encoding"` Cargo feature.
+
+Here we set the [`-[CALayer borderColor]`](https://developer.apple.com/documentation/quartzcore/calayer/1410903-bordercolor?language=objc) property (which uses `CGColorRef`).
+
+```rust, ignore
+use core_foundation::base::ToVoid;
+use core_graphics::color::CGColor;
+use objc2_quartz_core::CALayer;
+use objc2::msg_send;
+
+fn set_border_color(layer: &CALayer, color: &CGColor) {
+ let color = color.to_void();
+ // Passing `*const c_void` here requires the "relax-void-encoding" feature
+ unsafe { msg_send![layer, setBorderColor: color] }
+}
+
+let layer = unsafe { CALayer::new() };
+let color = CGColor::rgb(1.0, 0.0, 0.0, 1.0);
+set_border_color(&layer, &color);
+```
+
+
+## Toll-free bridging
+
+Certain CoreFoundation types are documented to be ["toll-free bridged"], which means that they're completely interoperable with the Foundation types. To convert between these in Rust, you'll have to cast the pointers, e.g. between `CFStringRef` and `*const NSString`.
+
+["toll-free bridged"]: https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFDesignConcepts/Articles/tollFreeBridgedTypes.html
+
+
+### Example
+
+Toll-free bridging between `CFString` and `NSString`.
+
+```rust
+use core_foundation::base::TCFType;
+use core_foundation::string::{CFString, CFStringRef};
+use objc2_foundation::{NSString, ns_string};
+use objc2::rc::Retained;
+
+fn cf_string_to_ns(s: &CFString) -> &NSString {
+ let ptr: CFStringRef = s.as_concrete_TypeRef();
+ let ptr: *const NSString = ptr.cast();
+ // SAFETY: CFString is toll-free bridged with NSString.
+ unsafe { ptr.as_ref().unwrap() }
+}
+
+// Note: The `core_foundation` crate does not allow zero-cost (non-owning)
+// references to objects, so we have to convert from a `Retained<NSString>` to
+// the owning `CFString` (i.e. `&NSString -> &CFString` is not possible).
+fn ns_string_to_cf(s: Retained<NSString>) -> CFString {
+ // Yield ownership over the string
+ let ptr: *const NSString = Retained::into_raw(s);
+ let ptr: CFStringRef = ptr.cast();
+ // SAFETY: NSString is toll-free bridged with CFString, and
+ // ownership was passed above with `Retained::into_raw`.
+ unsafe { CFString::wrap_under_create_rule(ptr) }
+}
+
+let cf = CFString::new("foo");
+let ns = NSString::from_str("foo");
+assert_eq!(cf_string_to_ns(&cf), &*ns);
+assert_eq!(cf, ns_string_to_cf(ns));
+```
diff --git a/third_party/rust/objc2/src/topics/cross_compiling.md b/third_party/rust/objc2/src/topics/cross_compiling.md
@@ -0,0 +1,37 @@
+# Cross-compiling from Linux/Windows
+
+When compiling for e.g. iOS, you're always doing cross-compilation, since the host platform (macOS) is different from the target platform (iOS). This means that Apple's tooling generally has pretty good support for cross-compilation.
+
+To cross-compile Rust applications, you need a few things:
+1. A cross-compiler.
+ - Rust is a cross-compiler by default, though you'll need to install the standard library for [the relevant target](https://doc.rust-lang.org/rustc/platform-support.html) via. `rustup target add $TARGET`, or use [Cargo's unstable `-Zbuild-std`](https://doc.rust-lang.org/cargo/reference/unstable.html#build-std).
+2. A cross-linker.
+ - [`ld64`](https://github.com/apple-oss-distributions/ld64) is Apple's native linker. It used to be open source, though the new implementation [hasn't been open sourced](https://developer.apple.com/forums/thread/749558).
+ - [LLVM's `lld`](https://lld.llvm.org/) is a fairly good alternative, it comes bundled with Rust as `rust-lld`.
+3. Linker stubs (`*.tbd` files).
+ - Used to figure out which dynamic system library each symbol comes from.
+ - These stubs are located inside the SDK.
+4. If compiling C code using e.g. `cc-rs`, you will need:
+ - A C cross-compiler (Clang is a good choice here).
+ - C headers. Also located inside the SDK.
+
+In the end, you should end up with something like the following:
+```console
+$ rustup target add aarch64-apple-darwin
+$ export CARGO_TARGET_AARCH64_APPLE_DARWIN_LINKER=rust-lld
+$ export SDKROOT=$(pwd)/MacOSX.sdk
+$ export CC=clang CXX=clang++ AR=llvm-ar # If compiling C code
+$ cargo build --target aarch64-apple-darwin
+```
+
+For more details, see [Rust Cross](https://github.com/japaric/rust-cross), this has a good explanation of how cross-compiling Rust works in general. [OSXCross](https://github.com/tpoechtrager/osxcross) is also a good resource, it outlines how to create a complete compiler toolchain (including `ld64`).
+
+## SDK
+
+The required `MacOSX.sdk`, `iPhoneOS.sdk` etc. are bundled with Xcode. Please make sure to review [the Xcode SLA](https://www.apple.com/legal/sla/docs/xcode.pdf), especially the sections regarding required use on Apple-branded computers.
+
+Once you've done that, you can [download Xcode here](https://developer.apple.com/download/all/?q=xcode) (requires logging in with an Apple ID). If you only need to cross-compile to macOS, downloading just the Command Line Tools for Xcode will suffice.
+
+Once downloaded, you need to extract and find the `MacOSX.sdk` folder, and pass that to `rustc` using the `SDKROOT` environment variable.
+
+It may also be possible to find these SDKs elsewhere on the web by searching for e.g. "MacOSX.sdk", though we will not endorse any such links here, as the legality of such a re-distribution is a grey area.
diff --git a/third_party/rust/objc2/src/topics/goals.md b/third_party/rust/objc2/src/topics/goals.md
@@ -0,0 +1,60 @@
+# Goals
+
+There are many conflicting priorities in an open-source project like this
+(performance, ergonomics, understandability, portability, ...), but the
+following two can be seen as the guiding principles for everything else in
+this project.
+
+
+## 1. Complete Soundness
+
+The non-negotiable goal of these crates is to be completely "sound", meaning
+it **must not be possible for safe Rust to cause undefined behaviour**!
+
+As of January 2023, I (`@madsmtm`) have yet to find a single Rust crate or
+project calling Objective-C soundly. Issues I have found include:
+- Wrong method calling ABI.
+- Wrong memory management (in the best cases they just leak a lot).
+- Incorrect usage of `&mut`.
+- Incorrect main-thread safety.
+
+I don't state this to throw shade at these projects, it is very much
+understandable! Objective-C and Rust have vastly different semantics, so
+tackling these issues require vigilance and a focus that e.g. a system
+clipboard crate just doesn't have!
+
+Rather, I state it to provide reassurance: Rust's fearless concurrency can be
+won back! This project's [approach to the issue][layered-safety] _works_,
+and leaks, segfaults, race conditions and so on _can_ be completely
+eliminated!
+
+Needless to say, nothing is perfect, so if you think you've found a soundness
+hole, please don't hesitate to report it on the [issue tracker]. Known
+soundness holes (however theoretical) are tracked in the [`I-unsound`] label.
+
+[layered-safety]: crate::topics::layered_safety
+[`I-unsound`]: https://github.com/madsmtm/objc2/labels/I-unsound
+[issue tracker]: https://github.com/madsmtm/objc2/issues/new
+
+
+## 2. Idiomatic Rust
+
+Soundness would be easy to achieve if we just marked every API as `unsafe`,
+and called it a day (the precursor to this, `objc`, is basically sound).
+However, that just pushes the burden onto you, the user, and then we're not
+much better off!
+
+As such, we'll try to be as safe and idiomatic as possible; using references
+instead of pointers to represent objects and their (interior) mutability, `Option`
+instead of `null`, doing memory management automatically instead of manually,
+and so on (see again [these notes on "Layered Safety"][layered-safety]). These
+abstractions should ideally be zero-cost, but this is of course a balancing
+act against being ergonomic.
+
+Some APIs in `objc2` and `block2` will still have to remain `unsafe`, so these
+contain thorough `# Safety` sections, to let you know exactly which safety
+guarantees you need to uphold.
+The framework crates are a bit difficult in this regard, since they are mostly
+autogenerated, which means that almost nothing can be safe by default!
+However, we can still try to mitigate this problem by marking manually audited
+functionality as safe.
diff --git a/third_party/rust/objc2/src/topics/interior_mutability.md b/third_party/rust/objc2/src/topics/interior_mutability.md
@@ -0,0 +1,92 @@
+# Interior mutability
+
+Everything in Objective-C is an object, and objects can be freely shared between each other. This is done especially in GUI code where e.g. the application keeps a reference to all the currently open windows, while views can also access their parent window.
+
+In Rust, we generally dislike this, since it leads to designs that are more interlinked, rarely thread safe, harder for the compiler to optimize, and harder to understand, so instead mutability is restricted to `&mut` references, which ensures that the references is not [aliased].
+
+The original design of `objc2` did actually use `&mut` references in certain circumstances, see [#563] and [#265], but we have since removed that functionality, since it is just too error prone and restrictive.
+
+So what are we to do? Well, luckily, Rust lets us "opt-out" of the usual rules using [`UnsafeCell`], which tells the compiler that a type is actually mutable within a `&` reference, so that's what we do; every object is `UnsafeCell` internally.
+
+[aliased]: https://doc.rust-lang.org/nomicon/aliasing.html
+[#563]: https://github.com/madsmtm/objc2/issues/563
+[#265]: https://github.com/madsmtm/objc2/issues/265
+[`UnsafeCell`]: std::cell::UnsafeCell
+
+
+## Interior mutability in practice
+
+So what does this mean in practice?
+
+Well, it means that when you define classes yourself, **you will have to use interior mutability helpers like [`Cell`] and [`RefCell`] before you can modify properties**. This is a bit annoying, and can be a bit confusing if you're not used to it.
+
+Let's take an example: We define a class that contains an [`i32`] and a [`Vec`]. In usual Rust, both would just be in a `struct`, and you wouldn't have to do anything extra to access them, but since the class is only mutable from shared references like `&`, we have to wrap the integer in `Cell` and the vector in `RefCell` before we can safely mutate them:
+
+```rust
+use std::cell::{Cell, RefCell};
+use objc2::{define_class, DefinedClass};
+use objc2::runtime::NSObject;
+
+// Usually, you would just do:
+
+struct MyStruct {
+ my_int: i32,
+ my_vec: Vec<i32>,
+}
+
+impl MyStruct {
+ fn add_next(&mut self) {
+ self.my_int += 1;
+ self.my_vec.push(self.my_int);
+ }
+}
+
+// But when interfacing with Objective-C, you have to do:
+
+struct Ivars {
+ // `Copy` types that we want to mutate have to be wrapped in Cell
+ my_int: Cell<i32>,
+ // non-`Copy` types that we want to mutate have to be wrapped in RefCell
+ my_vec: RefCell<Vec<i32>>,
+}
+
+define_class!(
+ #[unsafe(super(NSObject))]
+ #[ivars = Ivars]
+ struct MyClass;
+
+ impl MyClass {
+ #[unsafe(method(myMethod))]
+ fn add_next(&self) {
+ let ivars = self.ivars();
+
+ // self.my_int += 1;
+ ivars.my_int.set(ivars.my_int.get() + 1);
+
+ // self.my_vec.push(self.my_int);
+ ivars.my_vec.borrow_mut().push(ivars.my_int.get());
+ }
+ }
+);
+```
+
+Note how this makes your class no longer [`Sync`], since there could be a race condition if we tried to call `add_next` from two threads at the same time. If you wanted to make it thread safe, you would have to use [`AtomicI32`], with all of the difficulties inherent in that.
+
+[`Cell`]: std::cell::Cell
+[`RefCell`]: std::cell::RefCell
+[`Vec`]: std::vec::Vec
+[`Sync`]: std::marker::Sync
+[`AtomicI32`]: std::sync::atomic::AtomicI32
+
+
+## Thread safety
+
+This hints at a general observation: **very few Objective-C classes are thread safe**, because they allow mutation while aliased elsewhere. So that's the other major place where interior mutability is likely to affect you.
+
+Even something as basic as `NSString` is not thread safe, because it may have come from a `NSMutableString` (see [the notes on deref][deref]), and as such could be mutated from a separate thread without the type-system knowing that.
+
+See also the [`MainThreadMarker`] and [`MainThreadOnly`] for a similar, even harder restriction, where some types are only usable on the main thread because they access global statics.
+
+[deref]: about_generated::deref
+[`MainThreadMarker`]: crate::MainThreadMarker
+[`MainThreadOnly`]: crate::MainThreadOnly
diff --git a/third_party/rust/objc2/src/topics/kvo.md b/third_party/rust/objc2/src/topics/kvo.md
@@ -0,0 +1,127 @@
+# Key-Value Observing
+
+Key-Value Observing (KVO) is a mechanism that allows an object to be notified of changes to properties of another object.
+
+This is exposed via the `NSKeyValueCoding` and `NSKeyValueObserving` categories / informal protocols on `NSObject`.
+
+See Apple's documentation on [Key-Value Coding][kvc-doc], [Key-Value Observing][kvo-doc] and [Swift's documentation on KVO][swift-kvo] for more information.
+
+[kvc-doc]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueCoding/index.html
+[kvo-doc]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html
+[swift-kvo]: https://developer.apple.com/documentation/swift/using-key-value-observing-in-swift
+
+
+## Example
+
+Create a helper class to help with observing key-value changes.
+
+```rust
+use core::ffi::c_void;
+use core::ptr;
+
+use objc2::rc::Retained;
+use objc2::runtime::AnyObject;
+use objc2::{define_class, msg_send, AnyThread, ClassType, DefinedClass};
+use objc2_foundation::{
+ ns_string, NSCopying, NSDictionary, NSKeyValueChangeKey, NSKeyValueObservingOptions, NSObject,
+ NSObjectNSKeyValueObserverRegistration, NSObjectProtocol, NSString,
+};
+
+struct Ivars {
+ object: Retained<NSObject>,
+ key_path: Retained<NSString>,
+ handler: Box<dyn Fn(&NSDictionary<NSKeyValueChangeKey, AnyObject>) + 'static>,
+}
+
+define_class!(
+ // SAFETY:
+ // - The superclass NSObject does not have any subclassing requirements.
+ // - MyObserver implements `Drop` and ensures that:
+ // - It does not call an overridden method.
+ // - It does not `retain` itself.
+ #[unsafe(super(NSObject))]
+ #[ivars = Ivars]
+ struct MyObserver;
+
+ impl MyObserver {
+ #[unsafe(method(observeValueForKeyPath:ofObject:change:context:))]
+ fn observe_value(
+ &self,
+ _key_path: Option<&NSString>,
+ _object: Option<&AnyObject>,
+ change: Option<&NSDictionary<NSKeyValueChangeKey, AnyObject>>,
+ _context: *mut c_void,
+ ) {
+ if let Some(change) = change {
+ (self.ivars().handler)(change);
+ } else {
+ (self.ivars().handler)(&NSDictionary::new());
+ }
+ }
+ }
+
+ unsafe impl NSObjectProtocol for MyObserver {}
+);
+
+impl MyObserver {
+ fn new(
+ object: Retained<NSObject>,
+ key_path: &NSString,
+ options: NSKeyValueObservingOptions,
+ // TODO: Thread safety? This probably depends on whether the observed
+ // object is later moved to another thread.
+ handler: impl Fn(&NSDictionary<NSKeyValueChangeKey, AnyObject>) + 'static + Send + Sync,
+ ) -> Retained<Self> {
+ let observer = Self::alloc().set_ivars(Ivars {
+ object,
+ key_path: key_path.copy(),
+ handler: Box::new(handler),
+ });
+ let observer: Retained<Self> = unsafe { msg_send![super(observer), init] };
+
+ // SAFETY: We make sure to un-register the observer before it's deallocated.
+ //
+ // Passing `NULL` as the `context` parameter here is fine, as the observer does not
+ // have any subclasses, and the superclass (NSObject) is not observing anything.
+ unsafe {
+ observer
+ .ivars()
+ .object
+ .addObserver_forKeyPath_options_context(
+ &observer,
+ key_path,
+ options,
+ ptr::null_mut(),
+ );
+ }
+
+ observer
+ }
+}
+
+impl Drop for MyObserver {
+ fn drop(&mut self) {
+ unsafe {
+ self.ivars()
+ .object
+ .removeObserver_forKeyPath(&self, &self.ivars().key_path);
+ }
+ }
+}
+
+fn main() {
+ let obj;
+ # obj = NSObject::new();
+
+ let _observer = MyObserver::new(
+ obj,
+ ns_string!("myKeyPath"),
+ NSKeyValueObservingOptions::New | NSKeyValueObservingOptions::Old,
+ |change| {
+ println!("object changed: {:?}", change);
+ },
+ );
+
+ // Do something that triggers the observer
+}
+```
diff --git a/third_party/rust/objc2/src/topics/layered_safety.md b/third_party/rust/objc2/src/topics/layered_safety.md
@@ -0,0 +1,250 @@
+# Layered Safety
+
+Objective-C is different from Rust<sup>[citation needed]</sup>. In particular,
+Rust has a concept of "safety" (see the [nomicon] for details), which
+Objective-C completely lacks.
+
+You will find when using the framework crates that basically everything (that
+has not been manually audited) is `unsafe`. So you might rightfully ask:
+What's the point then? Can't I just use `msg_send!`, and save the extra
+dependency?
+Yes, you could, but in fact the framework crates are much safer than doing
+method calling manually, even though you may end up writing `unsafe` just as
+many times. I dub this "layered safety"<sup>1</sup> to capture the fact that
+_not all usage of `unsafe` is created equally_!
+
+Simply put, when using an `unsafe` method in e.g. `objc2-foundation`, you have
+to ensure the compiler of much fewer things than when doing method calling
+manually.
+To see why this is the case, let me guide you through the various abstraction
+layers that the framework crates and `objc2` provide, and we'll see how each
+step makes things safer!
+
+The framework crates are not perfect though, and there may be cases where you
+have to drop down into lower-level details; luckily though, the fact that we
+have this layered architecture with each step exposed along the way allows you
+to do exactly that!
+
+<sup>1: I haven't heard this concept named before, if you know of prior art on this please let me know.</sup>
+
+[citation needed]: https://xkcd.com/285/
+[nomicon]: https://doc.rust-lang.org/nomicon/intro.html
+
+
+## Layer 1: `objc_msgSend`
+
+Unlike C APIs where you define an `extern "C"` function that you want to call,
+method calling is done in Objective-C using the "trampoline functions"
+[`objc_msgSend`], `objc_msgSend_stret`, `objc_msgSend_fpret` and so on.
+Which of these is correct depends on the target architecture and the calling
+convention. Furthermore, to use these you first have to cast them to the
+correct function signature using `mem::transmute`.
+
+This is actually what's done [in the standard library][std-objc], since they
+need to do it so rarely, and the extra dependency on a crate wouldn't be worth
+the cost.
+
+[`objc_msgSend`]: crate::ffi::objc_msgSend
+[std-objc]: https://github.com/rust-lang/rust/blob/aa0189170057a6b56f445f05b9840caf6f260212/library/std/src/sys/unix/args.rs#L196-L248
+
+
+### Example
+
+Doing the Rust equivalent of Objective-C's `NSUInteger hash_code = [obj hash];`.
+
+```rust
+use std::mem::transmute;
+use std::ffi::c_char;
+use objc2::ffi::{objc_msgSend, sel_registerName, NSUInteger};
+use objc2::runtime::{Sel, AnyObject};
+
+let obj: *const AnyObject;
+# let obj = &*objc2::runtime::NSObject::new() as *const objc2::runtime::NSObject as *const _;
+let sel = unsafe { sel_registerName(b"hash\0".as_ptr() as *const c_char).unwrap() };
+let msg_send_fn = unsafe {
+ transmute::<
+ unsafe extern "C-unwind" fn(),
+ unsafe extern "C-unwind" fn(*const AnyObject, Sel) -> NSUInteger,
+ >(objc_msgSend)
+};
+let hash_code = unsafe { msg_send_fn(obj, sel) };
+```
+
+
+## Layer 2: `MessageReceiver`
+
+We can improve on this using [`MessageReceiver::send_message`], which
+abstracts away the calling convention details, as well as adding an `Encode`
+bound on all the involved types. This ensures that we don't accidentally try
+to pass e.g. a `Vec<T>`, which does not have a stable memory layout.
+
+Additionally, when `debug_assertions` are enabled, the types involved in the
+message send are compared to the types exposed in the Objective-C runtime.
+This cannot catch mistakes like passing `null` where a non-null object was
+expected, but it helps a lot with accidentally passing a `&c_int` where `int`
+was expected.
+
+[`MessageReceiver::send_message`]: crate::runtime::MessageReceiver::send_message
+
+
+### Example
+
+We'll reuse the `hash` example from above again.
+
+```rust
+use objc2::ffi::NSUInteger;
+use objc2::runtime::{MessageReceiver, NSObject, Sel};
+
+let obj: &NSObject;
+# let obj = &*objc2::runtime::NSObject::new();
+let sel = Sel::register(c"hash");
+let hash_code: NSUInteger = unsafe {
+ MessageReceiver::send_message(obj, sel, ())
+};
+```
+
+
+## Layer 3: `msg_send!`
+
+Introducing macros: [`msg_send!`] can abstract away the tediousness of writing
+the selector expression, as well as ensuring that the number of arguments to
+the method is correct. It also handles details surrounding Objective-C's
+`BOOL` type.
+
+[`msg_send!`]: crate::msg_send
+
+
+### Examples
+
+The `hash` example again.
+
+```rust
+use objc2::ffi::NSUInteger;
+use objc2::runtime::NSObject;
+use objc2::msg_send;
+
+let obj: &NSObject;
+# let obj = &*objc2::runtime::NSObject::new();
+let hash_code: NSUInteger = unsafe { msg_send![obj, hash] };
+```
+
+That example is now pretty close to as minimal as it gets, so let's introduce
+something more complex; creating and using an instance of [`NSData`].
+
+```rust
+use objc2::ffi::NSUInteger;
+use objc2::runtime::NSObject;
+use objc2::{class, msg_send};
+
+let obj: *const NSObject = unsafe { msg_send![class!(NSData), new] };
+let length: NSUInteger = unsafe { msg_send![obj, length] };
+// We have to specify the return type here, see layer 4 below
+let _: () = unsafe { msg_send![obj, release] };
+```
+
+[`NSData`]: https://developer.apple.com/documentation/foundation/nsdata?language=objc
+
+
+## Layer 4: `Retained<T>`
+
+As you can see in the new example involving `NSData`, it can be quite tedious
+to remember the `release` call when you're done with the object. Furthermore,
+whether you need to `retain` and `release` the object involves subtle rules
+that depend on the name of the method!
+
+Objective-C solved this years ago with the introduction of "ARC". Similarly,
+we can solve this by changing the return value to the smart pointer
+[`Retained`], which works together with `msg_send!` to ensure that the
+memory management of the object is done correctly.
+
+[`Retained`]: crate::rc::Retained
+
+
+### Example
+
+The `NSData` example again.
+
+```rust
+use objc2::ffi::NSUInteger;
+use objc2::rc::Retained;
+use objc2::runtime::NSObject;
+use objc2::{class, msg_send};
+
+let obj: Retained<NSObject> = unsafe { msg_send![class!(NSData), new] };
+let length: NSUInteger = unsafe { msg_send![&obj, length] };
+// `obj` goes out of scope, `release` is automatically sent to the object
+```
+
+
+## Layer 5: `extern_x!` macros
+
+There's still a problem with the above: we can't actually make a reusable
+`hash` nor `length` function, since `NSObject` can refer to any object, and
+all objects do not actually respond to that method.
+
+To help with this, we have the [`extern_class!`] macro, which define a new
+type resembling `NSObject`, but which represents the `NSData` class instead.
+
+This allows us to make a completely safe API for downstream users!
+
+Along with this, we can now use the [`extern_methods!`] macro to help with
+defining our methods, which is also a big improvement over the `msg_send!`
+macro, since it allows us to directly "see" the types, instead of having them
+work by type-inference.
+
+[`extern_class!`]: crate::extern_class
+[`extern_methods!`]: crate::extern_methods
+
+
+### Example
+
+The `NSData` example again.
+
+```rust
+use objc2::ffi::NSUInteger;
+use objc2::rc::Retained;
+use objc2::runtime::NSObject;
+use objc2::{extern_class, extern_methods, ClassType};
+
+extern_class!(
+ #[unsafe(super(NSObject))]
+ #[derive(PartialEq, Eq, Hash)]
+ pub struct NSData;
+);
+
+impl NSData {
+ extern_methods!(
+ #[unsafe(method(new))]
+ pub fn new() -> Retained<Self>;
+
+ #[unsafe(method(length))]
+ pub fn length(&self) -> NSUInteger;
+ );
+}
+
+let obj = NSData::new();
+let length = obj.length();
+```
+
+
+## Layer 6: Framework crates
+
+Apple has a _lot_ of Objective-C code, and manually defining an interface to
+all of it would take a lifetime. Especially keeping track of which methods are
+nullable, and which are not, is difficult.
+
+Instead, we can autogenerate the above definition from the headers directly
+using type information exposed by `clang`, giving us a very high confidence
+that it is correct!
+
+
+### Example
+
+The `NSData` example again.
+
+```rust, ignore
+use objc2_foundation::NSData;
+
+let obj = NSData::new();
+let length = obj.length();
+```
diff --git a/third_party/rust/objc2/src/topics/migrating_from_objc.md b/third_party/rust/objc2/src/topics/migrating_from_objc.md
@@ -0,0 +1,19 @@
+# Migrating from the `objc` crate
+
+If size of your project is fairly small, it'll probably be easiest to just
+jump straight into replacing everything to use the framework crates.
+
+If your project is large, you can consider upgrading in small steps, following
+the changelog at each step of the way. For the most common cases, the
+changelogs will include a helpful example on how to upgrade.
+
+As an example you'd start by using `objc2` instead of `objc` in your
+`Cargo.toml`:
+```toml
+[dependencies]
+objc = { package = "objc2", version = "0.2.7" }
+```
+
+Afterwards, you can upgrade to the next release, in this case
+`v0.3.0-alpha.0`, and make the required changes to your code following the
+changelog. And so on, with every following release.
diff --git a/third_party/rust/objc2/src/topics/mod.rs b/third_party/rust/objc2/src/topics/mod.rs
@@ -0,0 +1,33 @@
+//! Various explanations and topics of discussion.
+#![allow(clippy::needless_doctest_main)]
+
+pub mod about_generated;
+
+#[doc = include_str!("alternatives.md")]
+pub mod alternatives {}
+#[cfg(not(feature = "gnustep-1-7"))]
+#[doc = include_str!("crate_interop.md")]
+pub mod crate_interop {}
+#[doc = include_str!("cross_compiling.md")]
+pub mod cross_compiling {}
+#[doc = include_str!("kvo.md")]
+pub mod kvo {}
+#[doc = include_str!("layered_safety.md")]
+pub mod layered_safety {}
+#[doc = include_str!("migrating_from_objc.md")]
+pub mod migrating_from_objc {}
+#[doc = include_str!("mvc.md")]
+pub mod mvc {}
+#[doc = include_str!("interior_mutability.md")]
+pub mod interior_mutability {}
+#[doc = include_str!("swift.md")]
+pub mod swift {}
+#[doc = include_str!("weak_property.md")]
+pub mod weak_property {} // Referenced by header-translator
+#[cfg(not(feature = "gnustep-1-7"))]
+#[doc = include_str!("run_loop.md")]
+pub mod run_loop {}
+
+#[cfg(not(doctest))]
+#[doc = include_str!("../../CHANGELOG.md")]
+pub mod changelog {}
diff --git a/third_party/rust/objc2/src/topics/mvc.md b/third_party/rust/objc2/src/topics/mvc.md
@@ -0,0 +1,16 @@
+# The Model-View-Controller design pattern
+
+The [Model-View-Controller (MVC) design pattern][mvc-doc] is quite prominent in Cocoa application development, and you may end up needing to incorporate parts of it in your application. You can use the following as a rough guideline for how to do so.
+
+**Model**: Use plain Rust structures inside [`Cell<T>`] or [`RefCell<T>`] so that you can mutate them behind shared references (or possibly `Rc<RefCell<T>>`, depending on if you need to share data between multiple controllers).
+
+**View**: Use the built-in views (`NSView` or `UIView`). If you need to register a delegate on these, use the controller as the delegate.
+
+**Controller**: Use the [`define_class!`] macro to create a new object. Use [`MainThreadOnly`] as the [`ClassType::ThreadKind`], so that you can implement view delegate protocols.
+
+[mvc-doc]: https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/Model-View-Controller/Model-View-Controller.html
+[`Cell<T>`]: core::cell::Cell
+[`RefCell<T>`]: core::cell::RefCell
+[`define_class!`]: crate::define_class
+[`MainThreadOnly`]: crate::MainThreadOnly
+[`ClassType::ThreadKind`]: crate::ClassType::ThreadKind
diff --git a/third_party/rust/objc2/src/topics/run_loop.md b/third_party/rust/objc2/src/topics/run_loop.md
@@ -0,0 +1,135 @@
+# Run loops and applications
+
+At the core of all Cocoa development sits what is known as the "run loop". This is Apple's a mechanism for allowing scheduling different tasks on the same thread, a bit like a Rust `async` runtime. See [their introductory documentation][runloop-doc] for more details.
+
+A lot of things in various different frameworks assume that the main thread's run loop is currently running, and will block indefinitely or fail in confusing ways if it is not. To avoid this, you should make sure to, well, run the run loop.
+
+[runloop-doc]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html
+
+
+## Non-graphical applications
+
+In non-graphical applications, you get the thread's current `NSRunLoop`, and run it periodically to allow scheduled work to complete.
+
+```rust, no_run
+use objc2_foundation::{NSDate, NSDefaultRunLoopMode, NSRunLoop};
+
+fn main() {
+ let run_loop = unsafe { NSRunLoop::currentRunLoop() };
+
+ // Set up timers, sources, etc.
+
+ let mut date = unsafe { NSDate::now() };
+ // Run for roughly 10 seconds
+ for i in 0..10 {
+ date = unsafe { date.dateByAddingTimeInterval(1.0) };
+ unsafe { run_loop.runUntilDate(&date) };
+
+ // Do something every second (if there are any sources attached)
+ }
+}
+```
+
+
+## Graphical applications
+
+In graphical applications, the main run loop needs to be managed by the application object. To get feedback during the execution of the application, you usually use a delegate instead, as can be seen in the following example.
+
+```rust, no_run
+use objc2::rc::{Allocated, Retained};
+use objc2::{define_class, msg_send, ClassType, DefinedClass, MainThreadOnly};
+use objc2_foundation::{NSNotification, NSObject, NSObjectProtocol};
+
+// Application delegate protocols happens to share a few methods,
+// we can utilize that to be a bit more platform-generic.
+#[cfg(target_os = "macos")]
+use objc2_app_kit::NSApplicationDelegate as DelegateProtocol;
+#[cfg(not(target_os = "macos"))]
+use objc2_ui_kit::UIApplicationDelegate as DelegateProtocol;
+
+#[derive(Default)]
+struct AppState {
+ // Whatever state you want to store in your delegate.
+}
+
+define_class!(
+ // SAFETY:
+ // - NSObject does not have any subclassing requirements.
+ // - `AppDelegate` does not implement `Drop`.
+ #[unsafe(super(NSObject))]
+ #[thread_kind = MainThreadOnly]
+ #[ivars = AppState]
+ struct AppDelegate;
+
+ impl AppDelegate {
+ // Called by `NSApplicationMain`, `UIApplicationMain`
+ // or our `msg_send![AppDelegate::class(), new]`.
+ #[unsafe(method_id(init))]
+ fn init(this: Allocated<Self>) -> Retained<Self> {
+ let this = this.set_ivars(AppState::default());
+ unsafe { msg_send![super(this), init] }
+ }
+ }
+
+ unsafe impl NSObjectProtocol for AppDelegate {}
+
+ unsafe impl DelegateProtocol for AppDelegate {
+ #[unsafe(method(applicationDidFinishLaunching:))]
+ fn did_finish_launching(&self, _notification: &NSNotification) {
+ println!("did finish launching!");
+
+ // Do UI initialization in here, such as creating windows, views, etc.
+ }
+
+ #[unsafe(method(applicationWillTerminate:))]
+ fn will_terminate(&self, _notification: &NSNotification) {
+ println!("will terminate!");
+
+ // Tear down your application state here. `NSApplicationMain` and
+ // `UIApplicationMain` will not return, this is (roughly) the last
+ // thing that will be called.
+ }
+ }
+);
+
+// AppKit (macOS).
+#[cfg(target_os = "macos")]
+fn main() {
+ let mtm = objc2::MainThreadMarker::new().unwrap();
+ let app = objc2_app_kit::NSApplication::sharedApplication(mtm);
+ let delegate: Retained<AppDelegate> = unsafe { msg_send![AppDelegate::class(), new] };
+ app.setDelegate(Some(objc2::runtime::ProtocolObject::from_ref(&*delegate)));
+ app.run();
+}
+
+// AppKit (macOS), if bundled and using a storyboard.
+#[cfg(target_os = "macos")]
+# #[cfg(with_storyboard)] // Hack to make example compile.
+fn main() {
+ let mtm = objc2::MainThreadMarker::new().unwrap();
+ // Initialize the class so that the storyboard can see it.
+ //
+ // The name specified in `define_class!`, i.e. "AppDelegate", must
+ // match what's specified in the storyboard.
+ let _cls = AppDelegate::class();
+ objc2_app_kit::NSApplication::main(mtm);
+}
+
+// UIKit (iOS/tvOS/watchOS/visionOS).
+#[cfg(not(target_os = "macos"))]
+fn main() {
+ let mtm = objc2::MainThreadMarker::new().unwrap();
+ let delegate_class = objc2_foundation::NSString::from_class(AppDelegate::class());
+ objc2_ui_kit::UIApplication::main(None, Some(&delegate_class), mtm);
+}
+```
+
+See [the documentation in `objc2-app-kit`][appkit-docs] and [in `objc2-ui-kit`][uikit-docs] for more examples. Note in particular that in UIKit, you may want to use scenes as well.
+
+[appkit-docs]: https://docs.rs/objc2-app-kit/
+[uikit-docs]: https://docs.rs/objc2-ui-kit/
+
+
+## Performance sensitive applications
+
+In some performance-sensitive cases, it can make sense to drop into the lower-level details, and directly use `dispatch_main`, `CFRunLoop` or similar to run the run loop. Note that this may prevent things that depend on `NSRunLoop` features from working, so test thoroughly.
diff --git a/third_party/rust/objc2/src/topics/swift.md b/third_party/rust/objc2/src/topics/swift.md
@@ -0,0 +1,126 @@
+# Interoperating with Swift
+
+Automatically mapping Apple's Swift-only frameworks is out of scope for the `objc2` project, see [#524](https://github.com/madsmtm/objc2/issues/524) for discussion.
+
+That said, if you need to interface with Swift from Rust, there are a few ways to go about it.
+
+
+## Exposing C APIs with `@_cdecl`
+
+The simplest way is probably to sidestep `objc2` directly, and instead expose functionality as `@_cdecl("...")`.
+
+Something like the following:
+
+```swift
+// foo.swift
+
+@_cdecl("foo")
+func foo() -> Int32 {
+ return 42;
+}
+```
+
+```rust,ignore
+// build.rs
+
+fn main() {
+ // Somehow invoke `swiftc` to compile the library.
+ // You probably want to use a helper library for this!
+ let status = std::process::Command("swiftc")
+ .arg("foo.swift")
+ .arg("-emit-library")
+ .status()
+ .unwrap();
+ assert!(status.success());
+
+ // And somehow tell Cargo to link the library.
+ println!("cargo::rustc-link-lib=foo");
+}
+```
+
+```rust,no_run
+// main.rs
+
+extern "C" {
+ fn foo() -> i32;
+}
+
+fn main() {
+ println!("foo returned {}", unsafe { foo() });
+}
+```
+
+
+## Exposing Objective-C APIs with `@objc`
+
+Building on the above approach, you could instead expose an Objective-C API using `@objc`, and then map that to Rust using `objc2`. Something like the following:
+
+```swift
+// foo.swift
+
+import Foundation
+
+@objc(Foo) class Foo: NSObject {
+ @objc var foo: Int32 = 42;
+
+ @objc func doStuff() {
+ print("foo \(foo)")
+ }
+}
+```
+
+You can view the Objective-C interface for this with `swiftc file.swift -emit-objc-header`.
+
+Mapping this to Rust would then look something like:
+
+```rust,no_run
+// main.rs
+
+use objc2::rc::{Allocated, Retained};
+use objc2::runtime::NSObject;
+use objc2::{extern_class, extern_methods, AnyThread};
+
+extern_class!(
+ #[unsafe(super(NSObject))]
+ #[name = "Foo"] // Matching the name in @objc(Foo)
+ pub struct Foo;
+);
+
+#[allow(non_snake_case)]
+impl Foo {
+ extern_methods!(
+ // Generated by the Swift compiler.
+ #[unsafe(method(init))]
+ pub fn init(this: Allocated<Self>) -> Retained<Self>;
+
+ // Property accessors.
+ #[unsafe(method(foo))]
+ pub fn foo(&self) -> i32;
+ #[unsafe(method(setFoo:))]
+ pub fn setFoo(&self, value: i32);
+
+ // Method.
+ #[unsafe(method(doStuff))]
+ pub fn doStuff(&self);
+ );
+}
+
+fn main() {
+ let obj = Foo::init(Foo::alloc());
+ assert_eq!(obj.foo(), 42);
+ obj.setFoo(10);
+ obj.doStuff();
+}
+```
+
+The plan for the future is to allow you to automatically map the Objective-C API that the Swift compiler generated using `bindgen`, see [#729](https://github.com/madsmtm/objc2/issues/729).
+
+
+## Further research
+
+To my knowledge, there exist a few projects in the Rust ecosystem that help with some of this:
+
+- [`swift-rs`](https://github.com/Brendonovich/swift-rs)
+- [`swift-bridge`](https://github.com/chinedufn/swift-bridge)
+- [`swift-bindgen`](https://github.com/nvzqz/swift-bindgen)
+- [UniFFI](https://github.com/mozilla/uniffi-rs)
diff --git a/third_party/rust/objc2/src/topics/weak_property.md b/third_party/rust/objc2/src/topics/weak_property.md
@@ -0,0 +1,12 @@
+# Weak properties
+
+Delegates in Cocoa are often stored as [`rc::Weak`] properties, to avoid reference cycles when the delegate also wants to store the object it is the delegate of.
+
+This can be a bit confusing sometimes if you create a delegate, set it on an object, and then expect your delegate methods to be called later on (which they in reality won't since the delegate will have been deallocated).
+
+In practice, you will have to store your delegate objects somewhere else, for example in your top-level application delegate.
+
+See Apple's [documentation on weak references][mem-weak] for a few more details.
+
+[`rc::Weak`]: crate::rc::Weak
+[mem-weak]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-1000810
diff --git a/third_party/rust/objc2/src/verify.rs b/third_party/rust/objc2/src/verify.rs
@@ -0,0 +1,337 @@
+use core::fmt;
+use core::hash::Hash;
+use core::mem::size_of;
+use std::error::Error;
+
+use crate::encode::{Encoding, EncodingBox};
+use crate::runtime::{EncodingParseError, Method};
+
+#[derive(Debug, PartialEq, Eq, Hash)]
+pub(crate) enum Inner {
+ MethodNotFound,
+ EncodingParseError(EncodingParseError),
+ MismatchedReturn(EncodingBox, Encoding),
+ MismatchedArgumentsCount(usize, usize),
+ MismatchedArgument(usize, EncodingBox, Encoding),
+}
+
+impl fmt::Display for Inner {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::MethodNotFound => write!(f, "method not found"),
+ Self::EncodingParseError(e) => write!(f, "{e}"),
+ Self::MismatchedReturn(expected, actual) => {
+ write!(
+ f,
+ "expected return to have type code '{expected}', but found '{actual}'",
+ )
+ }
+ Self::MismatchedArgumentsCount(expected, actual) => {
+ write!(f, "expected {expected} arguments, but {actual} were given",)
+ }
+ Self::MismatchedArgument(i, expected, actual) => {
+ write!(
+ f,
+ "expected argument at index {i} to have type code '{expected}', but found '{actual}'",
+ )
+ }
+ }
+ }
+}
+
+/// Failed verifying selector on a class.
+///
+/// This is returned in the error case of [`AnyClass::verify_sel`], see that
+/// for details.
+///
+/// This implements [`Error`], and a description of the error can be retrieved
+/// using [`fmt::Display`].
+///
+/// [`AnyClass::verify_sel`]: crate::runtime::AnyClass::verify_sel
+#[derive(Debug, PartialEq, Eq, Hash)]
+pub struct VerificationError(Inner);
+
+impl From<EncodingParseError> for VerificationError {
+ fn from(e: EncodingParseError) -> Self {
+ Self(Inner::EncodingParseError(e))
+ }
+}
+
+impl From<Inner> for VerificationError {
+ fn from(inner: Inner) -> Self {
+ Self(inner)
+ }
+}
+
+impl fmt::Display for VerificationError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Delegate to inner
+ fmt::Display::fmt(&self.0, f)
+ }
+}
+
+impl Error for VerificationError {}
+
+/// Relaxed version of `Encoding::equivalent_to_box` that allows interchanging
+/// more types.
+///
+/// NOTE: This is a top-level comparison, e.g. `*mut *mut c_void` or structs
+/// containing `*mut c_void` are not allowed differently than usual.
+fn relaxed_equivalent_to_box(encoding: &Encoding, expected: &EncodingBox) -> bool {
+ // Register-sized integers are interoperable regardless of sign. The Rust
+ // [docs on ABI compatibility][abi] says:
+ // > Noteworthy cases of types _not_ being ABI-compatible in general are:
+ // > - `bool` vs `u8`, `i32` vs `u32`, `char` vs `i32`: on some targets,
+ // > the calling conventions for these types differ in terms of what
+ // > they guarantee for the remaining bits in the register that are not
+ // > used by the value.
+ // > - ...
+ // [abi]: https://doc.rust-lang.org/std/primitive.fn.html#abi-compatibility
+ //
+ // But if the type is the size of the target's register size, then there
+ // are no remaining bits that can differ, and hence these types should be
+ // ABI compatible.
+ let size_of_matching_integer_encodings = match (encoding, expected) {
+ (Encoding::Char | Encoding::UChar, EncodingBox::Char | EncodingBox::UChar) => {
+ size_of::<core::ffi::c_char>()
+ }
+ (Encoding::Short | Encoding::UShort, EncodingBox::Short | EncodingBox::UShort) => {
+ size_of::<core::ffi::c_short>()
+ }
+ (Encoding::Int | Encoding::UInt, EncodingBox::Int | EncodingBox::UInt) => {
+ size_of::<core::ffi::c_int>()
+ }
+ (Encoding::Long | Encoding::ULong, EncodingBox::Long | EncodingBox::ULong) => {
+ size_of::<core::ffi::c_long>()
+ }
+ (
+ Encoding::LongLong | Encoding::ULongLong,
+ EncodingBox::LongLong | EncodingBox::ULongLong,
+ ) => size_of::<core::ffi::c_longlong>(),
+ _ => 0,
+ };
+ // NOTE: We use `size_of<usize>()` here as a proxy for detecting the
+ // register size, though this is not strictly correct, we should perhaps
+ // use `size_of::<core::ffi::c_size_t>()` instead?
+ let register_size = size_of::<usize>();
+ if size_of_matching_integer_encodings == register_size && cfg!(target_vendor = "apple") {
+ // TODO: Swift is doing this, so it's definitely safe on Apple
+ // platforms. But is it also on others?
+ return true;
+ }
+
+ // Allow `*mut c_void` and `*const c_void` to be used in place of other
+ // pointers.
+ if cfg!(feature = "relax-void-encoding")
+ && matches!(encoding, Encoding::Pointer(&Encoding::Void))
+ && matches!(expected, EncodingBox::Pointer(_))
+ {
+ return true;
+ }
+
+ // Allow signed types where unsigned types are excepted.
+ if cfg!(feature = "relax-sign-encoding") {
+ let actual_signed = match encoding {
+ Encoding::UChar => &Encoding::Char,
+ Encoding::UShort => &Encoding::Short,
+ Encoding::UInt => &Encoding::Int,
+ Encoding::ULong => &Encoding::Long,
+ Encoding::ULongLong => &Encoding::LongLong,
+ enc => enc,
+ };
+ let expected_signed = match expected {
+ EncodingBox::UChar => &EncodingBox::Char,
+ EncodingBox::UShort => &EncodingBox::Short,
+ EncodingBox::UInt => &EncodingBox::Int,
+ EncodingBox::ULong => &EncodingBox::Long,
+ EncodingBox::ULongLong => &EncodingBox::LongLong,
+ enc => enc,
+ };
+ if actual_signed == expected_signed {
+ return true;
+ }
+ }
+
+ encoding.equivalent_to_box(expected)
+}
+
+pub(crate) fn verify_method_signature(
+ method: &Method,
+ args: &[Encoding],
+ ret: &Encoding,
+) -> Result<(), VerificationError> {
+ let mut iter = method.types();
+
+ // TODO: Verify stack layout
+ let (expected, _stack_layout) = iter.extract_return()?;
+ if !relaxed_equivalent_to_box(ret, &expected) {
+ return Err(Inner::MismatchedReturn(expected, ret.clone()).into());
+ }
+
+ iter.verify_receiver()?;
+ iter.verify_sel()?;
+
+ let actual_count = args.len();
+
+ for (i, actual) in args.iter().enumerate() {
+ if let Some(res) = iter.next() {
+ // TODO: Verify stack layout
+ let (expected, _stack_layout) = res?;
+ if !relaxed_equivalent_to_box(actual, &expected) {
+ return Err(Inner::MismatchedArgument(i, expected, actual.clone()).into());
+ }
+ } else {
+ return Err(Inner::MismatchedArgumentsCount(i, actual_count).into());
+ }
+ }
+
+ let remaining = iter.count();
+ if remaining != 0 {
+ return Err(Inner::MismatchedArgumentsCount(actual_count + remaining, actual_count).into());
+ }
+
+ let expected_count = method.name().number_of_arguments();
+ if expected_count != actual_count {
+ return Err(Inner::MismatchedArgumentsCount(expected_count, actual_count).into());
+ }
+
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::ffi;
+ use crate::runtime::Sel;
+ use crate::test_utils;
+ use crate::{msg_send, sel};
+ use alloc::string::ToString;
+ use core::ffi::c_void;
+ use core::panic::{RefUnwindSafe, UnwindSafe};
+
+ #[test]
+ fn test_verify_message() {
+ let cls = test_utils::custom_class();
+
+ assert!(cls.verify_sel::<(), u32>(sel!(foo)).is_ok());
+ assert!(cls.verify_sel::<(u32,), ()>(sel!(setFoo:)).is_ok());
+
+ let metaclass = cls.metaclass();
+ metaclass
+ .verify_sel::<(i32, i32), i32>(sel!(addNumber:toNumber:))
+ .unwrap();
+ }
+
+ #[test]
+ fn test_verify_message_errors() {
+ let cls = test_utils::custom_class();
+
+ // Unimplemented selector (missing colon)
+ let err = cls.verify_sel::<(), ()>(sel!(setFoo)).unwrap_err();
+ assert_eq!(err.to_string(), "method not found");
+
+ // Incorrect return type
+ let err = cls.verify_sel::<(u32,), u64>(sel!(setFoo:)).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "expected return to have type code 'v', but found 'Q'"
+ );
+
+ // Too many arguments
+ let err = cls.verify_sel::<(u32, i8), ()>(sel!(setFoo:)).unwrap_err();
+ assert_eq!(err.to_string(), "expected 1 arguments, but 2 were given");
+
+ // Too few arguments
+ let err = cls.verify_sel::<(), ()>(sel!(setFoo:)).unwrap_err();
+ assert_eq!(err.to_string(), "expected 1 arguments, but 0 were given");
+
+ // Incorrect argument type
+ let err = cls.verify_sel::<(Sel,), ()>(sel!(setFoo:)).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "expected argument at index 0 to have type code 'I', but found ':'"
+ );
+
+ // <https://github.com/madsmtm/objc2/issues/566>
+ let res = cls.verify_sel::<(), ffi::NSUInteger>(sel!(getNSInteger));
+ let expected = if cfg!(any(
+ target_vendor = "apple",
+ feature = "relax-sign-encoding"
+ )) {
+ Ok(())
+ } else if cfg!(target_pointer_width = "64") {
+ Err("expected return to have type code 'q', but found 'Q'".to_string())
+ } else {
+ Err("expected return to have type code 'i', but found 'I'".to_string())
+ };
+ assert_eq!(res.map_err(|e| e.to_string()), expected);
+
+ // Metaclass
+ let metaclass = cls.metaclass();
+ let err = metaclass
+ .verify_sel::<(i32, i32, i32), i32>(sel!(addNumber:toNumber:))
+ .unwrap_err();
+ assert_eq!(err.to_string(), "expected 2 arguments, but 3 were given");
+ }
+
+ #[test]
+ #[cfg(all(debug_assertions, not(feature = "disable-encoding-assertions")))]
+ #[should_panic = "invalid message send to -[CustomObject foo]: expected return to have type code 'I', but found '^i'"]
+ fn test_send_message_verified() {
+ let obj = test_utils::custom_object();
+ let _: *const i32 = unsafe { msg_send![&obj, foo] };
+ }
+
+ #[test]
+ #[cfg(all(debug_assertions, not(feature = "disable-encoding-assertions")))]
+ #[should_panic = "invalid message send to +[CustomObject abcDef]: method not found"]
+ fn test_send_message_verified_to_class() {
+ let cls = test_utils::custom_class();
+ let _: i32 = unsafe { msg_send![cls, abcDef] };
+ }
+
+ #[test]
+ fn test_marker_traits() {
+ fn assert_marker_traits<T: Send + Sync + UnwindSafe + RefUnwindSafe + Unpin>() {}
+ assert_marker_traits::<VerificationError>();
+ }
+
+ #[test]
+ fn test_get_reference() {
+ let obj = test_utils::custom_object();
+ let _: () = unsafe { msg_send![&obj, setFoo: 42u32] };
+
+ let res: &u32 = unsafe { msg_send![&obj, fooReference] };
+ assert_eq!(*res, 42);
+ let res: *const u32 = unsafe { msg_send![&obj, fooReference] };
+ assert_eq!(unsafe { *res }, 42);
+ let res: *mut u32 = unsafe { msg_send![&obj, fooReference] };
+ assert_eq!(unsafe { *res }, 42);
+ }
+
+ #[test]
+ #[cfg_attr(
+ all(
+ debug_assertions,
+ not(feature = "disable-encoding-assertions"),
+ not(feature = "relax-void-encoding")
+ ),
+ should_panic = "invalid message send to -[CustomObject fooReference]: expected return to have type code '^I', but found '^v'"
+ )]
+ fn test_get_reference_void() {
+ let obj = test_utils::custom_object();
+ let _: () = unsafe { msg_send![&obj, setFoo: 42u32] };
+
+ let res: *mut c_void = unsafe { msg_send![&obj, fooReference] };
+ let res: *mut u32 = res.cast();
+ assert_eq!(unsafe { *res }, 42);
+ }
+
+ #[test]
+ #[cfg(all(debug_assertions, not(feature = "disable-encoding-assertions")))]
+ #[should_panic = "invalid message send to -[CustomObject foo]: expected return to have type code 'I', but found '^v'"]
+ fn test_get_integer_void() {
+ let obj = test_utils::custom_object();
+ let _: *mut c_void = unsafe { msg_send![&obj, foo] };
+ }
+}
diff --git a/third_party/rust/objc2/tests/backtrace.rs b/third_party/rust/objc2/tests/backtrace.rs
@@ -0,0 +1,200 @@
+#![cfg(target_vendor = "apple")] // The test is very Apple centric
+use std::ffi::c_void;
+
+use objc2::{define_class, extern_methods, msg_send, ClassType};
+use objc2_foundation::{NSException, NSObject};
+
+#[allow(dead_code)]
+fn merge_objc_symbols(exc: &NSException) -> Vec<String> {
+ // Objective-C and Rust have different mangling schemes, and don't really
+ // understand each other. So we use both `NSException`'s backtrace, and
+ // `backtrace`' resolving mechanism, to figure out the full list of
+ // demangled symbols.
+ let mut demangled_symbols = vec![];
+
+ let nssymbols = unsafe { exc.callStackSymbols() };
+ let return_addrs = unsafe { exc.callStackReturnAddresses() };
+
+ for (nssymbol, addr) in nssymbols.iter().zip(return_addrs) {
+ let addr = addr.as_usize() as *mut c_void;
+ let mut call_count = 0;
+ backtrace::resolve(addr, |symbol| {
+ if let Some(name) = symbol.name() {
+ demangled_symbols.push(name.to_string());
+ } else {
+ demangled_symbols.push(format!("{nssymbol} ({:?})", symbol.addr()));
+ }
+ call_count += 1;
+ });
+ if call_count == 0 {
+ demangled_symbols.push(nssymbol.to_string());
+ }
+ }
+
+ demangled_symbols
+}
+
+#[test]
+#[cfg(feature = "exception")]
+#[cfg_attr(feature = "catch-all", ignore = "catch-all interferes with our catch")]
+fn array_exception() {
+ use objc2::rc::Retained;
+ use objc2_foundation::NSArray;
+
+ #[no_mangle]
+ fn array_exception_via_msg_send() {
+ let arr = NSArray::<NSObject>::new();
+ let _: Retained<NSObject> = unsafe { msg_send![&arr, objectAtIndex: 0usize] };
+ }
+ let expected_msg_send = [
+ "__exceptionPreprocess",
+ "objc_exception_throw",
+ "CFArrayApply",
+ "<(A,) as objc2::encode::EncodeArguments>::__invoke",
+ "objc2::runtime::message_receiver::msg_send_primitive::send",
+ "objc2::runtime::message_receiver::MessageReceiver::send_message",
+ "<MethodFamily as objc2::__macro_helpers::msg_send_retained::MsgSend<Receiver,Return>>::send_message",
+ "array_exception_via_msg_send",
+ ];
+
+ #[no_mangle]
+ fn array_exception_via_extern_methods() {
+ let arr = NSArray::<NSObject>::new();
+ let _ = arr.objectAtIndex(0);
+ }
+ let expected_extern_methods = [
+ "__exceptionPreprocess",
+ "objc_exception_throw",
+ "CFArrayApply",
+ "<(A,) as objc2::encode::EncodeArguments>::__invoke",
+ "objc2::runtime::message_receiver::msg_send_primitive::send",
+ "objc2::runtime::message_receiver::MessageReceiver::send_message",
+ "<MethodFamily as objc2::__macro_helpers::msg_send_retained::MsgSend<Receiver,Return>>::send_message",
+ "objc2_foundation::generated::__NSArray::NSArray<ObjectType>::objectAtIndex",
+ "array_exception_via_extern_methods",
+ ];
+
+ for (fnptr, expected) in [
+ (
+ array_exception_via_msg_send as fn(),
+ &expected_msg_send as &[_],
+ ),
+ (array_exception_via_extern_methods, &expected_extern_methods),
+ ] {
+ let res = objc2::exception::catch(fnptr);
+ let exc = res.unwrap_err().unwrap();
+ let exc = exc.downcast::<NSException>().unwrap();
+
+ let symbols = merge_objc_symbols(&exc);
+
+ // No debug info available, such as when using `--release`.
+ if symbols[3] == "__mh_execute_header" {
+ continue;
+ }
+
+ if symbols.len() < expected.len() {
+ panic!("did not find enough symbols: {symbols:?}");
+ }
+
+ for (expected, actual) in expected.iter().zip(&symbols) {
+ assert!(
+ actual.contains(expected),
+ "{expected:?} must be in {actual:?}:\n{symbols:#?}",
+ );
+ }
+ }
+}
+
+define_class!(
+ #[unsafe(super = NSObject)]
+ struct Thrower;
+
+ impl Thrower {
+ #[unsafe(method(backtrace))]
+ fn __backtrace() -> *mut c_void {
+ let backtrace = backtrace::Backtrace::new();
+ Box::into_raw(Box::new(backtrace)).cast()
+ }
+ }
+);
+
+impl Thrower {
+ extern_methods!(
+ #[unsafe(method(backtrace))]
+ fn backtrace() -> *mut c_void;
+ );
+}
+
+#[test]
+#[cfg_attr(feature = "catch-all", ignore = "catch-all changes the backtrace")]
+fn capture_backtrace() {
+ #[no_mangle]
+ fn rust_backtrace_via_msg_send() -> backtrace::Backtrace {
+ let ptr: *mut c_void = unsafe { msg_send![Thrower::class(), backtrace] };
+ *unsafe { Box::from_raw(ptr.cast()) }
+ }
+ let expected_msg_send: &[_] = &[
+ "Backtrace::new",
+ "Thrower::__backtrace",
+ "<() as objc2::encode::EncodeArguments>::__invoke",
+ "objc2::runtime::message_receiver::msg_send_primitive::send",
+ "objc2::runtime::message_receiver::MessageReceiver::send_message",
+ "<MethodFamily as objc2::__macro_helpers::msg_send_retained::MsgSend<Receiver,Return>>::send_message",
+ "rust_backtrace_via_msg_send",
+ ];
+
+ #[no_mangle]
+ fn rust_backtrace_via_extern_methods() -> backtrace::Backtrace {
+ let ptr = Thrower::backtrace();
+ *unsafe { Box::from_raw(ptr.cast()) }
+ }
+ let expected_extern_methods: &[_] = &[
+ "Backtrace::new",
+ "Thrower::__backtrace",
+ "<() as objc2::encode::EncodeArguments>::__invoke",
+ "objc2::runtime::message_receiver::msg_send_primitive::send",
+ "objc2::runtime::message_receiver::MessageReceiver::send_message",
+ "<MethodFamily as objc2::__macro_helpers::msg_send_retained::MsgSend<Receiver,Return>>::send_message",
+ "Thrower::backtrace",
+ "rust_backtrace_via_extern_methods",
+ ];
+
+ for (backtrace, expected) in [
+ (rust_backtrace_via_msg_send(), expected_msg_send),
+ (rust_backtrace_via_extern_methods(), expected_extern_methods),
+ ] {
+ let symbols: Vec<_> = backtrace
+ .frames()
+ .iter()
+ .flat_map(|frame| frame.symbols())
+ .map(|symbol| {
+ symbol
+ .name()
+ .map(|name| name.to_string())
+ .unwrap_or_default()
+ })
+ .skip_while(|name| !name.contains("Backtrace::new"))
+ .collect();
+
+ // No debug info available, such as when using `--release`.
+ if backtrace.frames()[0].symbols()[0]
+ .name()
+ .unwrap()
+ .to_string()
+ == "__mh_execute_header"
+ {
+ continue;
+ }
+
+ if symbols.len() < expected.len() {
+ panic!("did not find enough symbols: {backtrace:?}");
+ }
+
+ for (expected, actual) in expected.iter().zip(&symbols) {
+ assert!(
+ actual.contains(expected),
+ "{expected:?} must be in {actual:?}:\n{symbols:#?}",
+ );
+ }
+ }
+}
diff --git a/third_party/rust/objc2/tests/define_class.rs b/third_party/rust/objc2/tests/define_class.rs
@@ -0,0 +1,619 @@
+#![deny(deprecated, unreachable_code)]
+use core::ptr::{self, NonNull};
+use std::cell::UnsafeCell;
+use std::marker::PhantomData;
+use std::panic::{RefUnwindSafe, UnwindSafe};
+
+use objc2::rc::Retained;
+use objc2::runtime::NSObject;
+use objc2::{define_class, extern_methods, sel, ClassType, MainThreadOnly};
+use static_assertions::{assert_impl_all, assert_not_impl_any};
+
+// Test that adding the `deprecated` attribute does not mean that warnings
+// when using the method internally are output.
+#[test]
+fn allow_deprecated() {
+ #![deny(deprecated)]
+
+ // Test allow propagates to impls
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[deprecated]
+ #[allow(deprecated)]
+ struct AllowDeprecated;
+
+ #[expect(deprecated)]
+ impl AllowDeprecated {
+ #[unsafe(method(someMethod))]
+ fn some_method() {}
+ }
+ );
+
+ #[expect(deprecated)]
+ let _ = AllowDeprecated::class();
+}
+
+define_class!(
+ #[unsafe(super(NSObject))]
+ struct DefineClassDeprecatedMethod;
+
+ #[deprecated]
+ impl DefineClassDeprecatedMethod {
+ #[unsafe(method(deprecatedOnImpl))]
+ fn deprecated_on_impl() {}
+ }
+
+ impl DefineClassDeprecatedMethod {
+ #[deprecated]
+ #[unsafe(method(deprecatedOnMethod))]
+ fn deprecated_on_method() {}
+ }
+);
+
+#[test]
+fn test_deprecated() {
+ let _cls = DefineClassDeprecatedMethod::class();
+}
+
+#[test]
+fn cfg() {
+ // Test `cfg`. We use `debug_assertions` here because it's something that we
+ // know our CI already tests.
+
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[cfg(debug_assertions)]
+ struct OnlyOnDebugAssertions;
+ );
+
+ #[cfg(debug_assertions)]
+ let _ = OnlyOnDebugAssertions::class();
+
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[cfg(not(debug_assertions))]
+ struct NeverOnDebugAssertions;
+ );
+
+ #[cfg(not(debug_assertions))]
+ let _ = NeverOnDebugAssertions::class();
+}
+
+// Test that `cfg` in methods.
+define_class!(
+ #[unsafe(super(NSObject))]
+ struct DefineClassCfg;
+
+ impl DefineClassCfg {
+ #[cfg(debug_assertions)]
+ #[unsafe(method(changesOnCfg1))]
+ fn _changes_on_cfg1() -> i32 {
+ 1
+ }
+
+ #[cfg(not(debug_assertions))]
+ #[unsafe(method(changesOnCfg1))]
+ fn _changes_on_cfg1() -> i32 {
+ 2
+ }
+
+ #[cfg(debug_assertions)]
+ #[unsafe(method(onlyWhenEnabled1))]
+ fn _only_when_enabled1(&self) {}
+
+ #[cfg(not(debug_assertions))]
+ #[unsafe(method(onlyWhenDisabled1))]
+ fn _only_when_disabled1(&self) {}
+ }
+
+ #[cfg(debug_assertions)]
+ impl DefineClassCfg {
+ #[unsafe(method(changesOnCfg2))]
+ fn _changes_on_cfg2(&self) -> i32 {
+ 1
+ }
+
+ #[unsafe(method(onlyWhenEnabled2))]
+ fn _only_when_enabled2() {}
+ }
+
+ #[cfg(not(debug_assertions))]
+ impl DefineClassCfg {
+ #[unsafe(method(changesOnCfg2))]
+ fn _changes_on_cfg2(&self) -> i32 {
+ 2
+ }
+
+ #[unsafe(method(onlyWhenDisabled2))]
+ fn _only_when_disabled2() {}
+ }
+
+ #[cfg(debug_assertions)]
+ impl DefineClassCfg {
+ #[cfg(not(debug_assertions))]
+ #[unsafe(method(never))]
+ fn _never(&self) {}
+
+ #[cfg(not(debug_assertions))]
+ #[unsafe(method(never))]
+ fn _never_class() {}
+ }
+);
+
+impl DefineClassCfg {
+ extern_methods!(
+ #[unsafe(method(new))]
+ fn new() -> Retained<Self>;
+ );
+
+ extern_methods!(
+ #[unsafe(method(changesOnCfg1))]
+ fn changes_on_cfg1() -> i32;
+
+ #[unsafe(method(changesOnCfg2))]
+ fn changes_on_cfg2(&self) -> i32;
+
+ #[cfg(debug_assertions)]
+ #[unsafe(method(onlyWhenEnabled1))]
+ fn only_when_enabled1(&self);
+
+ #[cfg(not(debug_assertions))]
+ #[unsafe(method(onlyWhenDisabled1))]
+ fn only_when_disabled1(&self);
+ );
+}
+
+#[cfg(debug_assertions)]
+impl DefineClassCfg {
+ extern_methods!(
+ #[unsafe(method(onlyWhenEnabled2))]
+ fn only_when_enabled2();
+ );
+}
+
+#[cfg(not(debug_assertions))]
+impl DefineClassCfg {
+ extern_methods!(
+ #[unsafe(method(onlyWhenDisabled2))]
+ fn only_when_disabled2();
+ );
+}
+
+#[test]
+fn test_method_that_changes_based_on_cfg() {
+ let expected = if cfg!(debug_assertions) { 1 } else { 2 };
+ let actual = DefineClassCfg::changes_on_cfg1();
+ assert_eq!(expected, actual, "changes_on_cfg1");
+
+ let actual = DefineClassCfg::new().changes_on_cfg2();
+ assert_eq!(expected, actual, "changes_on_cfg2");
+}
+
+#[test]
+fn test_method_that_is_only_available_based_on_cfg() {
+ let cls = DefineClassCfg::class();
+ let metacls = cls.metaclass();
+ let obj = DefineClassCfg::new();
+
+ #[cfg(debug_assertions)]
+ {
+ assert!(!cls.responds_to(sel!(onlyWhenDisabled1)));
+ assert!(!metacls.responds_to(sel!(onlyWhenDisabled2)));
+
+ obj.only_when_enabled1();
+ DefineClassCfg::only_when_enabled2();
+ }
+ #[cfg(not(debug_assertions))]
+ {
+ assert!(!cls.responds_to(sel!(onlyWhenEnabled1)));
+ assert!(!metacls.responds_to(sel!(onlyWhenEnabled2)));
+
+ obj.only_when_disabled1();
+ DefineClassCfg::only_when_disabled2();
+ }
+}
+
+#[test]
+fn test_method_that_is_never_available() {
+ let cls = DefineClassCfg::class();
+ let metacls = cls.metaclass();
+ assert!(!cls.responds_to(sel!(never)));
+ assert!(!metacls.responds_to(sel!(never)));
+}
+
+define_class!(
+ #[unsafe(super(NSObject))]
+ struct TestMultipleColonSelector;
+
+ impl TestMultipleColonSelector {
+ #[unsafe(method(test::arg3:))]
+ fn _test_class(arg1: i32, arg2: i32, arg3: i32) -> i32 {
+ arg1 + arg2 + arg3
+ }
+
+ #[unsafe(method(test::arg3:))]
+ fn _test_instance(&self, arg1: i32, arg2: i32, arg3: i32) -> i32 {
+ arg1 * arg2 * arg3
+ }
+
+ #[unsafe(method(test::error:))]
+ fn _test_error(&self, _arg1: i32, _arg2: i32, _arg3: *mut *mut NSObject) -> bool {
+ true
+ }
+
+ #[unsafe(method_id(test:::withObject:))]
+ fn _test_object(
+ &self,
+ _arg1: i32,
+ _arg2: i32,
+ _arg3: i32,
+ _obj: *const Self,
+ ) -> Option<Retained<Self>> {
+ None
+ }
+ }
+);
+
+impl TestMultipleColonSelector {
+ extern_methods!(
+ #[unsafe(method(new))]
+ fn new() -> Retained<Self>;
+
+ #[unsafe(method(test::arg3:))]
+ fn test_class(arg1: i32, arg2: i32, arg3: i32) -> i32;
+
+ #[unsafe(method(test::arg3:))]
+ fn test_instance(&self, arg1: i32, arg2: i32, arg3: i32) -> i32;
+
+ #[unsafe(method(test::error:_))]
+ fn test_error(&self, arg1: i32, arg2: i32) -> Result<(), Retained<NSObject>>;
+
+ #[unsafe(method(test:::withObject:))]
+ fn test_object(
+ &self,
+ arg1: i32,
+ arg2: i32,
+ arg3: i32,
+ obj: *const Self,
+ ) -> Option<Retained<Self>>;
+ );
+}
+
+#[test]
+fn test_multiple_colon_selector() {
+ assert_eq!(TestMultipleColonSelector::test_class(2, 3, 4), 9);
+
+ let obj = TestMultipleColonSelector::new();
+ assert_eq!(obj.test_instance(1, 2, 3), 6);
+ assert!(obj.test_error(1, 2).is_ok());
+ assert!(obj.test_object(1, 2, 3, ptr::null()).is_none());
+}
+
+define_class!(
+ #[unsafe(super(NSObject))]
+ struct DefineClassAllTheBool;
+
+ impl DefineClassAllTheBool {
+ #[unsafe(method(returnsBool))]
+ fn returns_bool() -> bool {
+ true
+ }
+
+ #[unsafe(method(returnsBoolInstance))]
+ fn returns_bool_instance(&self) -> bool {
+ true
+ }
+
+ #[unsafe(method(takesBool:andMut:andUnderscore:))]
+ fn takes_bool(a: bool, mut b: bool, _: bool) -> bool {
+ if b {
+ b = a;
+ }
+ b
+ }
+
+ #[unsafe(method(takesBoolInstance:andMut:andUnderscore:))]
+ fn takes_bool_instance(&self, a: bool, mut b: bool, _: bool) -> bool {
+ if b {
+ b = a;
+ }
+ b
+ }
+
+ #[unsafe(method(takesReturnsBool:))]
+ fn takes_returns_bool(b: bool) -> bool {
+ b
+ }
+
+ #[unsafe(method(takesReturnsBoolInstance:))]
+ fn takes_returns_bool_instance(&self, b: bool) -> bool {
+ b
+ }
+
+ #[unsafe(method_id(idTakesBool:))]
+ fn id_takes_bool(_b: bool) -> Option<Retained<Self>> {
+ None
+ }
+
+ #[unsafe(method_id(idTakesBoolInstance:))]
+ fn id_takes_bool_instance(&self, _b: bool) -> Option<Retained<Self>> {
+ None
+ }
+ }
+);
+
+#[test]
+fn test_all_the_bool() {
+ let _ = DefineClassAllTheBool::class();
+}
+
+define_class!(
+ #[unsafe(super(NSObject))]
+ struct DefineClassUnreachable;
+
+ // Ensure none of these warn
+ impl DefineClassUnreachable {
+ #[unsafe(method(unreachable))]
+ fn unreachable(&self) -> bool {
+ unreachable!()
+ }
+
+ #[unsafe(method(unreachableClass))]
+ fn unreachable_class() -> bool {
+ unreachable!()
+ }
+
+ #[unsafe(method(unreachableVoid))]
+ fn unreachable_void(&self) {
+ unreachable!()
+ }
+
+ #[unsafe(method(unreachableClassVoid))]
+ fn unreachable_class_void() {
+ unreachable!()
+ }
+
+ #[unsafe(method_id(unreachableRetained))]
+ fn unreachable_retained(&self) -> Retained<Self> {
+ unreachable!()
+ }
+
+ #[unsafe(method_id(unreachableClassRetained))]
+ fn unreachable_class_retained() -> Retained<Self> {
+ unreachable!()
+ }
+ }
+);
+
+#[test]
+fn test_unreachable() {
+ let _ = DefineClassUnreachable::class();
+}
+
+define_class!(
+ #[unsafe(super(NSObject))]
+ #[derive(Debug)]
+ struct OutParam;
+
+ impl OutParam {
+ #[unsafe(method(unsupported1:))]
+ fn _unsupported1(_param: &mut Retained<Self>) {}
+
+ #[unsafe(method(unsupported2:))]
+ fn _unsupported2(_param: Option<&mut Retained<Self>>) {}
+
+ #[unsafe(method(unsupported3:))]
+ fn _unsupported3(_param: &mut Option<Retained<Self>>) {}
+
+ #[unsafe(method(unsupported4:))]
+ fn _unsupported4(_param: Option<&mut Option<Retained<Self>>>) {}
+ }
+);
+
+impl OutParam {
+ extern_methods!(
+ #[unsafe(method(new))]
+ fn new() -> Retained<Self>;
+
+ #[unsafe(method(unsupported1:))]
+ fn unsupported1(_param: &mut Retained<Self>);
+
+ #[unsafe(method(unsupported2:))]
+ fn unsupported2(_param: Option<&mut Retained<Self>>);
+
+ #[unsafe(method(unsupported3:))]
+ fn unsupported3(_param: &mut Option<Retained<Self>>);
+
+ #[unsafe(method(unsupported4:))]
+ fn unsupported4(_param: Option<&mut Option<Retained<Self>>>);
+ );
+}
+
+#[test]
+#[should_panic = "`&mut Retained<_>` is not supported in `define_class!` yet"]
+#[cfg_attr(
+ feature = "gnustep-1-7",
+ ignore = "unwinding seems to not work properly here"
+)]
+fn out_param1() {
+ let mut param = OutParam::new();
+ OutParam::unsupported1(&mut param);
+}
+
+#[test]
+#[should_panic = "`Option<&mut Retained<_>>` is not supported in `define_class!` yet"]
+#[cfg_attr(
+ feature = "gnustep-1-7",
+ ignore = "unwinding seems to not work properly here"
+)]
+fn out_param2() {
+ OutParam::unsupported2(None);
+}
+
+#[test]
+#[should_panic = "`&mut Option<Retained<_>>` is not supported in `define_class!` yet"]
+#[cfg_attr(
+ feature = "gnustep-1-7",
+ ignore = "unwinding seems to not work properly here"
+)]
+fn out_param3() {
+ let mut param = Some(OutParam::new());
+ OutParam::unsupported3(&mut param);
+}
+
+#[test]
+#[should_panic = "`Option<&mut Option<Retained<_>>>` is not supported in `define_class!` yet"]
+#[cfg_attr(
+ feature = "gnustep-1-7",
+ ignore = "unwinding seems to not work properly here"
+)]
+fn out_param4() {
+ OutParam::unsupported4(None);
+}
+
+#[test]
+fn test_pointer_receiver_allowed() {
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[derive(Debug)]
+ struct PointerReceiver;
+
+ impl PointerReceiver {
+ #[unsafe(method(constPtr))]
+ fn const_ptr(_this: *const Self) {}
+
+ #[unsafe(method(mutPtr))]
+ fn mut_ptr(_this: *mut Self) {}
+
+ #[unsafe(method(nonnullPtr))]
+ fn nonnull_ptr(_this: NonNull<Self>) {}
+ }
+ );
+
+ let _ = PointerReceiver::class();
+}
+
+#[test]
+fn test_auto_traits() {
+ struct NotSend(PhantomData<*mut usize>);
+ unsafe impl Sync for NotSend {}
+ assert_impl_all!(NotSend: Sync, UnwindSafe, RefUnwindSafe, Unpin);
+ assert_not_impl_any!(NotSend: Send);
+
+ struct NotSync(PhantomData<*mut usize>);
+ unsafe impl Send for NotSync {}
+ assert_impl_all!(NotSync: Send, UnwindSafe, RefUnwindSafe, Unpin);
+ assert_not_impl_any!(NotSync: Sync);
+
+ struct NotUnwindSafe(PhantomData<*mut UnsafeCell<usize>>);
+ unsafe impl Send for NotUnwindSafe {}
+ unsafe impl Sync for NotUnwindSafe {}
+ assert_impl_all!(NotUnwindSafe: Send, Sync, Unpin);
+ assert_not_impl_any!(NotUnwindSafe: UnwindSafe, RefUnwindSafe);
+
+ // Superclass propagates.
+
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[ivars = (NotSend, NotSync, NotUnwindSafe)]
+ struct NonThreadSafeHelper;
+ );
+ define_class!(
+ #[unsafe(super(NonThreadSafeHelper))]
+ #[ivars = ()]
+ struct InheritsCustomWithNonSendIvar;
+ );
+ let _ = InheritsCustomWithNonSendIvar::class();
+ assert_not_impl_any!(InheritsCustomWithNonSendIvar: Unpin, Send, Sync, UnwindSafe, RefUnwindSafe);
+
+ // Main thread only. Not Send + Sync.
+
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[thread_kind = MainThreadOnly]
+ #[ivars = ()]
+ struct InheritsNSObjectMainThreadOnly;
+ );
+ let _ = InheritsNSObjectMainThreadOnly::class();
+ assert_impl_all!(InheritsNSObjectMainThreadOnly: UnwindSafe, RefUnwindSafe);
+ assert_not_impl_any!(InheritsNSObjectMainThreadOnly: Unpin, Send, Sync);
+
+ // NSObject is special.
+
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[ivars = ()]
+ struct InheritsNSObject;
+ );
+ let _ = InheritsNSObject::class();
+ assert_impl_all!(InheritsNSObject: Send, Sync, UnwindSafe, RefUnwindSafe);
+ assert_not_impl_any!(InheritsNSObject: Unpin);
+
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[ivars = NotSend]
+ struct InheritsNSObjectWithNonSendIvar;
+ );
+ let _ = InheritsNSObjectWithNonSendIvar::class();
+ assert_impl_all!(InheritsNSObjectWithNonSendIvar: Sync, UnwindSafe, RefUnwindSafe);
+ assert_not_impl_any!(InheritsNSObjectWithNonSendIvar: Unpin, Send);
+
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[ivars = NotSync]
+ struct InheritsNSObjectWithNonSyncIvar;
+ );
+ let _ = InheritsNSObjectWithNonSyncIvar::class();
+ assert_impl_all!(InheritsNSObjectWithNonSyncIvar: Send, UnwindSafe, RefUnwindSafe);
+ assert_not_impl_any!(InheritsNSObjectWithNonSyncIvar: Unpin, Sync);
+
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[ivars = NotUnwindSafe]
+ struct InheritsNSObjectWithNonUnwindSafeIvar;
+ );
+ let _ = InheritsNSObjectWithNonUnwindSafeIvar::class();
+ assert_impl_all!(InheritsNSObjectWithNonUnwindSafeIvar: Send, Sync);
+ assert_not_impl_any!(InheritsNSObjectWithNonUnwindSafeIvar: Unpin, UnwindSafe, RefUnwindSafe);
+}
+
+#[test]
+fn auto_name() {
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[ivars = ()]
+ struct AutoName;
+ );
+
+ let expected = format!("define_class::AutoName{}", env!("CARGO_PKG_VERSION"));
+
+ assert_eq!(AutoName::class().name().to_str().unwrap(), expected);
+ assert_eq!(AutoName::NAME, expected);
+}
+
+#[test]
+fn set_name() {
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[name = "SetName"]
+ struct SetName;
+ );
+
+ assert_eq!(SetName::class().name().to_str().unwrap(), "SetName");
+ assert_eq!(SetName::NAME, "SetName");
+}
+
+#[test]
+fn name_can_be_expr() {
+ const NAME: &str = " NameCanBeExpr ";
+
+ define_class!(
+ #[unsafe(super(NSObject))]
+ #[name = NAME.trim_ascii()]
+ struct NameCanBeExpr;
+ );
+
+ let expected = "NameCanBeExpr";
+ assert_eq!(NameCanBeExpr::class().name().to_str().unwrap(), expected);
+ assert_eq!(NameCanBeExpr::NAME, expected);
+}
diff --git a/third_party/rust/objc2/tests/define_class_self.rs b/third_party/rust/objc2/tests/define_class_self.rs
@@ -0,0 +1,64 @@
+//! To remind myself that `Self` needs to work in methods in `define_class!`,
+//! and hence whenever we name any of the types involved in this, we need to
+//! do it in a context where `Self` works.
+use objc2::rc::{Allocated, Retained};
+use objc2::runtime::NSObject;
+use objc2::{define_class, ClassType};
+
+trait GetSameType {
+ type SameType: ?Sized;
+}
+
+impl<T: ?Sized> GetSameType for T {
+ type SameType = T;
+}
+
+trait GetRetained {
+ type RetainedType;
+}
+
+impl<T> GetRetained for T {
+ type RetainedType = Retained<T>;
+}
+
+macro_rules! get_self {
+ () => {
+ Self
+ };
+}
+
+define_class!(
+ #[unsafe(super(NSObject))]
+ struct MyTestObject;
+
+ impl MyTestObject {
+ #[unsafe(method_id(initWith:))]
+ fn init(
+ _this: Allocated<<Self as GetSameType>::SameType>,
+ _param: <*const Self as GetSameType>::SameType,
+ ) -> Retained<<Self as GetSameType>::SameType> {
+ unimplemented!()
+ }
+
+ #[unsafe(method(isEqual:))]
+ fn is_equal(&self, _other: &Self) -> bool {
+ unimplemented!()
+ }
+
+ #[unsafe(method_id(test4))]
+ #[allow(unused_parens)]
+ fn test4(_this: &<(Self) as GetSameType>::SameType) -> Retained<get_self!()> {
+ unimplemented!()
+ }
+
+ #[unsafe(method_id(test5))]
+ fn test5(&self) -> <Self as GetRetained>::RetainedType {
+ unimplemented!()
+ }
+ }
+);
+
+#[test]
+fn create_class() {
+ let _ = MyTestObject::class();
+}
diff --git a/third_party/rust/objc2/tests/extern_class.rs b/third_party/rust/objc2/tests/extern_class.rs
@@ -0,0 +1,58 @@
+use objc2::{extern_class, ClassType};
+use objc2_foundation::NSObject;
+use static_assertions::assert_impl_all;
+
+#[test]
+fn allow_deprecated() {
+ #![deny(deprecated)]
+
+ // Test allow propagates to impls
+ extern_class!(
+ #[unsafe(super(NSObject))]
+ #[deprecated]
+ #[allow(deprecated)]
+ struct AllowDeprecated;
+ );
+}
+
+#[test]
+fn cfg() {
+ // Test `cfg`. We use `debug_assertions` here because it's something that we
+ // know our CI already tests.
+
+ extern_class!(
+ #[unsafe(super(NSObject))]
+ #[cfg(debug_assertions)]
+ #[name = "NSObject"]
+ struct OnlyOnDebugAssertions;
+ );
+
+ #[cfg(debug_assertions)]
+ let _ = OnlyOnDebugAssertions::class();
+
+ extern_class!(
+ #[unsafe(super(NSObject))]
+ #[cfg(not(debug_assertions))]
+ #[name = "NSObject"]
+ struct NeverOnDebugAssertions;
+ );
+
+ #[cfg(not(debug_assertions))]
+ let _ = NeverOnDebugAssertions::class();
+}
+
+#[test]
+fn derive() {
+ extern_class!(
+ #[rustfmt::skip]
+ #[unsafe(super(NSObject))]
+ #[derive(PartialEq)]
+ #[derive()]
+ #[derive(Eq,)]
+ #[derive()]
+ #[derive(Debug, Hash,)]
+ struct Derive;
+ );
+
+ assert_impl_all!(Derive: PartialEq, Eq, core::hash::Hash, core::fmt::Debug);
+}
diff --git a/third_party/rust/objc2/tests/macros_mainthreadmarker.rs b/third_party/rust/objc2/tests/macros_mainthreadmarker.rs
@@ -0,0 +1,78 @@
+use objc2::rc::Retained;
+use objc2::runtime::{NSObject, NSObjectProtocol};
+use objc2::{define_class, extern_methods, extern_protocol};
+
+extern_protocol!(
+ #[allow(clippy::missing_safety_doc)]
+ #[name = "MainThreadMarkerTestProtocol"]
+ unsafe trait Proto: NSObjectProtocol {
+ #[unsafe(method(myMethod:))]
+ fn protocol_method(mtm: MainThreadMarker, arg: i32) -> i32;
+
+ #[unsafe(method(myMethodRetained:))]
+ fn protocol_method_retained(mtm: MainThreadMarker, arg: &Self) -> Retained<Self>;
+ }
+);
+
+define_class!(
+ #[unsafe(super(NSObject))]
+ #[name = "MainThreadMarkerTest"]
+ #[derive(PartialEq, Eq, Hash, Debug)]
+ struct Cls;
+
+ unsafe impl NSObjectProtocol for Cls {}
+
+ unsafe impl Proto for Cls {
+ #[unsafe(method(myMethod:))]
+ fn _my_mainthreadonly_method(arg: i32) -> i32 {
+ arg + 1
+ }
+
+ #[unsafe(method_id(myMethodRetained:))]
+ fn _my_mainthreadonly_method_retained(arg: &Self) -> Retained<Self> {
+ unsafe { Retained::retain(arg as *const Self as *mut Self).unwrap() }
+ }
+ }
+);
+
+// The macro does a textual match; but when users actually use
+// `objc2_foundation::MainThreadMarker` to ensure soundness, they will not
+// do this!
+#[derive(Clone, Copy)]
+struct MainThreadMarker {
+ _some_field: u32,
+}
+
+impl Cls {
+ extern_methods!(
+ #[unsafe(method(new))]
+ fn new(mtm: MainThreadMarker) -> Retained<Self>;
+
+ #[unsafe(method(myMethod:))]
+ fn method(mtm: MainThreadMarker, arg: i32, mtm2: MainThreadMarker) -> i32;
+
+ #[unsafe(method(myMethodRetained:))]
+ fn method_retained(
+ mtm: MainThreadMarker,
+ arg: &Self,
+ mtm2: MainThreadMarker,
+ ) -> Retained<Self>;
+ );
+}
+
+#[test]
+fn call() {
+ let mtm = MainThreadMarker { _some_field: 0 };
+ let obj1 = Cls::new(mtm);
+
+ let res = Cls::method(mtm, 2, mtm);
+ assert_eq!(res, 3);
+ let res = Cls::protocol_method(mtm, 3);
+ assert_eq!(res, 4);
+
+ let obj2 = Cls::method_retained(mtm, &obj1, mtm);
+ assert_eq!(obj1, obj2);
+
+ let obj2 = Cls::protocol_method_retained(mtm, &obj1);
+ assert_eq!(obj1, obj2);
+}
diff --git a/third_party/rust/objc2/tests/no_prelude.rs b/third_party/rust/objc2/tests/no_prelude.rs
@@ -0,0 +1,158 @@
+//! Tests macros in a hostile environment (`#![no_implicit_prelude]` and all
+//! functions, modules, traits, and types replaced with custom bogus user
+//! replacements).
+//!
+//! Heavy inspiration for this file taken from `objrs`:
+//! https://gitlab.com/objrs/objrs/-/blob/b4f6598696b3fa622e6fddce7aff281770b0a8c2/test/src/no_prelude.rs
+#![no_implicit_prelude]
+#![allow(dead_code, non_camel_case_types)]
+
+extern crate objc2 as new_objc2;
+
+mod core {}
+mod std {}
+mod libc {}
+mod objc2 {}
+
+enum BogusType {}
+type u8 = BogusType;
+type u16 = BogusType;
+type u32 = BogusType;
+type u64 = BogusType;
+type u128 = BogusType;
+type usize = BogusType;
+
+type i8 = BogusType;
+type i16 = BogusType;
+type i32 = BogusType;
+type i64 = BogusType;
+type i128 = BogusType;
+type isize = BogusType;
+
+type bool = BogusType;
+type char = BogusType;
+type str = BogusType;
+
+type f32 = BogusType;
+type f64 = BogusType;
+
+type Option = BogusType;
+type Some = BogusType;
+type None = BogusType;
+
+type Result = BogusType;
+type Ok = BogusType;
+type Err = BogusType;
+
+type Box = BogusType;
+type String = BogusType;
+type Vec = BogusType;
+
+type drop = BogusType;
+
+type Copy = BogusType;
+type Send = BogusType;
+type Sized = BogusType;
+type Sync = BogusType;
+type Drop = BogusType;
+type Fn = BogusType;
+type FnMut = BogusType;
+type FnOnce = BogusType;
+
+type ToOwned = BogusType;
+type Clone = BogusType;
+type PartialEq = BogusType;
+type PartialOrd = BogusType;
+type Eq = BogusType;
+type Ord = BogusType;
+type AsRef = BogusType;
+type AsMut = BogusType;
+type Into = BogusType;
+type From = BogusType;
+type Default = BogusType;
+type Hash = BogusType;
+type Debug = BogusType;
+type Iterator = BogusType;
+type Extend = BogusType;
+type IntoIterator = BogusType;
+type DoubleEndedIterator = BogusType;
+type ExactSizeIterator = BogusType;
+type SliceConcatExt = BogusType;
+type ToString = BogusType;
+
+type PhantomData = BogusType;
+
+// Test begin below this line
+
+struct MyCustomIvars {
+ ivars: i32,
+}
+
+new_objc2::define_class!(
+ #[unsafe(super(new_objc2::runtime::NSObject))]
+ #[ivars = MyCustomIvars]
+ struct CustomObject;
+
+ impl CustomObject {
+ #[unsafe(method(a))]
+ fn _a() {}
+
+ #[unsafe(method_id(b))]
+ fn _b() -> new_objc2::rc::Retained<CustomObject> {
+ ::core::unimplemented!()
+ }
+ }
+);
+
+impl CustomObject {
+ new_objc2::extern_methods!(
+ #[unsafe(method(a))]
+ fn a();
+
+ #[unsafe(method(b))]
+ fn b(&self);
+ );
+}
+
+new_objc2::extern_class!(
+ #[unsafe(super(new_objc2::runtime::NSObject))]
+ #[name = "NSObject"]
+ struct NSObject2;
+);
+
+new_objc2::extern_protocol!(
+ #[allow(clippy::missing_safety_doc)]
+ unsafe trait CustomProtocol {
+ #[unsafe(method(c))]
+ fn c(&self);
+ }
+);
+
+#[test]
+fn test_selector() {
+ let _sel = new_objc2::sel!(abc);
+ let _sel = new_objc2::sel!(abc:def:);
+}
+
+#[test]
+fn test_class() {
+ let _class = new_objc2::class!(NSObject);
+}
+
+fn test_msg_send(obj: &CustomObject) {
+ let superclass = obj.class().superclass().unwrap();
+ let _: () = unsafe { new_objc2::msg_send![obj, a] };
+ let _: () = unsafe { new_objc2::msg_send![obj, a: obj, b: obj] };
+ let _: () = unsafe { new_objc2::msg_send![super(obj), a] };
+ let _: () = unsafe { new_objc2::msg_send![super(obj), a: obj, b: obj] };
+ let _: () = unsafe { new_objc2::msg_send![super(obj, superclass), a] };
+ let _: () = unsafe { new_objc2::msg_send![super(obj, superclass), a: obj, b: obj] };
+
+ let _: new_objc2::rc::Retained<new_objc2::runtime::AnyObject> =
+ unsafe { new_objc2::msg_send![obj, a] };
+ let _: new_objc2::__macro_helpers::Option<
+ new_objc2::rc::Retained<new_objc2::runtime::AnyObject>,
+ > = unsafe { new_objc2::msg_send![obj, a] };
+ let _: new_objc2::rc::Retained<new_objc2::runtime::AnyObject> =
+ unsafe { new_objc2::msg_send![obj, a: obj, b: obj] };
+}
diff --git a/third_party/rust/objc2/tests/protocol_shared_retain_count.rs b/third_party/rust/objc2/tests/protocol_shared_retain_count.rs
@@ -0,0 +1,32 @@
+//! Test that AnyProtocol objects have a shared retain count.
+//!
+//! Separate test because this will likely not work if other tests are running
+//! at the same time.
+
+use objc2::runtime::{AnyObject, NSObject, NSObjectProtocol};
+use objc2::{Message, ProtocolType};
+
+#[test]
+#[cfg_attr(
+ feature = "gnustep-1-7",
+ ignore = "Protocols don't implement isKindOfClass: on GNUStep"
+)]
+#[cfg_attr(
+ all(target_os = "macos", target_arch = "x86"),
+ ignore = "protocols are not NSObject subclasses in the old runtime"
+)]
+fn protocol_has_shared_retain_count() {
+ let obj: &AnyObject = <dyn NSObjectProtocol>::protocol().unwrap().as_ref();
+ let obj = obj.downcast_ref::<NSObject>().unwrap();
+
+ assert_eq!(obj.retainCount(), 1);
+ let obj2 = obj.retain();
+ assert_eq!(obj.retainCount(), 2);
+ drop(obj2);
+ assert_eq!(obj.retainCount(), 1);
+
+ let obj2: &AnyObject = <dyn NSObjectProtocol>::protocol().unwrap().as_ref();
+ assert_eq!(obj.retainCount(), 1);
+ let _obj2 = obj2.retain();
+ assert_eq!(obj.retainCount(), 2);
+}
diff --git a/third_party/rust/objc2/tests/track_caller.rs b/third_party/rust/objc2/tests/track_caller.rs
@@ -0,0 +1,234 @@
+#![cfg(all(target_pointer_width = "64", not(feature = "catch-all")))]
+#![allow(dead_code)]
+//! Test that our use of #[track_caller] is making the correct line number
+//! show up.
+use std::panic;
+use std::process::abort;
+use std::ptr;
+use std::sync::Mutex;
+
+use objc2::encode::Encode;
+use objc2::rc::{self, Allocated, Retained};
+use objc2::runtime::{self, NSObject};
+use objc2::{class, define_class, msg_send, AnyThread, ClassType};
+
+#[path = "../src/rc/test_object.rs"]
+#[allow(dead_code)]
+mod test_object;
+use self::test_object::RcTestObject;
+
+static EXPECTED_MESSAGE: Mutex<String> = Mutex::new(String::new());
+static EXPECTED_LINE: Mutex<u32> = Mutex::new(0);
+
+struct PanicChecker(());
+
+impl PanicChecker {
+ fn new() -> Self {
+ panic::set_hook(Box::new(|info| {
+ let expected_message = EXPECTED_MESSAGE.lock().unwrap();
+ let expected_line = EXPECTED_LINE.lock().unwrap();
+
+ let payload = info.payload();
+ let message = if let Some(payload) = payload.downcast_ref::<&'static str>() {
+ payload.to_string()
+ } else if let Some(payload) = payload.downcast_ref::<String>() {
+ payload.clone()
+ } else {
+ format!("could not extract message: {payload:?}")
+ };
+ let location = info.location().expect("location");
+
+ if !message.contains(&*expected_message) {
+ eprintln!("expected {expected_message:?}, got: {message:?}");
+ abort();
+ }
+ if location.file() != file!() {
+ eprintln!("expected file {:?}, got: {:?}", file!(), location.file());
+ abort();
+ }
+ if location.line() != *expected_line {
+ eprintln!("expected line {expected_line}, got: {}", location.line());
+ abort();
+ }
+ }));
+ Self(())
+ }
+
+ fn assert_panics(&self, message: &str, line: u32, f: impl FnOnce()) {
+ *EXPECTED_MESSAGE.lock().unwrap() = message.to_string();
+ *EXPECTED_LINE.lock().unwrap() = line;
+
+ let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
+ f();
+ }));
+ assert!(res.is_err());
+
+ *EXPECTED_MESSAGE.lock().unwrap() = "unknown".to_string();
+ *EXPECTED_LINE.lock().unwrap() = 0;
+ }
+}
+
+impl Drop for PanicChecker {
+ fn drop(&mut self) {
+ let _ = panic::take_hook();
+ }
+}
+
+#[test]
+#[cfg_attr(panic = "abort", ignore = "requires `catch_unwind`")]
+fn test_track_caller() {
+ let checker = PanicChecker::new();
+
+ if cfg!(debug_assertions) {
+ test_nil(&checker);
+ if cfg!(not(feature = "disable-encoding-assertions")) {
+ test_verify(&checker);
+ test_error_methods(&checker);
+ }
+ }
+
+ test_retained_unwrap(&checker);
+
+ if cfg!(feature = "catch-all") {
+ test_catch_all(&checker);
+ }
+
+ test_unwind(&checker);
+
+ #[cfg(not(feature = "unstable-static-class"))]
+ test_unknown_class(&checker);
+}
+
+fn test_nil(checker: &PanicChecker) {
+ let nil: *mut NSObject = ptr::null_mut();
+
+ let msg = "messsaging description to nil";
+ checker.assert_panics(msg, line!() + 1, || {
+ let _: *mut NSObject = unsafe { msg_send![nil, description] };
+ });
+ checker.assert_panics(msg, line!() + 1, || {
+ let _: *mut NSObject = unsafe { msg_send![super(nil, NSObject::class()), description] };
+ });
+ checker.assert_panics(msg, line!() + 1, || {
+ let _: Option<Retained<NSObject>> = unsafe { msg_send![nil, description] };
+ });
+}
+
+fn test_verify(checker: &PanicChecker) {
+ let obj = NSObject::new();
+
+ let msg = "invalid message send to -[NSObject description]: expected return to have type code '@', but found 'v'";
+ checker.assert_panics(msg, line!() + 1, || {
+ let _: () = unsafe { msg_send![&obj, description] };
+ });
+
+ let msg = format!("invalid message send to -[NSObject hash]: expected return to have type code '{}', but found '@'", usize::ENCODING);
+ checker.assert_panics(&msg, line!() + 1, || {
+ let _: Option<Retained<NSObject>> = unsafe { msg_send![&obj, hash] };
+ });
+}
+
+fn test_error_methods(checker: &PanicChecker) {
+ let nil: *mut NSObject = ptr::null_mut();
+
+ let msg = "messsaging someSelectorWithError: to nil";
+ checker.assert_panics(msg, line!() + 1, || {
+ let _: Result<(), Retained<NSObject>> = unsafe { msg_send![nil, someSelectorWithError: _] };
+ });
+ checker.assert_panics(msg, line!() + 2, || {
+ let _: Result<(), Retained<NSObject>> =
+ unsafe { msg_send![super(nil, NSObject::class()), someSelectorWithError: _] };
+ });
+ checker.assert_panics(msg, line!() + 2, || {
+ let _: Result<Retained<NSObject>, Retained<NSObject>> =
+ unsafe { msg_send![nil, someSelectorWithError: _] };
+ });
+
+ let msg = "invalid message send to -[NSObject someSelectorWithError:]: method not found";
+ checker.assert_panics(msg, line!() + 3, || {
+ let obj = RcTestObject::new();
+ let _: Result<(), Retained<NSObject>> =
+ unsafe { msg_send![super(&obj), someSelectorWithError: _] };
+ });
+}
+
+fn test_retained_unwrap(checker: &PanicChecker) {
+ let cls = RcTestObject::class();
+ let obj = RcTestObject::new();
+
+ let msg = "failed creating new instance using +[__RcTestObject newReturningNull]";
+ checker.assert_panics(msg, line!() + 1, || {
+ let _obj: Retained<RcTestObject> = unsafe { msg_send![cls, newReturningNull] };
+ });
+
+ let msg = if cfg!(debug_assertions) {
+ "messsaging init to nil"
+ } else {
+ "failed allocating object"
+ };
+ checker.assert_panics(msg, line!() + 2, || {
+ let obj: Allocated<RcTestObject> = unsafe { msg_send![cls, allocReturningNull] };
+ let _obj: Retained<RcTestObject> = unsafe { msg_send![obj, init] };
+ });
+
+ let msg = "failed initializing object with -initReturningNull";
+ checker.assert_panics(msg, line!() + 2, || {
+ let _obj: Retained<RcTestObject> =
+ unsafe { msg_send![RcTestObject::alloc(), initReturningNull] };
+ });
+
+ let msg = "failed copying object";
+ checker.assert_panics(msg, line!() + 1, || {
+ let _obj: Retained<RcTestObject> = unsafe { msg_send![&obj, copyReturningNull] };
+ });
+
+ let msg = "unexpected NULL returned from -[__RcTestObject methodReturningNull]";
+ checker.assert_panics(msg, line!() + 1, || {
+ let _obj: Retained<RcTestObject> = unsafe { msg_send![&obj, methodReturningNull] };
+ });
+}
+
+fn test_catch_all(checker: &PanicChecker) {
+ let obj: Retained<NSObject> = unsafe { msg_send![class!(NSArray), new] };
+
+ let msg = "NSRangeException";
+ checker.assert_panics(msg, line!() + 1, || {
+ let _: *mut NSObject = unsafe { msg_send![&obj, objectAtIndex: 0usize] };
+ });
+
+ let msg = "NSRangeException";
+ checker.assert_panics(msg, line!() + 1, || {
+ let _: Retained<NSObject> = unsafe { msg_send![&obj, objectAtIndex: 0usize] };
+ });
+}
+
+define_class!(
+ #[unsafe(super(NSObject))]
+ struct PanickingClass;
+
+ impl PanickingClass {
+ #[unsafe(method(panic))]
+ fn _panic() -> *mut Self {
+ panic!("panic in PanickingClass")
+ }
+ }
+);
+
+fn test_unwind(checker: &PanicChecker) {
+ let msg = "panic in PanickingClass";
+ let line = line!() - 7;
+ checker.assert_panics(msg, line, || {
+ let _: *mut NSObject = unsafe { msg_send![PanickingClass::class(), panic] };
+ });
+ checker.assert_panics(msg, line, || {
+ let _: Retained<NSObject> = unsafe { msg_send![PanickingClass::class(), panic] };
+ });
+}
+
+#[cfg(not(feature = "unstable-static-class"))]
+fn test_unknown_class(checker: &PanicChecker) {
+ let msg = "class NonExistentClass could not be found";
+ checker.assert_panics(msg, line!() + 1, || {
+ let _ = class!(NonExistentClass);
+ });
+}
diff --git a/third_party/rust/objc2/tests/use_macros.rs b/third_party/rust/objc2/tests/use_macros.rs
@@ -0,0 +1,56 @@
+use objc2::runtime::{AnyClass, NSObject};
+use objc2::{class, define_class, msg_send, sel};
+
+define_class!(
+ #[unsafe(super(NSObject))]
+ pub struct MyObject;
+);
+
+#[test]
+fn use_class_and_msg_send() {
+ unsafe {
+ let cls = class!(NSObject);
+ let obj: *mut NSObject = msg_send![cls, new];
+ let _hash: usize = msg_send![obj, hash];
+ let _: () = msg_send![obj, release];
+ }
+}
+
+#[test]
+fn use_sel() {
+ let _sel = sel!(description);
+ let _sel = sel!(setObject:forKey:);
+}
+
+pub fn test_msg_send_comma_handling(obj: &MyObject, superclass: &AnyClass) {
+ unsafe {
+ let _: () = msg_send![obj, a];
+ let _: () = msg_send![obj, a,];
+ let _: () = msg_send![obj, a: 32i32];
+ let _: () = msg_send![obj, a: 32i32,];
+ #[allow(deprecated)]
+ let _: () = msg_send![obj, a: 32i32 b: 32i32];
+ let _: () = msg_send![obj, a: 32i32, b: 32i32];
+ let _: () = msg_send![obj, a: 32i32, b: 32i32,];
+ }
+
+ unsafe {
+ let _: () = msg_send![super(obj, superclass), a];
+ let _: () = msg_send![super(obj, superclass), a,];
+ let _: () = msg_send![super(obj, superclass), a: 32i32];
+ let _: () = msg_send![super(obj, superclass), a: 32i32,];
+ #[allow(deprecated)]
+ let _: () = msg_send![super(obj, superclass), a: 32i32 b: 32i32];
+ let _: () = msg_send![super(obj, superclass), a: 32i32, b: 32i32];
+ let _: () = msg_send![super(obj, superclass), a: 32i32, b: 32i32,];
+ }
+
+ unsafe {
+ let _: () = msg_send![super(obj), a];
+ let _: () = msg_send![super(obj), a,];
+ let _: () = msg_send![super(obj), a: 32i32];
+ let _: () = msg_send![super(obj), a: 32i32,];
+ let _: () = msg_send![super(obj), a: 32i32, b: 32i32];
+ let _: () = msg_send![super(obj), a: 32i32, b: 32i32,];
+ }
+}
diff --git a/third_party/rust/objc2/translation-config.toml b/third_party/rust/objc2/translation-config.toml
@@ -0,0 +1,20 @@
+framework = "ObjectiveC"
+crate = "objc2"
+required-crates = []
+link = false
+skipped = true
+is-library = true
+
+macos = "10.0"
+maccatalyst = "13.0"
+ios = "2.0"
+tvos = "9.0"
+watchos = "2.0"
+visionos = "1.0"
+gnustep = true
+
+# Both a protocol and a class, so we use Swift's naming scheme
+protocol.NSObject.renamed = "NSObjectProtocol"
+
+# Return type `oneway void`
+class.NSObject.methods.release.skipped = true