Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

1
vendor/block2/.cargo-checksum.json vendored Normal file
View File

@@ -0,0 +1 @@
{"files":{"CHANGELOG.md":"aeabbca665675536f681a698b942495a3e9a4e4ca63bb0e183dbb71fdbee02ba","Cargo.toml":"5b1e38f82e1a70cf6493c2d2b0c6085bb671bd1c98316d2d71baea53674f227c","README.md":"97afba7235e798ff0788861dc71209c6f280579788c3879919366fbab8d670b2","src/abi.rs":"93342b8dd4cd28e989e3a5c57708ca7755ae8f463485c16bb879abdff3c49429","src/block.rs":"8fa7435abf8295660c12407209b3641f0c9cefbf13c970d941d558b249f3068e","src/debug.rs":"42ecc2d8692d3b391467f728c29d36bd75645e52ff41050aacb3ab9d0c2d1cd4","src/ffi.rs":"a9508f9ec9cd8e7cbdaa6d22a2de2b069624b6d9312dcb47c6bdea2ea6824ead","src/global.rs":"8fffaecf332ff6e685e2c6ab98b4038637f9b75393e25cfa5bd211daa18314e0","src/lib.rs":"e10adaa7a5998c6c273237f76201157d7a21a6ec79265fc901d8fc87ad701b69","src/rc_block.rs":"14d0b5bafd280553a25e4c30dce5a27000f3cb91e666c6865e60ee9444bf142d","src/stack.rs":"0d953e39c588f94ed81239f3d6a58a6ffae5bd52b2b6ce7b8e7f809b151be026","src/traits.rs":"1b66e97259507f0cf06ac63e525e31a523c0fff324e7e08add7bd564bd3b9e3e"},"package":"2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"}

269
vendor/block2/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,269 @@
# 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.5.1 - 2024-05-21
### Deprecated
* Deprecated the `apple` Cargo feature flag, it is assumed by default on Apple
platforms.
## 0.5.0 - 2024-04-17
### 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
### Changed
* **BREAKING**: Updated `objc2` dependency to `v0.5.0`.
## 0.3.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 `0.2.0`.
## 0.2.0 - 2023-06-20
### Changed
* **BREAKING**: Updated `objc2` dependency to `v0.4.0`.
## 0.2.0-alpha.8 - 2023-02-07
### 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
### 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
### 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
### 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
### 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
### 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
### 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
### 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
### 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
### Added
* Support for linking to `libBlocksRuntime`.
## [0.1.5] (`block` crate) - 2016-04-04
### Changed
* Minor code changes
## [0.1.4] (`block` crate) - 2015-11-12
### Removed
* `libc` dependency.
## [0.1.3] (`block` crate) - 2015-11-07
### Changed
* Updated `libc` dependency.
## [0.1.2] (`block` crate) - 2015-10-10
### Fixed
* `improper_ctypes` warning.
## [0.1.1] (`block` crate) - 2015-09-03
### Fixed
* Missing `Sized` bounds on traits.
## [0.1.0] (`block` crate) - 2015-05-18
### 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
### Changed
* Use `objc_id`.
## [0.0.1] (`block` crate) - 2015-04-17
Initial version.
[0.1.6]: https://github.com/madsmtm/objc2/compare/block-0.1.5...block-0.1.6
[0.1.5]: https://github.com/madsmtm/objc2/compare/block-0.1.4...block-0.1.5
[0.1.4]: https://github.com/madsmtm/objc2/compare/block-0.1.3...block-0.1.4
[0.1.3]: https://github.com/madsmtm/objc2/compare/block-0.1.2...block-0.1.3
[0.1.2]: https://github.com/madsmtm/objc2/compare/block-0.1.1...block-0.1.2
[0.1.1]: https://github.com/madsmtm/objc2/compare/block-0.1.0...block-0.1.1
[0.1.0]: https://github.com/madsmtm/objc2/compare/block-0.0.2...block-0.1.0
[0.0.2]: https://github.com/madsmtm/objc2/compare/block-0.0.1...block-0.0.2
[0.0.1]: https://github.com/madsmtm/objc2/releases/tag/block-0.0.1

100
vendor/block2/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,100 @@
# 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.60"
name = "block2"
version = "0.5.1"
authors = [
"Steven Sheldon",
"Mads Marquart <mads@marquart.dk>",
]
build = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Apple's C language extension of blocks"
documentation = "https://docs.rs/block2/"
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"]
targets = [
"aarch64-apple-darwin",
"x86_64-apple-darwin",
"aarch64-apple-ios",
"x86_64-apple-ios",
"aarch64-apple-tvos",
"aarch64-apple-watchos",
"aarch64-apple-ios-macabi",
"x86_64-unknown-linux-gnu",
"i686-unknown-linux-gnu",
]
[package.metadata.release]
enable-features = []
shared-version = false
tag-prefix = "block"
[lib]
name = "block2"
path = "src/lib.rs"
[dependencies.objc2]
version = "0.5.2"
default-features = false
[features]
alloc = ["objc2/alloc"]
apple = []
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",
"objc2/std",
]
unstable-objfw = []
unstable-private = []
unstable-winobjc = ["gnustep-1-8"]

19
vendor/block2/README.md vendored Normal file
View File

@@ -0,0 +1,19 @@
# `block2`
[![Latest version](https://badgen.net/crates/v/block2)](https://crates.io/crates/block2)
[![License](https://badgen.net/badge/license/MIT/blue)](https://github.com/madsmtm/objc2/blob/master/LICENSE.txt)
[![Documentation](https://docs.rs/block2/badge.svg)](https://docs.rs/block2/)
[![CI](https://github.com/madsmtm/objc2/actions/workflows/ci.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/ci.yml)
Apple's C language extension of blocks in Rust.
This crate provides functionality for interracting 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.

338
vendor/block2/src/abi.rs vendored Normal file
View File

@@ -0,0 +1,338 @@
//! 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::fmt;
use core::{ffi::c_void, mem::MaybeUninit};
use std::os::raw::{c_char, c_int, c_ulong};
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);
}
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.0 & Self::$flag.0 != 0));
)*)
}
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.0 & Self::BLOCK_REFCOUNT_MASK.0 == Self::BLOCK_REFCOUNT_MASK.0),
);
f.field(
"reference_count",
&((self.0 & 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" 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" 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" 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" 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" 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>();
}
}

203
vendor/block2/src/block.rs vendored Normal file
View File

@@ -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 intented 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 distiction 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
}
}

132
vendor/block2/src/debug.rs vendored Normal file
View File

@@ -0,0 +1,132 @@
use core::fmt::{Debug, DebugStruct, Error, Formatter};
use core::ptr;
use std::ffi::CStr;
use crate::abi::{BlockDescriptorPtr, BlockFlags, BlockHeader};
use crate::ffi;
#[derive(Clone, Copy, PartialEq, Eq)]
struct Isa(*const ffi::Class);
impl Isa {
fn is_global(self) -> bool {
ptr::eq(
unsafe { ptr::addr_of!(ffi::_NSConcreteGlobalBlock) },
self.0,
)
}
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.0 & BlockFlags::BLOCK_HAS_COPY_DISPOSE.0 != 0,
has_signature: header.flags.0 & BlockFlags::BLOCK_HAS_SIGNATURE.0 != 0,
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]
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());
}
}

171
vendor/block2/src/ffi.rs vendored Normal file
View File

@@ -0,0 +1,171 @@
//! # Raw bindings to `Block.h`
use core::cell::UnsafeCell;
use core::ffi::c_void;
use core::marker::{PhantomData, PhantomPinned};
use std::os::raw::c_int;
/// 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 `objc_sys::OpaqueData`.
_opaque: UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>,
}
// TODO: Use `extern "C-unwind"` when in MSRV (because the runtime functions
// may call external routines).
extern "C" {
/// 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 std::os::raw::c_char;
#[cfg(any(doc, target_vendor = "apple", feature = "compiler-rt"))]
use std::os::raw::c_ulong;
extern "C" {
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;
use std::println;
#[test]
fn smoke() {
assert_eq!(unsafe { _Block_copy(ptr::null_mut()) }, ptr::null_mut());
unsafe { _Block_release(ptr::null_mut()) };
}
#[test]
fn test_linkable() {
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" fn(_) -> _);
println!(
"{:p}",
_Block_object_assign as unsafe extern "C" fn(_, _, _)
);
println!("{:p}", _Block_object_dispose as unsafe extern "C" fn(_, _));
println!("{:p}", _Block_release as unsafe extern "C" fn(_));
}
}

294
vendor/block2/src/global.rs vendored Normal file
View File

@@ -0,0 +1,294 @@
use core::fmt;
use core::marker::PhantomData;
use core::mem;
use core::mem::MaybeUninit;
use core::ops::Deref;
use core::ptr::{self, NonNull};
use std::os::raw::c_ulong;
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 is a smart pointer that [`Deref`]s to [`Block`].
///
/// It can created and stored in static memory using the [`global_block!`]
/// macro.
///
/// [`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(BlockFlags::BLOCK_IS_GLOBAL.0 | BlockFlags::BLOCK_USE_STRET.0);
#[doc(hidden)]
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
/// 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`]: std::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" fn inner(_: *mut $crate::GlobalBlock<dyn Fn($($t),*) $(-> $r)? + 'static>, $($a: $t),*) $(-> $r)? {
$body
}
// TODO: SAFETY
::core::mem::transmute::<
unsafe extern "C" fn(*mut $crate::GlobalBlock<dyn Fn($($t),*) $(-> $r)? + 'static>, $($a: $t),*) $(-> $r)?,
unsafe extern "C" 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 expected = format!(
"GlobalBlock {{
isa: _NSConcreteGlobalBlock,
flags: {DEBUG_BLOCKFLAGS},
reserved: core::mem::maybe_uninit::MaybeUninit<i32>,
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
}
}

395
vendor/block2/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,395 @@
//! # 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
//!
//!
//! ## 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.
//!
//! In the next few examples, we're going to work with a function
//! `check_addition`, that takes as parameter a block that adds two integers,
//! and 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 inside external methods declared with
//! [`objc2::extern_methods!`].
//!
//! ```
//! use block2::Block;
//! use objc2::extern_methods;
//! #
//! # use objc2::ClassType;
//! # objc2::extern_class!(
//! # struct MyClass;
//! #
//! # unsafe impl ClassType for MyClass {
//! # type Super = objc2::runtime::NSObject;
//! # type Mutability = objc2::mutability::InteriorMutable;
//! # const NAME: &'static str = "NSObject";
//! # }
//! # );
//!
//! extern_methods!(
//! unsafe impl MyClass {
//! #[method(checkAddition:)]
//! pub fn checkAddition(&self, block: &Block<dyn Fn(i32, i32) -> i32>);
//! }
//! );
//! ```
//!
//! If the function/method allowed passing `NULL` blocks, the type would be
//! `Option<&Block<dyn Fn(i32, i32) -> i32>>` instead.
//!
//!
//! ## 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.
//!
//!
//! ## Creating blocks
//!
//! Creating a block to pass to Objective-C can be done with [`RcBlock`] or
//! [`StackBlock`], depending on if you want to move the block to the heap,
//! or let the callee decide if it needs to do that.
//!
//! To call such a function / method, we could create a new block from a
//! closure using [`RcBlock::new`].
//!
//! ```
//! use block2::RcBlock;
//! #
//! # extern "C" fn check_addition(block: &block2::Block<dyn Fn(i32, i32) -> i32>) {
//! # assert_eq!(block.call((5, 8)), 13);
//! # }
//!
//! let block = RcBlock::new(|a, b| a + b);
//! check_addition(&block);
//! ```
//!
//! This creates the block on the heap. If the external function you're
//! calling is not going to copy the block, it may be more performant if you
//! construct a [`StackBlock`] directly, using [`StackBlock::new`].
//!
//! Note that this requires that the closure is [`Clone`], as the external
//! code is allowed to copy the block to the heap in the future.
//!
//! ```
//! 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);
//! ```
//!
//! As an optimization, if your closure doesn't capture any variables (as in
//! the above examples), you can use the [`global_block!`] macro to create a
//! static block.
//!
//! ```
//! use block2::global_block;
//! #
//! # extern "C" fn check_addition(block: &block2::Block<dyn Fn(i32, i32) -> i32>) {
//! # assert_eq!(block.call((5, 8)), 13);
//! # }
//!
//! global_block! {
//! static BLOCK = |a: i32, b: i32| -> i32 {
//! a + b
//! };
//! }
//!
//! check_addition(&BLOCK);
//! ```
//!
//!
//! ## 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. In particular, there is no good way to prevent
//! re-entrancy.
//!
//! You will likely have to use interior mutability instead.
//!
//!
//! ## 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 (you might have to disable the default
//! `"apple"` feature first).
//!
//!
//! ### Apple's [`libclosure`](https://github.com/apple-oss-distributions/libclosure)
//!
//! - Feature flag: `apple`.
//!
//! 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]
#![warn(missing_docs)]
#![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.5.1")]
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide))]
#![cfg_attr(docsrs, doc(cfg_hide(doc)))]
extern crate alloc;
extern crate std;
#[cfg(not(feature = "std"))]
compile_error!("The `std` feature currently must be enabled.");
#[cfg(all(
not(docsrs),
not(any(
target_vendor = "apple",
feature = "compiler-rt",
feature = "gnustep-1-7",
feature = "unstable-objfw",
))
))]
compile_error!("A runtime must be selected");
#[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;
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};
/// 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.

268
vendor/block2/src/rc_block.rs vendored Normal file
View File

@@ -0,0 +1,268 @@
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::{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>`.
#[doc(alias = "MallocBlock")]
pub struct RcBlock<F: ?Sized> {
// Covariant
ptr: NonNull<Block<F>>,
}
impl<F: ?Sized> RcBlock<F> {
/// 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 so.
pub fn new<'f, A, R, Closure>(closure: Closure) -> Self
where
A: EncodeArguments,
R: EncodeReturn,
Closure: IntoBlock<'f, A, R, Dyn = F>,
{
// 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(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);
}
}

306
vendor/block2/src/stack.rs vendored Normal file
View File

@@ -0,0 +1,306 @@
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 std::os::raw::c_ulong;
use objc2::encode::{EncodeArguments, EncodeReturn, Encoding, RefEncode};
use crate::abi::{
BlockDescriptor, BlockDescriptorCopyDispose, BlockDescriptorPtr, BlockFlags, BlockHeader,
};
use crate::debug::debug_block_header;
use crate::{ffi, Block, IntoBlock};
/// An Objective-C block constructed on the stack.
///
/// This is a smart pointer that [`Deref`]s to [`Block`].
///
///
/// # 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 reintepret 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 reintepret 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<'f, A, R, Closure> StackBlock<'f, 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" 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<'f, A, R, Closure: Clone> StackBlock<'f, A, R, Closure> {
/// Clone the closure from one block to another.
unsafe extern "C" 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> {
/// 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
#[inline]
pub fn new(closure: Closure) -> Self
where
A: EncodeArguments,
R: EncodeReturn,
Closure: IntoBlock<'f, A, R> + Clone,
{
let header = BlockHeader {
isa: unsafe { ptr::addr_of!(ffi::_NSConcreteStackBlock) },
// TODO: Add signature.
flags: BlockFlags::BLOCK_HAS_COPY_DISPOSE,
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: BlockDescriptorPtr {
with_copy_dispose: &Self::DESCRIPTOR_WITH_CLONE,
},
};
Self {
p: PhantomData,
header,
closure,
}
}
}
// `RcBlock::new`
impl<'f, A, R, Closure> StackBlock<'f, A, R, Closure> {
unsafe extern "C" 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::new`.
}
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(closure: Closure) -> Self
where
A: EncodeArguments,
R: EncodeReturn,
Closure: IntoBlock<'f, A, R>,
{
// Don't need to emit copy and dispose helpers if the closure
// doesn't need it.
//
// TODO: Add signature.
let flags = if mem::needs_drop::<Self>() {
BlockFlags::BLOCK_HAS_COPY_DISPOSE
} else {
BlockFlags::EMPTY
};
let descriptor = if mem::needs_drop::<Self>() {
BlockDescriptorPtr {
with_copy_dispose: &Self::DESCRIPTOR_WITH_DROP,
}
} else {
BlockDescriptorPtr {
basic: &Self::DESCRIPTOR_BASIC,
}
};
let header = BlockHeader {
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,
}
}
}
impl<'f, A, R, Closure: Clone> Clone for StackBlock<'f, A, R, Closure> {
#[inline]
fn clone(&self) -> Self {
Self {
p: PhantomData,
header: self.header,
closure: self.closure.clone(),
}
}
}
impl<'f, A, R, Closure: Copy> Copy for StackBlock<'f, 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<'f, A, R, Closure> fmt::Debug for StackBlock<'f, 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
}
}

137
vendor/block2/src/traits.rs vendored Normal file
View File

@@ -0,0 +1,137 @@
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" 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" 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" fn(),
block: *mut Block<Self>,
($($a,)*): Self::Args,
) -> Self::Output {
// Very similar to `MessageArguments::__invoke`
let invoke: unsafe extern "C" 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" fn() {
unsafe extern "C" 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" fn(*mut StackBlock<'f, ($($t,)*), R, Closure>, $($t,)*) -> R,
unsafe extern "C" 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);