diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1834db060..89c751f79 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,6 @@ env: # # This excludes `header-translator`, `test-assembly`, `tests` and `test-ui`. PUBLIC_CRATES: >- - --package=block-sys --package=block2 --package=icrate --package=objc-sys @@ -780,10 +779,8 @@ jobs: - lint env: - # `compiler-rt` is only relevant for these crates - PUBLIC_CRATES: >- - --package=block-sys - --package=block2 + # `compiler-rt` is only relevant for block2 + PUBLIC_CRATES: --package=block2 steps: - uses: actions/checkout@v3 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b93c27a97..c5e862bf3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -59,7 +59,6 @@ Copy and fill out the following checklist into the release PR: - `objc2-proc-macros` - `objc-sys` - `objc2-encode` - - `block-sys` - `objc2` - `block2` - `icrate` diff --git a/Cargo.lock b/Cargo.lock index 2177547c1..d87da7f52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,18 +49,10 @@ dependencies = [ "serde", ] -[[package]] -name = "block-sys" -version = "0.2.0" -dependencies = [ - "objc-sys", -] - [[package]] name = "block2" version = "0.4.0" dependencies = [ - "block-sys", "objc2", ] @@ -636,7 +628,6 @@ dependencies = [ name = "tests" version = "0.1.0" dependencies = [ - "block-sys", "block2", "cc", "icrate", diff --git a/crates/block-sys/CHANGELOG.md b/crates/block-sys/CHANGELOG.md deleted file mode 100644 index f992b14d9..000000000 --- a/crates/block-sys/CHANGELOG.md +++ /dev/null @@ -1,70 +0,0 @@ -# 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.2.0 - 2023-02-07 - -### Changed -* Updated `objc-sys` to `v0.3.0`. -* **BREAKING**: Changed `links` key from `block_0_1` to `block_0_2` (so - `DEP_BLOCK_0_1_CC_ARGS` in build scripts becomes `DEP_BLOCK_0_2_CC_ARGS`). - - -## 0.1.0-beta.2 - 2022-12-24 - -### Changed -* Updated `objc-sys` to `v0.2.0-beta.3`. - - -## 0.1.0-beta.1 - 2022-08-28 - -### Fixed -* Fixed `docs.rs` setup. - - -## 0.1.0-beta.0 - 2022-07-19 - -### Added -* Use `doc_auto_cfg` to improve documentation output. - -### Changed -* **BREAKING**: Changed `links` key from `block_0_0` to `block_0_1` (so - `DEP_BLOCK_0_0_CC_ARGS` in build scripts becomes `DEP_BLOCK_0_1_CC_ARGS`). - - -## 0.0.4 - 2022-06-13 - -### Changed -* **BREAKING**: Changed `links` key from `block` to `block_0_0` for better - future compatibility, until we reach 1.0 (so `DEP_BLOCK_CC_ARGS` in build - scripts becomes `DEP_BLOCK_0_0_CC_ARGS`). -* **BREAKING**: Apple's runtime is now always the default. -* **BREAKING**: Updated `objc-sys` to `v0.2.0-beta.0`. - -### Fixed -* **BREAKING**: Tweak the types of a lot of fields and arguments. - - -## 0.0.3 - 2022-01-03 - -### Changed -* **BREAKING**: Updated `objc-sys` to `v0.2.0-alpha.1`. - - -## 0.0.2 - 2021-12-22 - -### Changed -* **BREAKING**: Updated `objc-sys` to `v0.2.0-alpha.0`. - -### Fixed -* **BREAKING**: `Class` is now `!UnwindSafe`. - - -## 0.0.1 - 2021-11-22 - -Initial release. diff --git a/crates/block-sys/Cargo.toml b/crates/block-sys/Cargo.toml deleted file mode 100644 index 4fc64b4fd..000000000 --- a/crates/block-sys/Cargo.toml +++ /dev/null @@ -1,84 +0,0 @@ -[package] -name = "block-sys" -# Remember to update `html_root_url` in lib.rs and the `links` key. -# -# Also, beware of using pre-release versions here, since because of the -# `links` key, two pre-releases requested with `=...` are incompatible. -version = "0.2.0" -authors = ["Mads Marquart "] -edition = "2021" -rust-version = "1.60" - -description = "Raw bindings to Apple's C language extension of blocks" -keywords = ["objective-c", "macos", "ios", "blocks", "sys"] -categories = [ - "external-ffi-bindings", - # "no_std", # TODO - "os::macos-apis", -] -repository = "https://github.com/madsmtm/objc2" -documentation = "https://docs.rs/block-sys/" -license = "MIT" - -readme = "README.md" - -# Downstream users can customize the linking! -# See https://doc.rust-lang.org/cargo/reference/build-scripts.html#overriding-build-scripts -links = "block_0_2" -build = "build.rs" - -[features] -# The default runtime is Apple's. Other platforms will probably error if the -# correct feature flag is not specified. -default = ["std", "apple"] - -# Currently not possible to turn off, put here for forwards compatibility. -std = ["alloc", "objc-sys?/std"] -alloc = ["objc-sys?/alloc"] - -# Link to Apple's libclosure (exists in libSystem) -apple = [] - -# Link to libBlocksRuntime from compiler-rt -compiler-rt = [] - -# Link to GNUStep's libobjc2 (which contains the block implementation) -gnustep-1-7 = ["objc-sys", "objc-sys/gnustep-1-7"] -gnustep-1-8 = ["objc-sys/gnustep-1-8", "gnustep-1-7"] -gnustep-1-9 = ["objc-sys/gnustep-1-9", "gnustep-1-8"] -gnustep-2-0 = ["objc-sys/gnustep-2-0", "gnustep-1-9"] -gnustep-2-1 = ["objc-sys/gnustep-2-1", "gnustep-2-0"] - -# Link to Microsoft's libobjc2 -unstable-winobjc = ["objc-sys/unstable-winobjc", "gnustep-1-8"] - -# Link to ObjFW -unstable-objfw = [] - -# Private -# Need `objc-sys` on certain platforms -unstable-docsrs = ["objc-sys", "objc-sys/unstable-docsrs"] - -[dependencies] -objc-sys = { path = "../objc-sys", version = "0.3.2", default-features = false, optional = true } - -[package.metadata.docs.rs] -default-target = "x86_64-apple-darwin" -no-default-features = true -features = ["std", "unstable-docsrs"] - -targets = [ - # MacOS - "x86_64-apple-darwin", - "aarch64-apple-darwin", - # "i686-apple-darwin", - # iOS - "aarch64-apple-ios", - "x86_64-apple-ios", - # "i386-apple-ios", - # GNUStep - "x86_64-unknown-linux-gnu", - "i686-unknown-linux-gnu", - # Windows - "x86_64-pc-windows-msvc", -] diff --git a/crates/block-sys/README.md b/crates/block-sys/README.md deleted file mode 100644 index e26a2ae24..000000000 --- a/crates/block-sys/README.md +++ /dev/null @@ -1,119 +0,0 @@ -# `block-sys` - -[![Latest version](https://badgen.net/crates/v/block-sys)](https://crates.io/crates/block-sys) -[![License](https://badgen.net/badge/license/MIT/blue)](https://github.com/madsmtm/objc2/blob/master/LICENSE.txt) -[![Documentation](https://docs.rs/block-sys/badge.svg)](https://docs.rs/block-sys/) -[![CI](https://github.com/madsmtm/objc2/actions/workflows/ci.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/ci.yml) - -Raw Rust bindings to Apple's C language extension of blocks. - -This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2), -see that for related crates. - - -## Runtime Support - -This library is a raw interface to the aptly specified [Blocks ABI][abi]. -However, 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 the linking must take that -into account. - -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). Note that if the `objc-sys` crate is present in the module -tree, this should have the same feature flag enabled as that. - - -[abi]: https://clang.llvm.org/docs/Block-ABI-Apple.html - - -### Apple's [`libclosure`](https://github.com/apple-oss-distributions/libclosure) - -- Feature flag: `apple`. - -This is the most sophisticated runtime, and it has quite a lot more features -than the specification mandates. It is used by default. - -The minimum required operating system versions are as follows: -- macOS: `10.6` -- iOS: `3.2` -- tvOS: Unknown -- watchOS: Unknown - -Though in practice Rust itself requires higher versions than this. - - -### 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 `objc-sys` 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 `objc-sys` 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!** - -TODO. - - -## C Compiler configuration - -To our knowledge, currently only `clang` supports the [Language Specification -for Blocks][block-lang]. To assist in compiling C (or Objective-C) sources -using these features, this crate's build script expose the -`DEP_BLOCK_0_2_CC_ARGS` environment variable to downstream build scripts. - -Example usage in your `build.rs` (using the `cc` crate) would be as follows: - -```rust , ignore -fn main() { - let mut builder = cc::Build::new(); - builder.compiler("clang"); - builder.file("my_script_using_blocks.c"); - - for flag in std::env::var("DEP_BLOCK_0_2_CC_ARGS").unwrap().split(' ') { - builder.flag(flag); - } - - builder.compile("libmy_script_using_blocks.a"); -} -``` - -[block-lang]: https://clang.llvm.org/docs/BlockLanguageSpec.html diff --git a/crates/block-sys/build.rs b/crates/block-sys/build.rs deleted file mode 100644 index 72bfa1c87..000000000 --- a/crates/block-sys/build.rs +++ /dev/null @@ -1,76 +0,0 @@ -use std::env; -use std::path::Path; - -fn main() { - // Only rerun if this file changes; the script doesn't depend on our code - println!("cargo:rerun-if-changed=build.rs"); - - let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); - - let mut apple = env::var_os("CARGO_FEATURE_APPLE").is_some(); - let compiler_rt = env::var_os("CARGO_FEATURE_COMPILER_RT").is_some(); - let mut gnustep = env::var_os("CARGO_FEATURE_GNUSTEP_1_7").is_some(); - let objfw = env::var_os("CARGO_FEATURE_UNSTABLE_OBJFW").is_some(); - - // Only when the crate is being compiled directly - if cfg!(feature = "unstable-docsrs") { - if let "macos" | "ios" | "tvos" | "watchos" = &*target_os { - apple = true; - // Add cheaty #[cfg(feature = "apple")] directive - println!("cargo:rustc-cfg=feature=\"apple\""); - } else { - // Also winobjc - gnustep = true; - // Add cheaty #[cfg(feature = "gnustep-1-7")] directive - println!("cargo:rustc-cfg=feature=\"gnustep-1-7\""); - } - } - - let mut cc_args = "-fblocks".to_owned(); - - match (apple, compiler_rt, gnustep, objfw) { - (true, false, false, false) => { - // Link to libclosure (internally called libsystem_blocks), 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. - println!("cargo:rustc-link-lib=dylib=System"); - // Alternative: Only link to libsystem_blocks.dylib - // println!("cargo:rustc-link-search=native=/usr/lib/system"); - // println!("cargo:rustc-link-lib=dylib=system_blocks"); - } - (false, true, false, false) => { - println!("cargo:rustc-link-lib=dylib=BlocksRuntime"); - } - (false, false, true, false) => { - // Don't link to anything; objc-sys already does that for us! - - // Add GNUStep compability headers to make `#include ` - // work (on newer GNUStep versions these headers are present) - if env::var_os("CARGO_FEATURE_GNUSTEP_2_0").is_none() { - let compat_headers = - Path::new(env!("CARGO_MANIFEST_DIR")).join("compat-headers/gnustep"); - cc_args.push_str(" -I"); - cc_args.push_str(compat_headers.to_str().unwrap()); - } - } - (false, false, false, true) => { - // Add compability headers to make `#include ` work. - let compat_headers = Path::new(env!("CARGO_MANIFEST_DIR")).join("compat-headers/objfw"); - cc_args.push_str(" -I"); - cc_args.push_str(compat_headers.to_str().unwrap()); - println!("cargo:rustc-link-lib=dylib=objfw"); - unimplemented!("ObjFW is not yet supported") - } - // Checked in if-let above - (false, false, false, false) => { - panic!("Invalid feature combination; at least one runtime must be selected!") - } - (_, _, _, _) => panic!("Invalid feature combination; only one runtime may be selected!"), - } - - // Add DEP_BLOCK_[version]_CC_ARGS - println!("cargo:cc_args={cc_args}"); -} diff --git a/crates/block-sys/src/lib.rs b/crates/block-sys/src/lib.rs deleted file mode 100644 index 145226376..000000000 --- a/crates/block-sys/src/lib.rs +++ /dev/null @@ -1,455 +0,0 @@ -//! # Raw bindings to Apple's C language extension of blocks -//! -//! 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! -//! -//! See also the [`README.md`](https://crates.io/crates/block-sys) for more -//! background information, and for how to configure the desired runtime. -//! -//! [ABI]: https://clang.llvm.org/docs/Block-ABI-Apple.html - -// TODO: Replace `extern "C"` with `extern "C-unwind"` where applicable. -// See https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html. - -#![no_std] -#![warn(elided_lifetimes_in_paths)] -#![warn(missing_copy_implementations)] -#![deny(non_ascii_idents)] -#![warn(unreachable_pub)] -#![deny(unsafe_op_in_unsafe_fn)] -#![warn(clippy::cargo)] -#![warn(clippy::ptr_as_ptr)] -#![warn(clippy::missing_errors_doc)] -#![warn(clippy::missing_panics_doc)] -#![allow(non_camel_case_types)] -// Update in Cargo.toml as well. -#![doc(html_root_url = "https://docs.rs/block-sys/0.2.0")] -#![cfg_attr(feature = "unstable-docsrs", feature(doc_auto_cfg, doc_cfg_hide))] -#![cfg_attr(feature = "unstable-docsrs", doc(cfg_hide(doc)))] - -extern crate std; - -#[cfg(not(feature = "std"))] -compile_error!("The `std` feature currently must be enabled."); - -// Ensure linkage actually happens -#[cfg(feature = "gnustep-1-7")] -extern crate objc_sys as _; - -#[cfg(doctest)] -#[doc = include_str!("../README.md")] -extern "C" {} - -use core::cell::UnsafeCell; -use core::ffi::c_void; -use core::marker::{PhantomData, PhantomPinned}; -use std::os::raw::{c_char, c_ulong}; - -#[repr(C)] -pub struct Class { - #[cfg(any(feature = "apple", feature = "compiler-rt"))] - _priv: [*mut c_void; 32], - - #[cfg(any(feature = "gnustep-1-7", feature = "objfw"))] - // The size of this is unknown - _priv: [u8; 0], - - /// See objc_sys::OpaqueData - _opaque: UnsafeCell, PhantomPinned)>>, -} - -/// Block descriptor flags. -/// Values for Block_layout->flags to describe block objects -#[allow(non_camel_case_types)] -pub type block_flags = i32; - -#[cfg(any(doc, feature = "apple"))] -pub const BLOCK_DEALLOCATING: block_flags = 0x0001; - -pub const BLOCK_REFCOUNT_MASK: block_flags = 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 = "objfw")) { - 0xffff -} else if cfg!(feature = "apple") { - 0xfffe // runtime -} else { - 0 -}; - -#[cfg(any(doc, feature = "apple"))] -/// compiler -pub const BLOCK_INLINE_LAYOUT_STRING: block_flags = 1 << 21; - -#[cfg(any(doc, feature = "apple"))] -/// compiler -pub const BLOCK_SMALL_DESCRIPTOR: block_flags = 1 << 22; - -#[cfg(any(doc, feature = "apple"))] // Part of ABI? -/// compiler -pub const BLOCK_IS_NOESCAPE: block_flags = 1 << 23; - -#[cfg(any(doc, feature = "apple"))] -/// runtime -pub const BLOCK_NEEDS_FREE: block_flags = 1 << 24; - -/// The block descriptor contains copy and dispose helpers. -/// compiler -pub const BLOCK_HAS_COPY_DISPOSE: block_flags = 1 << 25; - -/// The helpers have C++ code. -/// compiler: helpers have C++ code -pub const BLOCK_HAS_CTOR: block_flags = 1 << 26; - -#[cfg(any(doc, feature = "apple"))] -/// compiler -pub const BLOCK_IS_GC: block_flags = 1 << 27; - -/// Block is stored in global memory and does not need to be copied. -/// compiler -pub const BLOCK_IS_GLOBAL: block_flags = 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 -#[doc(alias = "BLOCK_USE_SRET")] -#[doc(alias = "BLOCK_HAS_DESCRIPTOR")] // compiler-rt || macOS 10.6 -pub const BLOCK_USE_STRET: block_flags = 1 << 29; - -/// Block has an Objective-C type encoding. -/// compiler -pub const BLOCK_HAS_SIGNATURE: block_flags = 1 << 30; - -#[cfg(any(doc, feature = "apple"))] -/// compiler -pub const BLOCK_HAS_EXTENDED_LAYOUT: block_flags = 1 << 31; - -/// Flags used in the final argument to _Block_object_assign() and -/// _Block_object_dispose(). These indicate the type of copy or dispose to -/// perform. -/// Values for _Block_object_assign() and _Block_object_dispose() parameters -/// -/// This is a helper type, in the sources this type does not have a name! -#[allow(non_camel_case_types)] -pub type block_assign_dispose_flags = i32; - -/// 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 const BLOCK_FIELD_IS_OBJECT: block_assign_dispose_flags = 3; - -/// The field is a block. This must be copied by the block copy functions. -/// -/// a block variable -pub const BLOCK_FIELD_IS_BLOCK: block_assign_dispose_flags = 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 const BLOCK_FIELD_IS_BYREF: block_assign_dispose_flags = 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 const BLOCK_FIELD_IS_WEAK: block_assign_dispose_flags = 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 const BLOCK_BYREF_CALLER: block_assign_dispose_flags = 128; - -#[cfg(any(doc, feature = "apple"))] -pub const BLOCK_ALL_COPY_DISPOSE_FLAGS: block_assign_dispose_flags = BLOCK_FIELD_IS_OBJECT - | BLOCK_FIELD_IS_BLOCK - | BLOCK_FIELD_IS_BYREF - | BLOCK_FIELD_IS_WEAK - | BLOCK_BYREF_CALLER; - -// TODO: BLOCK_LAYOUT_X - -extern "C" { - // the raw data space for runtime classes for blocks - // class+meta used for stack, malloc, and collectable based blocks - - pub static _NSConcreteGlobalBlock: Class; - pub static _NSConcreteStackBlock: Class; - pub static _NSConcreteMallocBlock: Class; - #[cfg(any(doc, feature = "apple", feature = "compiler-rt"))] - pub static _NSConcreteAutoBlock: Class; - #[cfg(any(doc, feature = "apple", feature = "compiler-rt"))] - pub static _NSConcreteFinalizingBlock: Class; - #[cfg(any(doc, feature = "apple", feature = "compiler-rt"))] - pub static _NSConcreteWeakBlockVariable: Class; - - pub fn _Block_copy(block: *const c_void) -> *mut c_void; - pub fn _Block_release(block: *const c_void); - - /// Runtime entry point called by compiler when assigning objects inside - /// copy helper routines - pub fn _Block_object_assign( - dest_addr: *mut c_void, - object: *const c_void, - flags: block_assign_dispose_flags, - ); - - /// runtime entry point called by the compiler when disposing of objects - /// inside dispose helper routine - pub fn _Block_object_dispose(object: *const c_void, flags: block_assign_dispose_flags); - - #[cfg(any(doc, feature = "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, feature = "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, feature = "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, feature = "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, feature = "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, feature = "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, feature = "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, feature = "apple", feature = "gnustep-1-7"))] - // pub fn _Block_signature(block: *mut c_void) -> *const c_char; -} - -#[repr(C)] -pub struct Block_layout { - /// 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 `block_flags` enumerated type for possible values. - /// Contains ref count in Apple and ObjFW. - pub flags: block_flags, - /// Reserved - always initialised to 0 by the compiler (but this is not - /// said in the specification). - /// - /// Used for the reference count in GNUStep and WinObjC. - pub reserved: i32, - /// The function that implements the block. The first argument is this - /// structure, the subsequent arguments are the block's explicit - /// parameters. If the BLOCK_USE_SRET & BLOCK_HAS_SIGNATURE flag is set, - /// there is an additional hidden argument, which is a pointer to the - /// space on the stack allocated to hold the return value. - pub invoke: Option, - /// The block's descriptor. The actual type of this is: - /// ```pseudo-code - /// match (BLOCK_HAS_COPY_DISPOSE, BLOCK_HAS_SIGNATURE) { - /// (false, false) => Block_descriptor_header, - /// (true, false) => Block_descriptor, - /// (false, true) => Block_descriptor_basic, - /// (true, true) => Block_descriptor_with_signature, - /// } - /// ``` - /// - /// But since all of these start with `Block_descriptor_header`, it is - /// always safe to reinterpret this pointer as that. - // Note: Important to use `*const c_void` until we know which type it is! - pub descriptor: *const c_void, -} - -#[repr(C)] -#[allow(missing_copy_implementations)] -pub struct Block_descriptor_header { - /// Reserved for future use. Currently always 0. - pub reserved: c_ulong, // usize - /// Size of the block. - pub size: c_ulong, // usize -} - -/// Block descriptor that contains copy and dispose operations. -/// -/// Requires BLOCK_HAS_COPY_DISPOSE -#[repr(C)] -pub struct Block_descriptor { - pub header: Block_descriptor_header, - - /// Copy function, generated by the compiler to help copy the block if it - /// contains nontrivial copy operations. - pub copy: Option, - /// Dispose function, generated by the compiler to help copy the block if - /// it contains nontrivial destructors. - pub dispose: Option, -} - -/// Extended block descriptor that does not contain copy and dispose helper -/// functions. -/// -/// Requires BLOCK_HAS_SIGNATURE -#[repr(C)] -pub struct Block_descriptor_basic { - pub header: Block_descriptor_header, - - /// Objective-C type encoding of the block. - #[doc(alias = "signature")] - pub encoding: *const c_char, -} - -/// Requires BLOCK_HAS_COPY_DISPOSE and BLOCK_HAS_SIGNATURE -#[repr(C)] -pub struct Block_descriptor_with_signature { - pub header: Block_descriptor_header, - - /// Same as [`Block_descriptor::copy`]. - pub copy: Option, - /// Same as [`Block_descriptor::dispose`]. - pub dispose: Option, - - /// Objective-C type encoding of the block. - #[doc(alias = "signature")] - pub encoding: *const c_char, -} - -// #[cfg(any(doc, feature = "apple"))] -// pub layout: *const c_char, - -// #[repr(C)] -// pub struct Block_descriptor_small { -// pub size: u32, -// pub signature: i32, -// pub layout: i32, -// pub copy: i32, -// pub dispose: i32, -// } - -// #[repr(C)] -// pub struct Block_basic { -// pub isa: *mut c_void, -// pub Block_flags: i32, -// pub Block_size: i32, -// pub Block_invoke: Option, -// pub Block_copy: Option, -// pub Block_dispose: Option, -// } -// Example usage: https://github.com/apple-oss-distributions/libdispatch/blob/libdispatch-84.5/src/once.c - -/// Structure used for on-stack variables that are referenced by blocks. -#[repr(C)] -#[doc(alias = "Block_byref_1")] -pub struct Block_byref_header { - /// Class pointer. Currently unused on GNUStep and always NULL. Could be - /// used in the future to support introspection. - pub isa: *const Class, - /// The pointer to the structure that contains the real version of the - /// data. All accesses go via this pointer. If an on-stack byref structure - /// is copied to the heap, then its forwarding pointer should point to the - /// heap version. Otherwise it should point to itself. - pub forwarding: *mut Block_byref_header, - /// Flags and reference count. - /// - /// TODO: Volatile! - pub flags: block_flags, - #[cfg(feature = "apple")] - /// Size of this structure. - pub size: u32, - #[cfg(not(feature = "apple"))] - /// Size of this structure. - pub size: i32, -} - -/// Structure used for on-stack variables that are referenced by blocks. -/// -/// requires BLOCK_BYREF_HAS_COPY_DISPOSE -#[repr(C)] -#[doc(alias = "Block_byref_2")] -pub struct Block_byref { - pub header: Block_byref_header, - /// Copy function. - pub keep: Option, - /// Dispose function. - pub destroy: Option, -} - -#[cfg(any(doc, feature = "apple"))] -/// Structure used for on-stack variables that are referenced by blocks. -/// -/// requires BLOCK_BYREF_LAYOUT_EXTENDED -#[repr(C)] -#[doc(alias = "Block_byref_3")] -pub struct Block_byref_extended { - pub header: Block_byref_header, - /// Same as [`Block_byref::keep`]. - pub keep: Option, - /// Same as [`Block_byref::destroy`]. - pub destroy: Option, - pub layout: *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()); - } - - #[test] - fn test_linkable() { - println!("{:p}", unsafe { &_NSConcreteGlobalBlock }); - println!("{:p}", unsafe { &_NSConcreteStackBlock }); - println!("{:p}", unsafe { &_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(_)); - #[cfg(any(feature = "apple", feature = "compiler-rt"))] - { - println!("{:p}", unsafe { &_NSConcreteAutoBlock }); - println!("{:p}", unsafe { &_NSConcreteFinalizingBlock }); - println!("{:p}", unsafe { &_NSConcreteWeakBlockVariable }); - println!("{:p}", Block_size as unsafe extern "C" fn(_) -> _); - } - } -} diff --git a/crates/block2/CHANGELOG.md b/crates/block2/CHANGELOG.md index 9e24815d4..bf777ac50 100644 --- a/crates/block2/CHANGELOG.md +++ b/crates/block2/CHANGELOG.md @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). `_Block_release`, `_Block_object_assign`, `_Block_object_dispose`, `_NSConcreteGlobalBlock`, `_NSConcreteStackBlock` and `Class` in `ffi` module. +* No longer use the `block-sys` crate for linking to the blocks runtime. ## 0.4.0 - 2023-12-03 @@ -76,7 +77,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed * **BREAKING**: Updated `objc2-encode` to `v2.0.0-pre.0`. -* **BREAKING**: Updated `ffi` module to `block-sys v0.0.4`. +* **BREAKING**: Updated `ffi` module to `block-sys v0.0.4`. This tweaks the + types of a lot of fields and arguments, and makes the apple runtime always + be the default. ### Removed * **BREAKING**: Removed `DerefMut` implementation for `ConcreteBlock`. @@ -97,7 +100,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). creating blocks that don't reference their environment. ### Changed -* **BREAKING**: Updated `ffi` module to `block-sys v0.0.2` +* **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 diff --git a/crates/block2/Cargo.toml b/crates/block2/Cargo.toml index 86fd02465..3998881dc 100644 --- a/crates/block2/Cargo.toml +++ b/crates/block2/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "block2" -# Remember to update html_root_url in lib.rs and README.md +# Remember to update html_root_url in lib.rs version = "0.4.0" authors = ["Steven Sheldon", "Mads Marquart "] edition = "2021" @@ -12,6 +12,7 @@ categories = [ "api-bindings", "development-tools::ffi", "os::macos-apis", + "external-ffi-bindings", ] readme = "README.md" repository = "https://github.com/madsmtm/objc2" @@ -19,24 +20,38 @@ documentation = "https://docs.rs/block2/" license = "MIT" [features] +# The default runtime is Apple's. Other platforms will probably error if the +# correct feature flag is not specified. default = ["std", "apple"] # Currently not possible to turn off, put here for forwards compatibility. -std = ["alloc", "objc2/std", "block-sys/std"] -alloc = ["objc2/alloc", "block-sys/alloc"] - -# Runtime selection. Default is `apple`. See `block-sys` for details. -apple = ["block-sys/apple", "objc2/apple"] -compiler-rt = ["block-sys/compiler-rt", "objc2/unstable-compiler-rt"] # TODO: fix this -gnustep-1-7 = ["block-sys/gnustep-1-7", "objc2/gnustep-1-7"] -gnustep-1-8 = ["gnustep-1-7", "block-sys/gnustep-1-8", "objc2/gnustep-1-8"] -gnustep-1-9 = ["gnustep-1-8", "block-sys/gnustep-1-9", "objc2/gnustep-1-9"] -gnustep-2-0 = ["gnustep-1-9", "block-sys/gnustep-2-0", "objc2/gnustep-2-0"] -gnustep-2-1 = ["gnustep-2-0", "block-sys/gnustep-2-1", "objc2/gnustep-2-1"] +std = ["alloc", "objc2/std"] +alloc = ["objc2/alloc"] + +# Link to Apple's libclosure (exists in libSystem). +apple = ["objc2/apple"] + +# Link to libBlocksRuntime from compiler-rt. +compiler-rt = ["objc2/unstable-compiler-rt"] # TODO: fix this + +# Link to GNUStep's libobjc2 (which contains the block implementation). +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"] + +# Link to Microsoft's libobjc2. +unstable-winobjc = ["gnustep-1-8"] + +# Link to ObjFW. +unstable-objfw = [] + +# Expose private ffi functions and statics. +unstable-private = [] [dependencies] objc2 = { path = "../objc2", version = "0.5.0", default-features = false } -block-sys = { path = "../block-sys", version = "0.2.0", default-features = false } [package.metadata.docs.rs] default-target = "x86_64-apple-darwin" diff --git a/crates/block2/src/abi.rs b/crates/block2/src/abi.rs index 159d0e94e..fe5ab024e 100644 --- a/crates/block2/src/abi.rs +++ b/crates/block2/src/abi.rs @@ -1 +1,312 @@ -pub(crate) use block_sys::*; +//! 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( + unreachable_pub, + unused, + non_camel_case_types, + missing_debug_implementations +)] + +use core::ffi::c_void; +use std::os::raw::{c_char, c_ulong}; + +use crate::ffi::Class; + +/// Block descriptor flags. +/// Values for Block_layout->flags to describe block objects +#[allow(non_camel_case_types)] +pub type block_flags = i32; + +#[cfg(any(doc, feature = "apple"))] +pub const BLOCK_DEALLOCATING: block_flags = 0x0001; + +pub const BLOCK_REFCOUNT_MASK: block_flags = 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 = "objfw")) { + 0xffff +} else if cfg!(feature = "apple") { + 0xfffe // runtime +} else { + 0 +}; + +#[cfg(any(doc, feature = "apple"))] +/// compiler +pub const BLOCK_INLINE_LAYOUT_STRING: block_flags = 1 << 21; + +#[cfg(any(doc, feature = "apple"))] +/// compiler +pub const BLOCK_SMALL_DESCRIPTOR: block_flags = 1 << 22; + +#[cfg(any(doc, feature = "apple"))] // Part of ABI? +/// compiler +pub const BLOCK_IS_NOESCAPE: block_flags = 1 << 23; + +#[cfg(any(doc, feature = "apple"))] +/// runtime +pub const BLOCK_NEEDS_FREE: block_flags = 1 << 24; + +/// The block descriptor contains copy and dispose helpers. +/// compiler +pub const BLOCK_HAS_COPY_DISPOSE: block_flags = 1 << 25; + +/// The helpers have C++ code. +/// compiler: helpers have C++ code +pub const BLOCK_HAS_CTOR: block_flags = 1 << 26; + +#[cfg(any(doc, feature = "apple"))] +/// compiler +pub const BLOCK_IS_GC: block_flags = 1 << 27; + +/// Block is stored in global memory and does not need to be copied. +/// compiler +pub const BLOCK_IS_GLOBAL: block_flags = 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 +#[doc(alias = "BLOCK_USE_SRET")] +#[doc(alias = "BLOCK_HAS_DESCRIPTOR")] // compiler-rt || macOS 10.6 +pub const BLOCK_USE_STRET: block_flags = 1 << 29; + +/// Block has an Objective-C type encoding. +/// compiler +pub const BLOCK_HAS_SIGNATURE: block_flags = 1 << 30; + +#[cfg(any(doc, feature = "apple"))] +/// compiler +pub const BLOCK_HAS_EXTENDED_LAYOUT: block_flags = 1 << 31; + +/// Flags used in the final argument to _Block_object_assign() and +/// _Block_object_dispose(). These indicate the type of copy or dispose to +/// perform. +/// Values for _Block_object_assign() and _Block_object_dispose() parameters +/// +/// This is a helper type, in the sources this type does not have a name! +#[allow(non_camel_case_types)] +pub type block_assign_dispose_flags = i32; + +/// 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 const BLOCK_FIELD_IS_OBJECT: block_assign_dispose_flags = 3; + +/// The field is a block. This must be copied by the block copy functions. +/// +/// a block variable +pub const BLOCK_FIELD_IS_BLOCK: block_assign_dispose_flags = 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 const BLOCK_FIELD_IS_BYREF: block_assign_dispose_flags = 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 const BLOCK_FIELD_IS_WEAK: block_assign_dispose_flags = 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 const BLOCK_BYREF_CALLER: block_assign_dispose_flags = 128; + +#[cfg(any(doc, feature = "apple"))] +pub const BLOCK_ALL_COPY_DISPOSE_FLAGS: block_assign_dispose_flags = BLOCK_FIELD_IS_OBJECT + | BLOCK_FIELD_IS_BLOCK + | BLOCK_FIELD_IS_BYREF + | BLOCK_FIELD_IS_WEAK + | BLOCK_BYREF_CALLER; + +// TODO: BLOCK_LAYOUT_X + +/// The expected layout of every block. +#[repr(C)] +#[doc(alias = "__block_literal")] +pub struct Block_layout { + /// 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 `block_flags` enumerated type for possible values. + /// Contains ref count in Apple and ObjFW. + pub flags: block_flags, + /// Reserved - always initialised to 0 by the compiler (but this is not + /// said in the specification). + /// + /// Used for the reference count in GNUStep and WinObjC. + pub reserved: i32, + /// The function that implements the block. The first argument is this + /// structure, the subsequent arguments are the block's explicit + /// parameters. If the BLOCK_USE_SRET & BLOCK_HAS_SIGNATURE flag is set, + /// there is an additional hidden argument, which is a pointer to the + /// space on the stack allocated to hold the return value. + pub invoke: Option, + /// The block's descriptor. The actual type of this is: + /// ```pseudo-code + /// match (BLOCK_HAS_COPY_DISPOSE, BLOCK_HAS_SIGNATURE) { + /// (false, false) => Block_descriptor_header, + /// (true, false) => Block_descriptor, + /// (false, true) => Block_descriptor_basic, + /// (true, true) => Block_descriptor_with_signature, + /// } + /// ``` + /// + /// But since all of these start with `Block_descriptor_header`, it is + /// always safe to reinterpret this pointer as that. + // Note: Important to use `*const c_void` until we know which type it is! + pub descriptor: *const c_void, +} + +#[repr(C)] +#[allow(missing_copy_implementations)] +#[doc(alias = "__block_descriptor")] +#[doc(alias = "Block_descriptor_1")] +pub struct Block_descriptor_header { + /// Reserved for future use. Currently always 0. + pub reserved: c_ulong, // usize + /// Size of the block. + pub size: c_ulong, // usize +} + +/// Block descriptor that contains copy and dispose operations. +/// +/// Requires BLOCK_HAS_COPY_DISPOSE +#[repr(C)] +#[doc(alias = "Block_descriptor_2")] +pub struct Block_descriptor { + pub header: Block_descriptor_header, + + /// Copy function, generated by the compiler to help copy the block if it + /// contains nontrivial copy operations. + pub copy: Option, + /// Dispose function, generated by the compiler to help copy the block if + /// it contains nontrivial destructors. + pub dispose: Option, +} + +/// Extended block descriptor that does not contain copy and dispose helper +/// functions. +/// +/// Requires BLOCK_HAS_SIGNATURE +#[repr(C)] +#[doc(alias = "Block_descriptor_3")] +pub struct Block_descriptor_basic { + pub header: Block_descriptor_header, + + /// Objective-C type encoding of the block. + #[doc(alias = "signature")] + pub encoding: *const c_char, +} + +/// Requires BLOCK_HAS_COPY_DISPOSE and BLOCK_HAS_SIGNATURE +#[repr(C)] +#[doc(alias = "Block_descriptor_2")] +#[doc(alias = "Block_descriptor_3")] +pub struct Block_descriptor_with_signature { + pub header: Block_descriptor_header, + + /// Same as [`Block_descriptor::copy`]. + pub copy: Option, + /// Same as [`Block_descriptor::dispose`]. + pub dispose: Option, + + /// Objective-C type encoding of the block. + #[doc(alias = "signature")] + pub encoding: *const c_char, +} + +// #[cfg(any(doc, feature = "apple"))] +// pub layout: *const c_char, + +// #[repr(C)] +// pub struct Block_descriptor_small { +// pub size: u32, +// pub signature: i32, +// pub layout: i32, +// pub copy: i32, +// pub dispose: i32, +// } + +// #[repr(C)] +// pub struct Block_basic { +// pub isa: *mut Class, +// pub Block_flags: i32, +// pub Block_size: i32, +// pub Block_invoke: Option, +// pub Block_copy: Option, +// pub Block_dispose: Option, +// } +// Example usage: https://github.com/apple-oss-distributions/libdispatch/blob/libdispatch-84.5/src/once.c + +/// Structure used for on-stack variables that are referenced by blocks. +#[repr(C)] +#[doc(alias = "Block_byref_1")] +#[doc(alias = "_block_byref")] +pub struct Block_byref_header { + /// Class pointer. Currently unused on GNUStep and always NULL. Could be + /// used in the future to support introspection. + pub isa: *const Class, + /// The pointer to the structure that contains the real version of the + /// data. All accesses go via this pointer. If an on-stack byref structure + /// is copied to the heap, then its forwarding pointer should point to the + /// heap version. Otherwise it should point to itself. + pub forwarding: *mut Block_byref_header, + /// Flags and reference count. + /// + /// TODO: Volatile! + pub flags: block_flags, + #[cfg(feature = "apple")] + /// Size of this structure. + pub size: u32, + #[cfg(not(feature = "apple"))] + /// Size of this structure. + pub size: i32, +} + +/// Structure used for on-stack variables that are referenced by blocks. +/// +/// requires BLOCK_BYREF_HAS_COPY_DISPOSE +#[repr(C)] +#[doc(alias = "Block_byref_2")] +pub struct Block_byref { + pub header: Block_byref_header, + /// Copy function. + pub keep: Option, + /// Dispose function. + pub destroy: Option, +} + +#[cfg(any(doc, feature = "apple"))] +/// Structure used for on-stack variables that are referenced by blocks. +/// +/// requires BLOCK_BYREF_LAYOUT_EXTENDED +#[repr(C)] +#[doc(alias = "Block_byref_3")] +pub struct Block_byref_extended { + pub header: Block_byref_header, + /// Same as [`Block_byref::keep`]. + pub keep: Option, + /// Same as [`Block_byref::destroy`]. + pub destroy: Option, + pub layout: *const c_char, +} diff --git a/crates/block2/src/debug.rs b/crates/block2/src/debug.rs index c1b9d486a..430a87d98 100644 --- a/crates/block2/src/debug.rs +++ b/crates/block2/src/debug.rs @@ -7,7 +7,7 @@ use std::ffi::CStr; use crate::{abi, ffi, Block, ConcreteBlock, GlobalBlock, RcBlock}; #[derive(Clone, Copy, PartialEq, Eq)] -struct Isa(*const abi::Class); +struct Isa(*const ffi::Class); impl Isa { fn is_global(self) -> bool { @@ -222,7 +222,7 @@ mod tests { let isa = Isa(unsafe { &ffi::_NSConcreteStackBlock }); assert!(!isa.is_global()); assert!(isa.is_stack()); - let isa = Isa(unsafe { &abi::_NSConcreteMallocBlock }); + let isa = Isa(unsafe { &ffi::private::_NSConcreteMallocBlock }); assert!(!isa.is_global()); assert!(!isa.is_stack()); let isa = Isa(ptr::null()); diff --git a/crates/block2/src/ffi.rs b/crates/block2/src/ffi.rs index a338aac1e..a8776d177 100644 --- a/crates/block2/src/ffi.rs +++ b/crates/block2/src/ffi.rs @@ -1,6 +1,167 @@ -//! `Block.h` +//! # Raw bindings to `Block.h` -pub use block_sys::{ - Class, _Block_copy, _Block_object_assign, _Block_object_dispose, _Block_release, - _NSConcreteGlobalBlock, _NSConcreteStackBlock, -}; +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. + #[cfg(any(feature = "apple", feature = "compiler-rt"))] + _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 = "objfw"))] + _priv: [u8; 0], + + /// Mark as `!Send + !Sync + !Unpin` and as mutable behind shared + /// references (`!Freeze`). + /// + /// Same as `objc_sys::OpaqueData`. + _opaque: 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, feature = "apple", feature = "gnustep-1-7"))] + use std::os::raw::c_char; + #[cfg(any(doc, feature = "apple", feature = "compiler-rt"))] + use std::os::raw::c_ulong; + + extern "C" { + pub static _NSConcreteMallocBlock: Class; + #[cfg(any(doc, feature = "apple", feature = "compiler-rt"))] + pub static _NSConcreteAutoBlock: Class; + #[cfg(any(doc, feature = "apple", feature = "compiler-rt"))] + pub static _NSConcreteFinalizingBlock: Class; + #[cfg(any(doc, feature = "apple", feature = "compiler-rt"))] + pub static _NSConcreteWeakBlockVariable: Class; + + #[cfg(any(doc, feature = "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, feature = "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, feature = "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, feature = "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, feature = "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, feature = "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, feature = "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, feature = "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!("{:p}", unsafe { &_NSConcreteGlobalBlock }); + println!("{:p}", unsafe { &_NSConcreteStackBlock }); + println!("{:p}", unsafe { &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(_)); + } +} diff --git a/crates/block2/src/lib.rs b/crates/block2/src/lib.rs index a313eee7e..5cf34aedb 100644 --- a/crates/block2/src/lib.rs +++ b/crates/block2/src/lib.rs @@ -12,6 +12,10 @@ //! (Note that while this library can be used separately from Objective-C, //! they're most commonly used together). //! +//! [lang]: https://clang.llvm.org/docs/BlockLanguageSpec.html +//! [ABI]: http://clang.llvm.org/docs/Block-ABI-Apple.html +//! +//! //! ## Invoking blocks //! //! The [`Block`] struct is used for invoking blocks from Objective-C. For @@ -38,6 +42,7 @@ //! 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 the @@ -71,8 +76,93 @@ //! assert_eq!(unsafe { MY_BLOCK.call(()) }, 10.0); //! ``` //! -//! [lang]: https://clang.llvm.org/docs/BlockLanguageSpec.html -//! [ABI]: http://clang.llvm.org/docs/Block-ABI-Apple.html +//! +//! ## 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). Note that if the `objc2` crate is present in the +//! dependency tree, this should have the same feature flag enabled as that. +//! +//! +//! ### Apple's [`libclosure`](https://github.com/apple-oss-distributions/libclosure) +//! +//! - Feature flag: `apple`. +//! +//! This is the most common an 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 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. #![no_std] #![warn(elided_lifetimes_in_paths)] @@ -92,11 +182,50 @@ extern crate alloc; extern crate std; +#[cfg(doctest)] +#[doc = include_str!("../README.md")] +extern "C" {} + #[cfg(not(feature = "std"))] compile_error!("The `std` feature currently must be enabled."); -#[cfg(doctest)] -#[doc = include_str!("../README.md")] +#[cfg(not(any( + feature = "apple", + feature = "compiler-rt", + feature = "gnustep-1-7", + feature = "unstable-objfw", +)))] +compile_error!("A runtime must be selected"); + +#[cfg(any( + all(feature = "apple", feature = "compiler-rt"), + all(feature = "compiler-rt", feature = "gnustep-1-7"), + all(feature = "gnustep-1-7", feature = "unstable-objfw"), + all(feature = "unstable-objfw", feature = "apple"), + all(feature = "apple", feature = "gnustep-1-7"), + 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(feature = "apple", link(name = "System", kind = "dylib"))] +// Link to `libBlocksRuntime`. +#[cfg_attr(feature = "compiler-rt", link(name = "BlocksRuntime", kind = "dylib"))] +// Don't link to anything on GNUStep; objc2 already does that for us! +// #[cfg_attr(feature = "gnustep-1-7")] +// Link to `libobjfw`, which provides the blocks implementation. +#[cfg_attr(feature = "unstable-objfw", link(name = "objfw", kind = "dylib"))] extern "C" {} mod abi; @@ -107,9 +236,9 @@ pub mod ffi; mod global; mod rc_block; -pub use block::{Block, BlockArguments}; #[doc(hidden)] -pub use block_sys::Block_layout as __Block_layout; +pub use abi::Block_layout as __Block_layout; +pub use block::{Block, BlockArguments}; pub use concrete_block::{ConcreteBlock, IntoConcreteBlock}; pub use global::GlobalBlock; pub use rc_block::RcBlock; diff --git a/crates/tests/Cargo.toml b/crates/tests/Cargo.toml index 8ec52d52a..9a6991791 100644 --- a/crates/tests/Cargo.toml +++ b/crates/tests/Cargo.toml @@ -39,7 +39,6 @@ gnustep-2-1 = ["gnustep-2-0", "block2/gnustep-2-1", "objc2/gnustep-2-1", "icrate [dependencies] block2 = { path = "../block2", default-features = false } -block-sys = { path = "../block-sys", default-features = false } objc-sys = { path = "../objc-sys", default-features = false } objc2 = { path = "../objc2", default-features = false } icrate = { path = "../icrate", default-features = false } diff --git a/crates/tests/build.rs b/crates/tests/build.rs index b0836ecda..093cf4ca1 100644 --- a/crates/tests/build.rs +++ b/crates/tests/build.rs @@ -8,14 +8,19 @@ fn main() { builder.file("extern/block_utils.c"); println!("cargo:rerun-if-changed=extern/block_utils.c"); - for flag in env::var("DEP_BLOCK_0_2_CC_ARGS").unwrap().split(' ') { + for flag in env::var("DEP_OBJC_0_3_CC_ARGS").unwrap().split(' ') { builder.flag(flag); } - for flag in env::var("DEP_OBJC_0_3_CC_ARGS").unwrap().split(' ') { - builder.flag(flag); + if cfg!(feature = "gnustep-1-7") && !cfg!(feature = "gnustep-2-0") { + builder.include("compat-headers/gnustep-pre-2-0"); } + if cfg!(feature = "unstable-objfw") { + builder.include("compat-headers/objfw"); + } + + builder.flag("-fblocks"); builder.flag("-fno-objc-arc"); builder.flag("-xobjective-c"); @@ -29,9 +34,7 @@ fn main() { println!("cargo:rerun-if-changed=extern/encode_utils.m"); println!("cargo:rerun-if-changed=extern/test_object.m"); - for flag in env::var("DEP_BLOCK_0_2_CC_ARGS").unwrap().split(' ') { - builder.flag(flag); - } + builder.flag("-fblocks"); for flag in env::var("DEP_OBJC_0_3_CC_ARGS").unwrap().split(' ') { builder.flag(flag); diff --git a/crates/block-sys/compat-headers/gnustep/Block.h b/crates/tests/compat-headers/gnustep-pre-2-0/Block.h similarity index 100% rename from crates/block-sys/compat-headers/gnustep/Block.h rename to crates/tests/compat-headers/gnustep-pre-2-0/Block.h diff --git a/crates/block-sys/compat-headers/gnustep/Block_private.h b/crates/tests/compat-headers/gnustep-pre-2-0/Block_private.h similarity index 100% rename from crates/block-sys/compat-headers/gnustep/Block_private.h rename to crates/tests/compat-headers/gnustep-pre-2-0/Block_private.h diff --git a/crates/block-sys/compat-headers/objfw/Block.h b/crates/tests/compat-headers/objfw/Block.h similarity index 100% rename from crates/block-sys/compat-headers/objfw/Block.h rename to crates/tests/compat-headers/objfw/Block.h