Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
1
third-party/vendor/objc2/.cargo-checksum.json
vendored
Normal file
1
third-party/vendor/objc2/.cargo-checksum.json
vendored
Normal file
File diff suppressed because one or more lines are too long
784
third-party/vendor/objc2/CHANGELOG.md
vendored
Normal file
784
third-party/vendor/objc2/CHANGELOG.md
vendored
Normal file
|
|
@ -0,0 +1,784 @@
|
|||
# Changelog
|
||||
|
||||
Notable changes to this crate will be documented in this file. See the
|
||||
`CHANGELOG_FOUNDATION.md` file for changes to the `objc2::foundation` module!
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## Unreleased - YYYY-MM-DD
|
||||
|
||||
## 0.3.0-beta.3 - 2022-09-01
|
||||
|
||||
### Added
|
||||
* Added `Ivar::write`, `Ivar::as_ptr` and `Ivar::as_mut_ptr` for safely
|
||||
querying and modifying instance variables inside `init` methods.
|
||||
* Added `IvarDrop<T>` to allow storing complex `Drop` values in ivars
|
||||
(currently `rc::Id<T, O>`, `Box<T>`, `Option<rc::Id<T, O>>` or
|
||||
`Option<Box<T>>`).
|
||||
* **BREAKING**: Added required `ClassType::NAME` constant for statically
|
||||
determining the name of a specific class.
|
||||
* Allow directly specifying class name in declare_class! macro.
|
||||
|
||||
### Removed
|
||||
* **BREAKING**: `MaybeUninit` no longer implements `IvarType` directly; use
|
||||
`Ivar::write` instead.
|
||||
|
||||
|
||||
## 0.3.0-beta.2 - 2022-08-28
|
||||
|
||||
### Added
|
||||
* Added the `"unstable-static-class"` and `"unstable-static-class-inlined"`
|
||||
feature flags to make the `class!` macro zero cost.
|
||||
* Moved the external crate `objc2_foundation` into `objc2::foundation` under
|
||||
(default) feature flag `"foundation"`.
|
||||
* Added `declare_class!`, `extern_class!` and `ns_string!` macros from
|
||||
`objc2-foundation`.
|
||||
* Added helper method `ClassBuilder::add_static_ivar`.
|
||||
* **BREAKING**: Added `ClassType` trait, and moved the associated `class`
|
||||
methods that `extern_class!` and `declare_class!` generated to that. This
|
||||
means you'll have to `use objc2::ClassType` whenever you want to use e.g.
|
||||
`NSData::class()`.
|
||||
* Added `Id::into_super`.
|
||||
* Added `extern_methods!` macro.
|
||||
* Added ability to call `msg_send![super(obj), ...]` without explicitly
|
||||
specifying the superclass.
|
||||
* Added automatic conversion of `bool` to/from the Objective-C `BOOL` in
|
||||
`msg_send!`, `msg_send_id!`, `extern_methods!` and `declare_class!`.
|
||||
|
||||
Example:
|
||||
```rust
|
||||
// Before
|
||||
use objc2::{msg_send, msg_send_bool};
|
||||
use objc2::rc::{Id, Shared};
|
||||
use objc2::runtime::{Bool, Object};
|
||||
|
||||
let obj: Id<Object, Shared>;
|
||||
let _: () = unsafe { msg_send![&obj, setArg: Bool::YES] };
|
||||
let is_equal = unsafe { msg_send_bool![&obj, isEqual: &*obj] };
|
||||
|
||||
// After
|
||||
use objc2::msg_send;
|
||||
use objc2::rc::{Id, Shared};
|
||||
use objc2::runtime::Object;
|
||||
|
||||
let obj: Id<Object, Shared>;
|
||||
let _: () = unsafe { msg_send![&obj, setArg: true] };
|
||||
let is_equal: bool = unsafe { msg_send![&obj, isEqual: &*obj] };
|
||||
```
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: Change syntax in `extern_class!` macro to be more Rust-like.
|
||||
* **BREAKING**: Change syntax in `declare_class!` macro to be more Rust-like.
|
||||
* **BREAKING**: Renamed `Id::from_owned` to `Id::into_shared`.
|
||||
* **BREAKING**: The return type of `msg_send_id!` is now more generic; it can
|
||||
now either be `Option<Id<_, _>>` or `Id<_, _>` (if the latter, it'll simply
|
||||
panic).
|
||||
|
||||
Example:
|
||||
```rust
|
||||
// Before
|
||||
let obj: Id<Object, Shared> = unsafe {
|
||||
msg_send_id![msg_send_id![class!(MyObject), alloc], init].unwrap()
|
||||
};
|
||||
|
||||
// After
|
||||
let obj: Id<Object, Shared> = unsafe {
|
||||
msg_send_id![msg_send_id![class!(MyObject), alloc], init]
|
||||
};
|
||||
```
|
||||
* Updated `ffi` module to `objc-sys v0.2.0-beta.2`.
|
||||
* **BREAKING**: Updated `encode` module `objc2-encode v2.0.0-pre.2`.
|
||||
|
||||
In particular, `Encoding` no longer has a lifetime parameter:
|
||||
```rust
|
||||
// Before
|
||||
#[repr(C)]
|
||||
pub struct NSRange {
|
||||
pub location: usize,
|
||||
pub length: usize,
|
||||
}
|
||||
unsafe impl Encode for NSRange {
|
||||
const ENCODING: Encoding<'static> = Encoding::Struct(
|
||||
"_NSRange", // This is how the struct is defined in C header files
|
||||
&[usize::ENCODING, usize::ENCODING]
|
||||
);
|
||||
}
|
||||
unsafe impl RefEncode for NSRange {
|
||||
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
|
||||
}
|
||||
|
||||
// After
|
||||
#[repr(C)]
|
||||
pub struct NSRange {
|
||||
pub location: usize,
|
||||
pub length: usize,
|
||||
}
|
||||
unsafe impl Encode for NSRange {
|
||||
const ENCODING: Encoding = Encoding::Struct(
|
||||
"_NSRange", // This is how the struct is defined in C header files
|
||||
&[usize::ENCODING, usize::ENCODING]
|
||||
);
|
||||
}
|
||||
unsafe impl RefEncode for NSRange {
|
||||
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
|
||||
}
|
||||
```
|
||||
|
||||
### Deprecated
|
||||
* Depreacted `msg_send_bool!` in favour of new functionality on `msg_send!`
|
||||
that allows seamlessly handling `bool`.
|
||||
|
||||
|
||||
## 0.3.0-beta.1 - 2022-07-19
|
||||
|
||||
### Added
|
||||
* Added `msg_send_id!` to help with following Objective-C's memory management
|
||||
rules. **It is highly recommended that you use this instead of doing memory
|
||||
management yourself!**
|
||||
|
||||
Example:
|
||||
```rust
|
||||
// Before
|
||||
let obj: Id<Object, Shared> = unsafe {
|
||||
let obj: *mut Object = msg_send![class!(MyObject), alloc];
|
||||
let obj: *mut Object = msg_send![obj, init];
|
||||
Id::new(obj).unwrap()
|
||||
};
|
||||
|
||||
// After
|
||||
let obj: Id<Object, Shared> = unsafe {
|
||||
msg_send_id![msg_send_id![class!(MyObject), alloc], init].unwrap()
|
||||
};
|
||||
```
|
||||
* Added the `"unstable-static-sel"` and `"unstable-static-sel-inlined"`
|
||||
feature flags to make the `sel!` macro (and by extension, the `msg_send!`
|
||||
macros) faster.
|
||||
* Added `"unstable-c-unwind"` feature.
|
||||
* Added unsafe function `Id::cast` for converting between different types of
|
||||
objects.
|
||||
* Added `Object::ivar_ptr` to allow direct access to instance variables
|
||||
through `&Object`.
|
||||
* Added `VerificationError` as more specific return type from
|
||||
`Class::verify_sel`.
|
||||
* Added `rc::Allocated` struct which is used within `msg_send_id!`.
|
||||
* Added `Class::responds_to`.
|
||||
* Added `exception::Exception` object to improve error messages from caught
|
||||
exceptions.
|
||||
* Added `declare::Ivar<T>` helper struct. This is useful for building safe
|
||||
abstractions that access instance variables.
|
||||
* Added `Id::from_owned` helper function.
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: `Sel` is now required to be non-null, which means that you
|
||||
have to ensure that any selectors you receive from method calls are
|
||||
non-null before using them.
|
||||
* **BREAKING**: `ClassBuilder::root` is now generic over the function pointer,
|
||||
meaning you will have to coerce initializer functions to pointers like in
|
||||
`ClassBuilder::add_method` before you can use it.
|
||||
* **BREAKING**: Moved `MessageReceiver::verify_message` to `Class::verify_sel`
|
||||
and changed return type.
|
||||
* Improved debug output with `verify_message` feature enabled.
|
||||
* **BREAKING**: Changed `MessageReceiver::send_message` to panic instead of
|
||||
returning an error.
|
||||
* **BREAKING**: Renamed `catch_all` feature to `catch-all`.
|
||||
* **BREAKING**: Made passing the function pointer argument to
|
||||
`ClassBuilder::add_method`, `ClassBuilder::add_class_method` and similar
|
||||
more ergonomic.
|
||||
|
||||
Let's say you have the following code:
|
||||
```rust
|
||||
// Before
|
||||
let init: extern "C" fn(&mut Object, Sel) -> *mut Object = init;
|
||||
builder.add_method(sel!(init), init);
|
||||
```
|
||||
|
||||
Unfortunately, you will now encounter a very confusing error:
|
||||
```
|
||||
|
|
||||
2 | builder.add_method(sel!(init), init);
|
||||
| ^^^^^^^^^^ implementation of `MethodImplementation` is not general enough
|
||||
|
|
||||
= note: `MethodImplementation` would have to be implemented for the type `for<'r> extern "C" fn(&'r mut Object, Sel) -> *mut Object`
|
||||
= note: ...but `MethodImplementation` is actually implemented for the type `extern "C" fn(&'0 mut Object, Sel) -> *mut Object`, for some specific lifetime `'0`
|
||||
```
|
||||
|
||||
Fret not, the fix is easy! Just let the compiler infer the argument and
|
||||
return types:
|
||||
```rust
|
||||
// After
|
||||
let init: extern "C" fn(_, _) -> _ = init;
|
||||
builder.add_method(sel!(init), init);
|
||||
```
|
||||
* Updated `ffi` module to `objc-sys v0.2.0-beta.1`.
|
||||
* **BREAKING**: Updated `encode` module `objc2-encode v2.0.0-pre.1`.
|
||||
|
||||
### Fixed
|
||||
* **BREAKING**: Disallow throwing `nil` exceptions in `exception::throw`.
|
||||
|
||||
### Removed
|
||||
* **BREAKING**: Removed the `Sel::from_ptr` method.
|
||||
* **BREAKING**: Removed `MessageError`.
|
||||
|
||||
|
||||
## 0.3.0-beta.0 - 2022-06-13
|
||||
|
||||
### Added
|
||||
* Added deprecated `Object::get_ivar` and `Object::get_mut_ivar` to make
|
||||
upgrading easier.
|
||||
* Allow using `From`/`TryFrom` to convert between `rc::Id` and `rc::WeakId`.
|
||||
* Added `Bool::as_bool` (more descriptive name than `Bool::is_true`).
|
||||
* Added convenience method `Id::as_ptr` and `Id::as_mut_ptr`.
|
||||
* The `objc2-encode` dependency is now exposed as `objc2::encode`.
|
||||
* Added `Id::retain_autoreleased` to allow following Cocoas memory management
|
||||
rules more efficiently.
|
||||
* Consistently allow trailing commas in `msg_send!`.
|
||||
* Added `msg_send_bool!`, a less error-prone version of `msg_send!` for
|
||||
Objective-C methods that return `BOOL`.
|
||||
* Implemented `MethodImplementation` for `unsafe` function pointers.
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: Changed signature of `Id::new` and `Id::retain` from
|
||||
`fn(NonNull<T>) -> Id<T>` to `fn(*mut T) -> Option<Id<T>>`.
|
||||
|
||||
Concretely, you will have to change your code as follows.
|
||||
```rust
|
||||
// Before
|
||||
let obj: *mut Object = unsafe { msg_send![class!(NSObject), new] };
|
||||
let obj = NonNull::new(obj).expect("Failed to allocate object.");
|
||||
let obj = unsafe { Id::new(obj) };
|
||||
// After
|
||||
let obj: *mut Object = unsafe { msg_send![class!(NSObject), new] };
|
||||
let obj = unsafe { Id::new(obj) }.expect("Failed to allocate object.");
|
||||
```
|
||||
* Allow specifying any receiver `T: Message` for methods added with
|
||||
`ClassBuilder::add_method`.
|
||||
* Renamed `ClassDecl` and `ProtocolDecl` to `ClassBuilder` and
|
||||
`ProtocolBuilder`. The old names are kept as deprecated aliases.
|
||||
* **BREAKING**: Changed how `msg_send!` works wrt. capturing its arguments.
|
||||
|
||||
This will require changes to your code wherever you used `Id`, for example:
|
||||
```rust
|
||||
// Before
|
||||
let obj: Id<Object, Owned> = ...;
|
||||
let p: i32 = unsafe { msg_send![obj, parameter] };
|
||||
let _: () = unsafe { msg_send![obj, setParameter: p + 1] };
|
||||
// After
|
||||
let mut obj: Id<Object, Owned> = ...;
|
||||
let p: i32 = unsafe { msg_send![&obj, parameter] };
|
||||
let _: () = unsafe { msg_send![&mut obj, setParameter: p + 1] };
|
||||
```
|
||||
|
||||
Notice that we now clearly pass `obj` by reference, and therein also
|
||||
communicate the mutability of the object (in the first case, immutable, and
|
||||
in the second, mutable).
|
||||
|
||||
If you previously used `*mut Object` or `&Object` as the receiver, message
|
||||
sending should work exactly as before.
|
||||
* **BREAKING**: `Class` no longer implements `Message` (but it can still be
|
||||
used as the receiver in `msg_send!`, so this is unlikely to break anything
|
||||
in practice).
|
||||
* **BREAKING**: Sealed the `MethodImplementation` trait, and made its `imp`
|
||||
method privat.
|
||||
* **BREAKING**: Updated `ffi` module to `objc-sys v0.2.0-beta.0`.
|
||||
* **BREAKING**: Updated `objc2-encode` (`Encoding`, `Encode`, `RefEncode` and
|
||||
`EncodeArguments`) to `v2.0.0-pre.0`.
|
||||
|
||||
### Fixed
|
||||
* Properly sealed the `MessageArguments` trait (it already had a hidden
|
||||
method, so this is not really a breaking change).
|
||||
|
||||
### Removed
|
||||
* **BREAKING**: `ManuallyDrop` no longer implements `Message` directly.
|
||||
* **BREAKING**: `MessageReceiver::as_raw_receiver` is no longer public.
|
||||
|
||||
|
||||
## 0.3.0-alpha.6 - 2022-01-03
|
||||
|
||||
### Added
|
||||
* Implement `Hash` for `Sel`, `Ivar`, `Class`, `Method` and `MessageError`.
|
||||
* Implement `PartialEq` and `Eq` for `Ivar`, `Method` and `MessageError`.
|
||||
* Implement `fmt::Pointer` for `Sel` and `rc::AutoreleasePool`.
|
||||
* Implement `fmt::Debug` for `ClassDecl`, `ProtocolDecl` and `rc::AutoreleasePool`.
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: Renamed:
|
||||
- `Object::get_ivar` -> `Object::ivar`
|
||||
- `Object::get_mut_ivar` -> `Object::ivar_mut`
|
||||
* Vastly improved documentation.
|
||||
* **BREAKING**: Updated `ffi` module to `objc-sys v0.2.0-alpha.1`.
|
||||
* **BREAKING**: Updated `objc2-encode` (`Encoding`, `Encode`, `RefEncode` and
|
||||
`EncodeArguments`) to `v2.0.0-beta.2`.
|
||||
|
||||
|
||||
## 0.3.0-alpha.5 - 2021-12-22
|
||||
|
||||
### Added
|
||||
* Export `objc-sys` as `ffi` module.
|
||||
* Added common trait impls on `rc::Owned` and `rc::Shared` (useful in generic
|
||||
contexts).
|
||||
* Implement `RefEncode` for `runtime::Protocol`.
|
||||
* Added `Message` and `MessageReceiver` implementation for `ManuallyDrop<T>`
|
||||
(where `T` is appropriately bound). This allows patterns like:
|
||||
```rust
|
||||
let obj = Id::new(msg_send![class!(MyObject), alloc]);
|
||||
let obj = ManuallyDrop::new(obj);
|
||||
// `init` takes ownership and possibly returns a new object.
|
||||
let obj = Id::new(msg_send![obj, init]);
|
||||
```
|
||||
* New cargo feature `"malloc"`, which allows cutting down on dependencies,
|
||||
most crates don't need the introspection features that this provides.
|
||||
|
||||
### Changed
|
||||
* Deprecated `runtime::BOOL`, `runtime::YES` and `runtime::NO`. Use the
|
||||
newtype `Bool` instead, or low-level `ffi::BOOL`, `ffi::YES` and `ffi::NO`.
|
||||
* **BREAKING**: The following methods now require the new `"malloc"` feature
|
||||
flag to be enabled:
|
||||
- `MessageReceiver::verify_message` (temporarily)
|
||||
- `Method::return_type`
|
||||
- `Method::argument_type`
|
||||
- `Class::classes`
|
||||
- `Class::instance_methods`
|
||||
- `Class::adopted_protocols`
|
||||
- `Class::instance_variables`
|
||||
- `Protocol::protocols`
|
||||
- `Protocol::adopted_protocols`
|
||||
* Relaxed `Sized` bound on `rc::Id` and `rc::WeakId` to prepare for
|
||||
`extern type` support.
|
||||
* **BREAKING**: Relaxed `Sized` bound on `rc::SliceId` and `rc::DefaultId`.
|
||||
* **BREAKING**: Updated `objc-sys` to `v0.2.0-alpha.0`.
|
||||
* Updated `objc2-encode` (`Encoding`, `Encode`, `RefEncode` and
|
||||
`EncodeArguments`) to `v2.0.0-beta.1`.
|
||||
|
||||
### Removed
|
||||
* **BREAKING**: Removed the raw FFI functions from the `runtime` module. These
|
||||
are available in the new `ffi` module instead.
|
||||
|
||||
### Fixed
|
||||
* An issue with inlining various `rc` methods.
|
||||
* Most types (e.g. `Class` and `Method`) are now `Send`, `Sync`, `UnwindSafe`
|
||||
and `RefUnwindSafe` again.
|
||||
Notable exception is `Object`, because that depends on the specific
|
||||
subclass.
|
||||
|
||||
|
||||
## 0.3.0-alpha.4 - 2021-11-22
|
||||
|
||||
Note: To use this version, specify `objc2-encode = "=2.0.0-beta.0"` in your
|
||||
`Cargo.toml` as well.
|
||||
|
||||
### Added
|
||||
* **BREAKING**: GNUStep users must depend on, and specify the appropriate
|
||||
feature flag on `objc-sys` for the version they're using.
|
||||
* Moved `objc_exception` crate into `exception` module (under feature flag).
|
||||
* Added support for `_Complex` types.
|
||||
* Added `rc::SliceId`, `rc::SliceIdMut` and `rc::DefaultId` helper traits for
|
||||
extra functionality on `rc::Id`.
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: The `exception` feature now just enables the `exception`
|
||||
module for general use. Use the new `catch_all` feature to wrap all message
|
||||
sends in a `@try/@catch`.
|
||||
* **BREAKING**: Updated `objc-sys` to `v0.1.0`.
|
||||
* **BREAKING**: Updated `objc2-encode` (`Encoding`, `Encode`, `RefEncode` and
|
||||
`EncodeArguments`) to `v2.0.0-beta.0`.
|
||||
|
||||
|
||||
## 0.3.0-alpha.3 - 2021-09-05
|
||||
|
||||
Note: To use this version, specify `objc2-encode = "=2.0.0-alpha.1"` in your
|
||||
`Cargo.toml` as well.
|
||||
|
||||
### Added
|
||||
* Now uses the `objc-sys` (`v0.0.1`) crate for possibly better
|
||||
interoperability with other crates that link to `libobjc`.
|
||||
* Added newtype `runtime::Bool` to fix soundness issues with using
|
||||
`runtime::BOOL` or `bool`.
|
||||
* Moved `objc_id` crate into `rc` module. Notable changes:
|
||||
- Vastly improved documentation
|
||||
- Added `Id::autorelease`
|
||||
- Added `Id::from_shared`
|
||||
- Added a lot of forwarding implementations on `Id` for easier use
|
||||
- `Id` and `WeakId` are now able to use the null-pointer optimization
|
||||
- **BREAKING**: Added `T: Message` bounds on `Id`
|
||||
- **BREAKING**: Remove `ShareId` type alias
|
||||
- **BREAKING**: `Id` no longer have a default `Ownership`, you must specify
|
||||
it everywhere as either `Id<T, Shared>` or `Id<T, Owned>`
|
||||
- **BREAKING**: Sealed the `Ownership` trait
|
||||
- **BREAKING**: Renamed `Id::from_ptr` to `Id::retain`
|
||||
- **BREAKING**: Renamed `Id::from_retained_ptr` to `Id::new`
|
||||
- **BREAKING**: Changed `Id::share` to a `From` implementation (usage of
|
||||
`obj.share()` can be changed to `obj.into()`)
|
||||
- **BREAKING**: Fixed soundness issues with missing `Send` and `Sync` bounds
|
||||
on `Id` and `WeakId`
|
||||
* Added sealed (for now) trait `MessageReceiver` to specify types that can
|
||||
be used as the receiver of a message (instead of only allowing pointer
|
||||
types).
|
||||
* Add `MessageReceiver::send_super_message` method for dynamic selectors.
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: Change types of parameters to FFI functions exported in the
|
||||
`runtime` module.
|
||||
* **BREAKING**: Most types are now `!UnwindSafe`, to discourage trying to use
|
||||
them after an unwind. This restriction may be lifted in the future.
|
||||
* **BREAKING**: Most types are now `!Send` and `!Sync`. This was an oversight
|
||||
that is fixed in a later version.
|
||||
* A lot of smaller things.
|
||||
* **BREAKING**: Dynamic message sending with `Message::send_message` is moved
|
||||
to `MessageReceiver`.
|
||||
* **BREAKING** Make `MessageArguments` a subtrait of `EncodeArguments`.
|
||||
* Allow an optional comma after each argument to `msg_send!`.
|
||||
|
||||
### Removed
|
||||
* **BREAKING**: Removed `rc::StrongPtr`. Use `Option<rc::Id<Object, Shared>>`
|
||||
instead (beware: This has stronger safety invariants!).
|
||||
* **BREAKING**: Removed `rc::WeakPtr`. Use `rc::WeakId<Object>` instead.
|
||||
|
||||
### Fixed
|
||||
* **BREAKING**: Stop unsafely dereferencing `msg_send!`s first argument. The
|
||||
`MessageReceiver` trait was introduced to avoid most breakage from this
|
||||
change.
|
||||
|
||||
|
||||
## 0.3.0-alpha.2 - 2021-09-05
|
||||
|
||||
### Added
|
||||
* Added `rc::AutoreleasePool` and `rc::AutoreleaseSafe` to make accessing
|
||||
autoreleased objects safe, by binding references to it using the
|
||||
`ptr_as_ref` and `ptr_as_mut` methods.
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: The closure in `rc::autoreleasepool` now takes an argument
|
||||
`&rc::AutoreleasePool`. This reference can be given to functions like
|
||||
`INSString::as_str` so that it knows which lifetime to bound the returned
|
||||
`&str` with.
|
||||
|
||||
Simple migration:
|
||||
```rust
|
||||
// Change
|
||||
autoreleasepool(|| {
|
||||
// Some code that autoreleases objects
|
||||
});
|
||||
// To
|
||||
autoreleasepool(|_pool| {
|
||||
// Some code that autoreleases objects
|
||||
});
|
||||
```
|
||||
|
||||
### Fixed
|
||||
* The encoding of `BOOL` on `GNUStep`.
|
||||
|
||||
|
||||
## 0.3.0-alpha.1 - 2021-09-02
|
||||
|
||||
### Added
|
||||
* More documentation of safety requirements, and in general.
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: Change `objc-encode` dependency to `objc2-encode` version
|
||||
`2.0.0-alpha.1`, and re-export the new `RefEncode` trait from that.
|
||||
* **BREAKING**: Require that the receiver, arguments and return types of
|
||||
messages always implement `Encode`. This helps ensuring that only types made
|
||||
to go across the FFI boundary (`repr(C)`, ...) may. These requirements were
|
||||
already present when the `verify_message` feature was enabled.
|
||||
|
||||
This is a very _disruptive change_, since libraries are now required to
|
||||
implement `Encode` and `RefEncode` for all types intended to go across the
|
||||
FFI-boundary to Objective-C. The change is justified because it helps
|
||||
ensuring that users only pass valid types to `msg_send!` (for example, this
|
||||
prevents users from accidentally passing `Drop` types to `msg_send`).
|
||||
|
||||
See the following examples for how to implement these traits, and otherwise
|
||||
refer to the documentation of `objc2-encode` (`v2.0.0-alpha.1` or above).
|
||||
```rust
|
||||
use objc2::{Encode, Encoding, RefEncode};
|
||||
|
||||
/// Example struct.
|
||||
#[repr(C)]
|
||||
pub struct NSRange {
|
||||
pub location: usize,
|
||||
pub length: usize,
|
||||
}
|
||||
unsafe impl Encode for NSRange {
|
||||
const ENCODING: Encoding<'static> = Encoding::Struct(
|
||||
"_NSRange", // This is how the struct is defined in C header files
|
||||
&[usize::ENCODING, usize::ENCODING]
|
||||
);
|
||||
}
|
||||
unsafe impl RefEncode for NSRange {
|
||||
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
|
||||
}
|
||||
|
||||
/// Example object.
|
||||
#[repr(C)]
|
||||
pub struct __CFString(c_void);
|
||||
|
||||
pub type CFStringRef = *const __CFString;
|
||||
|
||||
unsafe impl RefEncode for __CFString {
|
||||
const ENCODING_REF: Encoding<'static> = Encoding::Object;
|
||||
}
|
||||
```
|
||||
* Temporarily disabled iOS tests.
|
||||
|
||||
### Fixed
|
||||
* Statically find the correct `objc_msgSend[_X]` function to use based on the
|
||||
`Encode` implementation of the return type. This fixes using functions that
|
||||
return e.g. `type CGFloat = f32 / f64;`.
|
||||
* Documentation links.
|
||||
|
||||
|
||||
## 0.3.0-alpha.0 - 2021-08-29
|
||||
|
||||
Note: This is the version that is, as of this writing, available on the
|
||||
`master` branch in the original `objc` project.
|
||||
|
||||
### Added
|
||||
* Improve macro hygiene.
|
||||
```rust
|
||||
// You can now do
|
||||
use objc2::{sel, class, msg_send};
|
||||
// Instead of
|
||||
#[macro_use]
|
||||
extern crate objc2;
|
||||
```
|
||||
* Update to Rust 2018.
|
||||
* Other internal improvements.
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: Forked the project, so the crate name is now `objc2`.
|
||||
* **BREAKING**: Updated encoding utilities to use `objc-encode`. See that for
|
||||
how to use the updated type `Encoding` and trait `Encode`.
|
||||
|
||||
In short, you will likely need to change your implementations of `Encode`
|
||||
like this:
|
||||
```rust
|
||||
use objc2::{Encode, Encoding};
|
||||
|
||||
pub type CGFloat = ...; // Varies based on target_pointer_width
|
||||
|
||||
#[repr(C)]
|
||||
pub struct NSPoint {
|
||||
pub x: CGFloat,
|
||||
pub y: CGFloat,
|
||||
}
|
||||
|
||||
// Before
|
||||
unsafe impl Encode for NSPoint {
|
||||
fn encode() -> Encoding {
|
||||
let encoding = format!(
|
||||
"{{CGPoint={}{}}}",
|
||||
CGFloat::encode().as_str(),
|
||||
CGFloat::encode().as_str(),
|
||||
);
|
||||
unsafe { Encoding::from_str(&encoding) }
|
||||
}
|
||||
}
|
||||
|
||||
// After
|
||||
unsafe impl Encode for NSPoint {
|
||||
const ENCODING: Encoding<'static> = Encoding::Struct(
|
||||
"CGPoint",
|
||||
&[CGFloat::ENCODING, CGFloat::ENCODING]
|
||||
);
|
||||
}
|
||||
```
|
||||
* **BREAKING**: Updated public dependency `malloc_buf` to `1.0`.
|
||||
* **BREAKING**: `Method::return_type` and `Method::argument_type` now return
|
||||
`Malloc<str>` instead of `Encoding`.
|
||||
* **BREAKING**: `Ivar::type_encoding` now return `&str` instead of `Encoding`.
|
||||
|
||||
### Removed
|
||||
* **BREAKING**: Removed hidden `sel_impl!` macro.
|
||||
|
||||
|
||||
## [0.2.7] (`objc` crate) - 2019-10-19
|
||||
|
||||
### Fixed
|
||||
* **BREAKING**: Uses of `msg_send!` will now correctly fail to compile if no
|
||||
return type can be inferred, instead of relying on an edge case of the
|
||||
compiler that will soon change and silently cause undefined behavior.
|
||||
|
||||
|
||||
## [0.2.6] (`objc` crate) - 2019-03-25
|
||||
|
||||
### Fixed
|
||||
* Suppressed a deprecation warning in `sel!`, `msg_send!`, and `class!`.
|
||||
|
||||
|
||||
## [0.2.5] (`objc` crate) - 2018-07-24
|
||||
|
||||
### Added
|
||||
* **BREAKING**: `autoreleasepool` returns the value returned by its body
|
||||
closure.
|
||||
|
||||
|
||||
## [0.2.4] (`objc` crate) - 2018-07-22
|
||||
|
||||
### Added
|
||||
* Added an `rc` module with reference counting utilities:
|
||||
`StrongPtr`, `WeakPtr`, and `autoreleasepool`.
|
||||
* Added some reference counting ABI foreign functions to the `runtime` module.
|
||||
|
||||
### Fixed
|
||||
* Messaging nil under GNUstep now correctly returns zeroed results for all
|
||||
return types.
|
||||
|
||||
|
||||
## [0.2.3] (`objc` crate) - 2018-07-07
|
||||
|
||||
### Added
|
||||
* Added a `class!` macro for getting statically-known classes. The result is
|
||||
non-optional (avoiding a need to unwrap) and cached so each usage will only
|
||||
look up the class once.
|
||||
* Added caching to the `sel!` macro so that each usage will only register the
|
||||
selector once.
|
||||
|
||||
### Fixed
|
||||
* Implementation of `objc2::runtime` structs so there can't be unsound
|
||||
references to uninhabited types.
|
||||
|
||||
|
||||
## [0.2.2] (`objc` crate) - 2016-10-30
|
||||
|
||||
### Added
|
||||
* Implemented `Sync` and `Send` for `Sel`.
|
||||
|
||||
|
||||
## [0.2.1] (`objc` crate) - 2016-04-23
|
||||
|
||||
### Added
|
||||
* Added support for working with protocols with the `Protocol` struct.
|
||||
The protocols a class conforms to can be examined with the new
|
||||
`Class::adopted_protocols` and `Class::conforms_to` methods.
|
||||
* Protocols can be declared using the new `ProtocolDecl` struct.
|
||||
|
||||
|
||||
## [0.2.0] (`objc` crate) - 2016-03-20
|
||||
|
||||
### Added
|
||||
* Added verification for the types used when sending messages.
|
||||
This can be enabled for all messages with the `"verify_message"` feature,
|
||||
or you can test before sending specific messages with the
|
||||
`Message::verify_message` method. Verification errors are reported using the
|
||||
new `MessageError` struct.
|
||||
* Added support for the GNUstep runtime!
|
||||
Operating systems besides OSX and iOS will fall back to the GNUstep runtime.
|
||||
* Root classes can be declared by using the `ClassDecl::root` constructor.
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: C types are now used from `std::os::raw` rather than `libc`.
|
||||
This means `Encode` may not be implemented for `libc` types; switch them to
|
||||
the `std::os::raw` equivalents instead. This avoids an issue that would
|
||||
arise from simultaneously using different versions of the libc crate.
|
||||
* **BREAKING**: Dynamic messaging was moved into the `Message` trait; instead
|
||||
of `().send(obj, sel!(description))`, use
|
||||
`obj.send_message(sel!(description), ())`.
|
||||
* **BREAKING**: Rearranged the parameters to `ClassDecl::new` for consistency;
|
||||
instead of `ClassDecl::new(superclass, "MyObject")`, use
|
||||
`ClassDecl::new("MyObject", superclass)`.
|
||||
* **BREAKING**: Overhauled the `MethodImplementation` trait. Encodings are now
|
||||
accessed through the `MethodImplementation::Args` associated type. The
|
||||
`imp_for` method was replaced with `imp` and no longer takes a selector or
|
||||
returns an `UnequalArgsError`, although `ClassDecl::add_method` still
|
||||
validates the number of arguments.
|
||||
* **BREAKING**: Updated the definition of `Imp` to not use the old dispatch
|
||||
prototypes. To invoke an `Imp`, it must first be transmuted to the correct
|
||||
type.
|
||||
|
||||
### Removed
|
||||
**BREAKING**: `objc_msgSend` functions from the `runtime` module; the
|
||||
availability of these functions varies and they shouldn't be called without
|
||||
trasmuting, so they are now hidden as an implementation detail of messaging.
|
||||
|
||||
### Fixed
|
||||
* Corrected alignment of ivars in `ClassDecl`; declared classes may now have a
|
||||
smaller size.
|
||||
* With the `"exception"` or `"verify_message"` feature enabled, panics from
|
||||
`msg_send!` will now be triggered from the line and file where the macro is
|
||||
used, rather than from within the implementation of messaging.
|
||||
|
||||
## [0.1.8] (`objc` crate) - 2015-11-06
|
||||
|
||||
### Changed
|
||||
* Updated `libc` dependency.
|
||||
|
||||
|
||||
## [0.1.7] (`objc` crate) - 2015-09-23
|
||||
|
||||
### Fixed
|
||||
* `improper_ctypes` warning.
|
||||
|
||||
|
||||
## [0.1.6] (`objc` crate) - 2015-08-08
|
||||
|
||||
### Added
|
||||
* Added `"exception"` feature which catches Objective-C exceptions and turns
|
||||
them into Rust panics.
|
||||
* Added support for `ARM`, `ARM64` and `x86` architectures.
|
||||
* **BREAKING**: Added `Any` bound on message return types. In practice this
|
||||
probably won't break anything.
|
||||
* Start testing on iOS.
|
||||
|
||||
|
||||
## [0.1.5] (`objc` crate) - 2015-05-02
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: Renamed `IntoMethodImp` to `MethodImplementation`.
|
||||
* **BREAKING**: Renamed `MethodImplementation::into_imp` to `::imp_for`.
|
||||
* **BREAKING**: Relaxed `Sized` bounds on `Encode` and `Message`. In practice
|
||||
this probably won't break anything.
|
||||
|
||||
### Removed
|
||||
* **BREAKING**: Removed `Id`, `Owned`, `Ownership`, `Shared`, `ShareId` and
|
||||
`WeakId`. Use them from the `objc_id` crate instead.
|
||||
* **BREAKING**: Removed `Method::set_implementation` and
|
||||
`Method::exchange_implementation`.
|
||||
|
||||
|
||||
## [0.1.4] (`objc` crate) - 2015-04-17
|
||||
|
||||
### Removed
|
||||
* **BREAKING**: Removed `block` module. Use them from the `block` crate
|
||||
instead.
|
||||
|
||||
|
||||
## [0.1.3] (`objc` crate) - 2015-04-11
|
||||
|
||||
### Added
|
||||
* Implement `fmt::Pointer` for `Id`.
|
||||
|
||||
### Fixed
|
||||
* Odd lifetime bug.
|
||||
|
||||
|
||||
## [0.1.2] (`objc` crate) - 2015-04-04
|
||||
|
||||
### Fixed
|
||||
* **BREAKING**: Replace uses of `PhantomFn` with `Sized`.
|
||||
|
||||
|
||||
## [0.1.1] (`objc` crate) - 2015-03-27
|
||||
|
||||
### Added
|
||||
* Implement `Error` for `UnequalArgsError`.
|
||||
|
||||
### Removed
|
||||
* **BREAKING**: Move `objc::foundation` into new crate `objc_foundation`.
|
||||
|
||||
|
||||
[0.2.7]: https://github.com/madsmtm/objc2/compare/objc-0.2.6...objc-0.2.7
|
||||
[0.2.6]: https://github.com/madsmtm/objc2/compare/objc-0.2.5...objc-0.2.6
|
||||
[0.2.5]: https://github.com/madsmtm/objc2/compare/objc-0.2.4...objc-0.2.5
|
||||
[0.2.4]: https://github.com/madsmtm/objc2/compare/objc-0.2.3...objc-0.2.4
|
||||
[0.2.3]: https://github.com/madsmtm/objc2/compare/objc-0.2.2...objc-0.2.3
|
||||
[0.2.2]: https://github.com/madsmtm/objc2/compare/objc-0.2.1...objc-0.2.2
|
||||
[0.2.1]: https://github.com/madsmtm/objc2/compare/objc-0.2.0...objc-0.2.1
|
||||
[0.2.0]: https://github.com/madsmtm/objc2/compare/objc-0.1.8...objc-0.2.0
|
||||
[0.1.8]: https://github.com/madsmtm/objc2/compare/objc-0.1.7...objc-0.1.8
|
||||
[0.1.7]: https://github.com/madsmtm/objc2/compare/objc-0.1.6...objc-0.1.7
|
||||
[0.1.6]: https://github.com/madsmtm/objc2/compare/objc-0.1.5...objc-0.1.6
|
||||
[0.1.5]: https://github.com/madsmtm/objc2/compare/objc-0.1.4...objc-0.1.5
|
||||
[0.1.4]: https://github.com/madsmtm/objc2/compare/objc-0.1.3...objc-0.1.4
|
||||
[0.1.3]: https://github.com/madsmtm/objc2/compare/objc-0.1.2...objc-0.1.3
|
||||
[0.1.2]: https://github.com/madsmtm/objc2/compare/objc-0.1.1...objc-0.1.2
|
||||
[0.1.1]: https://github.com/madsmtm/objc2/compare/objc-0.1.0...objc-0.1.1
|
||||
318
third-party/vendor/objc2/CHANGELOG_FOUNDATION.md
vendored
Normal file
318
third-party/vendor/objc2/CHANGELOG_FOUNDATION.md
vendored
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
# Changelog
|
||||
|
||||
Changes to the `objc2::foundation` module will be documented in this file.
|
||||
This previously existed as a separate crate `objc2_foundation`, hence the
|
||||
separation.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## Unreleased - YYYY-MM-DD
|
||||
|
||||
## objc2 0.3.0-beta.3 - 2022-09-01
|
||||
|
||||
### Added
|
||||
* Added `NSSet`.
|
||||
* Added `NSMutableSet`.
|
||||
* Added `NSMutableDictionary`.
|
||||
* Added `NSNotFound`.
|
||||
* Added `NSBundle`.
|
||||
* Added `NSTimeInterval`.
|
||||
* Added `NSString::len_utf16` and `NSAttributedString::len_utf16`.
|
||||
* Added `NSString::concat` and `NSString::join_path`.
|
||||
* Added `CGSize`, `CGPoint` and `CGRect` (just aliases to equivalent
|
||||
`NS`-types, but helps readability).
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: `NSSize::new` no longer requires it's arguments to be
|
||||
non-negative. Use `NSSize::abs` or `NSRect::standardize` if the API you're
|
||||
binding to requires a non-negative size.
|
||||
|
||||
|
||||
## objc2 0.3.0-beta.2 - 2022-08-28
|
||||
|
||||
### Added
|
||||
* Added `NSNumber`.
|
||||
* Added `NSError`.
|
||||
* Implement `UnwindSafe` and `RefUnwindSafe` for all objects.
|
||||
* Implemented `IntoIterator` for references to `NSArray`, `NSMutableArray`,
|
||||
`NSData` and `NSMutableData`.
|
||||
* Implemented `Extend` for `NSMutableArray`.
|
||||
* Add extra `Extend<&u8>` impl for `NSMutableData`.
|
||||
* Added function `NSValue::contains_encoding` for determining if the encoding
|
||||
of the `NSValue` matches the encoding of the given type.
|
||||
* Added functions `get_range`, `get_point`, `get_size` and `get_rect` to
|
||||
`NSValue` to help safely returning various types it will commonly contain.
|
||||
* `NSArray` and `NSMutableArray` now have sensible defaults for the ownership
|
||||
of the objects they contain.
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: Moved from external crate `objc2_foundation` into
|
||||
`objc2::foundation`.
|
||||
* **BREAKING**: Made `NSValue` not generic any more. While we loose some
|
||||
type-safety from this, it makes `NSValue` much more useful in the real
|
||||
world!
|
||||
* **BREAKING**: Made `NSArray::new` generic over ownership.
|
||||
* **BREAKING**: Made `NSObject::is_kind_of` take a generic `T: ClassType`
|
||||
instead of a `runtime::Class`.
|
||||
|
||||
### Fixed
|
||||
* Made `Debug` impls for all objects print something useful.
|
||||
|
||||
### Removed
|
||||
* `NSObject::hash_code`, `NSObject::is_equal` and `NSObject::description` in
|
||||
favour of just having the trait implementations `Hash`, `PartiqalEq` and
|
||||
`Debug`.
|
||||
|
||||
|
||||
## objc2-foundation 0.2.0-alpha.6 - 2022-07-19
|
||||
|
||||
### Added
|
||||
* Added `MainThreadMarker` to help with designing APIs where a method is only
|
||||
safe to call on the main thread.
|
||||
* Added `NSException` object.
|
||||
* Added `extern_class!` macro to help with defining interfaces to classes.
|
||||
Further changelog for this can be found in `CHANGELOG.md`.
|
||||
* Added `declare_class!` macro to help with declaring custom classes.
|
||||
Further changelog for this can be found in `CHANGELOG.md`.
|
||||
* Expose the `objc2` version that this uses in the crate root.
|
||||
* Added `NSZone`.
|
||||
|
||||
### Changed
|
||||
* Changed a few `Debug` impls.
|
||||
|
||||
|
||||
## objc2-foundation 0.2.0-alpha.5 - 2022-06-13
|
||||
|
||||
### Added
|
||||
* Objects now `Deref` to their superclasses. E.g. `NSMutableArray` derefs to
|
||||
`NSArray`, which derefs to `NSObject`, which derefs to `Object`.
|
||||
|
||||
This allows more ergonomic usage.
|
||||
* Implement `PartialOrd` and `Ord` for `NSString` and `NSRange`.
|
||||
* Added `NSString::has_prefix` and `NSString::has_suffix`.
|
||||
* Added `NSRange` methods `new`, `is_empty`, `contains` and `end`.
|
||||
* Added `NSThread` object.
|
||||
* Added `is_multi_threaded` and `is_main_thread` helper functions.
|
||||
* Added `NSProcessInfo` object.
|
||||
* Added `NSMutableData` methods `from_data`, `with_capacity` and `push`.
|
||||
* Added `io::Write` and `iter::Extend` implementation for `NSMutableData`.
|
||||
* Added `NSUUID` object.
|
||||
* Added `NSMutableString` object.
|
||||
* Added basic `NSAttributedString` object.
|
||||
* Added basic `NSMutableAttributedString` object.
|
||||
* Added `NSInteger` and `NSUInteger` (type aliases to `isize` and `usize`).
|
||||
* Added `CGFloat`.
|
||||
* Added `NSPoint`.
|
||||
* Added `NSSize`.
|
||||
* Added `NSRect`.
|
||||
* Implement `Borrow` and `BorrowMut` for all objects.
|
||||
* Implement `ToOwned` for copyable types.
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: Removed the following helper traits in favor of inherent
|
||||
methods on the objects themselves:
|
||||
- `INSMutableArray`
|
||||
- `INSArray`
|
||||
- `INSMutableData`
|
||||
- `INSData`
|
||||
- `INSDictionary`
|
||||
- `INSString`
|
||||
- `INSValue`
|
||||
- `INSObject`
|
||||
|
||||
This changed because objects now deref to their superclasses.
|
||||
* **BREAKING**: Relaxed a lot of bounds from `INSObject` to `Message`. At some
|
||||
point in the future a new trait will be introduced which remedies this
|
||||
change.
|
||||
* **BREAKING**: Removed the `I` prefix from:
|
||||
- `INSCopying` (now `NSCopying`)
|
||||
- `INSMutableCopying` (now `NSMutableCopying`)
|
||||
- `INSFastEnumeration` (now `NSFastEnumeration`)
|
||||
* **BREAKING**: Renamed `NSMutableData::append` to `extend_from_slice`.
|
||||
|
||||
|
||||
## 0.2.0-alpha.4 - 2022-01-03
|
||||
|
||||
### Added
|
||||
* Implement `PartialOrd` and `Ord` for `NSComparisonResult` and `NSValue`.
|
||||
* Implement `fmt::Display` for `NSValue`.
|
||||
* Implement `DefaultId` for relevant objects.
|
||||
* Implement `AsRef` and `Index` for `NSData` and `NSMutableData`.
|
||||
* Implement `AsMut` and `IndexMut` for `NSMutableData`.
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: Renamed `INSFastEnumeration::enumerator` to
|
||||
`INSFastEnumeration::iter_fast`.
|
||||
|
||||
### Removed
|
||||
* **BREAKING**: Removed `Deref` and `DerefMut` from `NSData` and
|
||||
`NSMutableData`, since these invoke a non-trivial amount of code, and could
|
||||
easily lead to hard-to-diagnose performance issues.
|
||||
|
||||
|
||||
## objc2-foundation 0.2.0-alpha.3 - 2021-12-22
|
||||
|
||||
### Added
|
||||
* **BREAKING**: Added associated `Ownership` type to `NSCopying`.
|
||||
* **BREAKING**: Added associated `Ownership` type to `INSData`.
|
||||
* **BREAKING**: Added associated `Ownership` type to `INSArray`.
|
||||
* Added common trait impls (`PartialEq`, `Eq`, `Hash` and `Debug`) to
|
||||
`NSValue`, `NSDictionary`, `NSArray` and `NSMutableArray`.
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: Made some creation methods a bit less generic (e.g.
|
||||
`INSDictionary::from_keys_and_objects` now always returns `Id<_, Shared>`).
|
||||
* Relax bounds on generic `INSObject` impls.
|
||||
|
||||
### Removed
|
||||
* **BREAKING**: Removed associated `Ownership` type from `INSObject`; instead,
|
||||
it is present on the types that actually need it (for example `NSCopying`).
|
||||
* **BREAKING**: Removed `Sized` bound on `INSObject`.
|
||||
|
||||
### Fixed
|
||||
* Soundness issue with `NSValue`, `NSDictionary`, `NSArray` and
|
||||
`NSMutableArray` not being `#[repr(C)]`.
|
||||
* **BREAKING**: `NSObject` is no longer `Send` and `Sync` (because its
|
||||
subclasses may not be).
|
||||
|
||||
|
||||
## objc2-foundation 0.2.0-alpha.2 - 2021-11-22
|
||||
|
||||
### Added
|
||||
* **BREAKING**: Added associated `Ownership` type to `INSObject` to specify
|
||||
whether the type can be mutated or not. `NSString` is a prime example of a
|
||||
type that you may never get a `Owned/&mut` reference to, since it is very
|
||||
easy to create two `NSString`s with the same underlying allocation.
|
||||
* Added helper `is_empty` methods.
|
||||
* Added `INSArray::first_mut` and `INSArray::last_mut`.
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: Renamed a lot of methods to better match Rusts naming scheme:
|
||||
- `INSArray`
|
||||
- `count` -> `len`
|
||||
- `object_at` -> `get`
|
||||
- `mut_object_at` -> `get_mut`
|
||||
- `shared_object_at` -> `get_retained`
|
||||
- `first_object` -> `first`
|
||||
- `last_object` -> `last`
|
||||
- `object_enumerator` -> `iter`
|
||||
- `INSMutableArray`
|
||||
- `add_object` -> `push`
|
||||
- `insert_object_at` -> `insert`
|
||||
- `replace_object_at` -> `replace`
|
||||
- `remove_object_at` -> `remove`
|
||||
- `remove_last_object` -> `pop`
|
||||
- `remove_all_objects` -> `clear`
|
||||
- `INSDictionary`
|
||||
- `count` -> `len`
|
||||
- `object_for` -> `get`
|
||||
- `key_enumerator` -> `iter_keys`
|
||||
- `object_enumerator` -> `iter_values`
|
||||
- `INSValue`
|
||||
- `value` -> `get`
|
||||
- `from_value` -> `new`
|
||||
- `NSComparisonResult`
|
||||
- `from_ordering` -> `from`
|
||||
- `as_ordering` -> `into`
|
||||
- `NSRange`
|
||||
- `from_range` -> `from`
|
||||
- `as_range` -> `into`
|
||||
* Use `SliceId` for better performance when creating arrays and dictionaries.
|
||||
|
||||
### Removed
|
||||
* **BREAKING**: Removed the `object_struct!` macro. It may be re-added in
|
||||
another form in the future.
|
||||
* **BREAKING**: Removed `NSMutableSharedArray<T>` and `NSSharedArray<T>` type
|
||||
aliases. Use `NSMutableArray<T, Shared>` and `NSArray<T, Shared>` instead.
|
||||
* **BREAKING**: Removed `Any / 'static` bound on `INSObject`. This allows
|
||||
implementing it for objects that contain lifetimes from the outer scope.
|
||||
|
||||
### Fixed
|
||||
* **BREAKING**: Marked `INS...` traits as `unsafe` to implement.
|
||||
* **BREAKING**: Removed `new` method from `INSObject` since some classes don't
|
||||
want this called. It has been re-added to other `INS...` traits on a case by
|
||||
case basis (in particular not `NSValue`).
|
||||
* **BREAKING**: `INSString::as_str` now takes an a reference to
|
||||
`objc2::rc::AutoreleasePool`. This ensure that the returned `&str` is only
|
||||
used while the current autorelease pool is valid.
|
||||
* Fixed `NSData::from_vec` on GNUStep.
|
||||
|
||||
|
||||
## objc2-foundation 0.2.0-alpha.1 - 2021-10-28
|
||||
|
||||
### Added
|
||||
* Implement new `RefEncode` trait for objects.
|
||||
* Implement `Encode` for `NSComparisonResult` and `NSFastEnumerationState`.
|
||||
* Implement `RefEncode` for objects and `NSFastEnumerationState`.
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: Uses `Id` from `objc2::rc` module instead of `objc_id` crate.
|
||||
* **BREAKING**: `INSValue::encoding` now returns `&str` instead of `Encoding`.
|
||||
|
||||
### Fixed
|
||||
* Use proper `#[repr(C)]` structs to represent Objective-C objects.
|
||||
* `INSString::from_str` on GNUStep (`UTF8_ENCODING` was the wrong type).
|
||||
|
||||
|
||||
## objc2-foundation 0.2.0-alpha.0 - 2021-08-29
|
||||
|
||||
Note: This is the version that is, as of this writing, available on the
|
||||
`master` branch in the original `objc-foundation` project.
|
||||
|
||||
### Added
|
||||
* Implement `Display` for `NSString`.
|
||||
* Make `INSObject::class` faster using the `objc::class!` macro.
|
||||
|
||||
### Changed
|
||||
* **BREAKING**: Forked the project, the crate name is now `objc2-foundation`.
|
||||
|
||||
### Fixed
|
||||
* Fixed types in various calls to `objc::msg_send!` for better verification.
|
||||
|
||||
|
||||
## objc-foundation [0.1.1] - 2016-06-19
|
||||
|
||||
### Fixed
|
||||
* An issue with passing functions (instead of function pointers) in
|
||||
`INSMutableArray::sort_by`.
|
||||
|
||||
|
||||
## objc-foundation [0.1.0] - 2016-03-20
|
||||
|
||||
### Changed
|
||||
* Update `objc` to `v0.2`.
|
||||
* Update `objc_id` to `v0.1`.
|
||||
|
||||
|
||||
## objc-foundation [0.0.4] - 2015-12-09
|
||||
|
||||
### Removed
|
||||
* `libc` dependency.
|
||||
|
||||
|
||||
## objc-foundation [0.0.3] - 2015-11-07
|
||||
|
||||
### Added
|
||||
* `object_struct!` macro.
|
||||
|
||||
### Changed
|
||||
* `libc` version can both be `0.1` and `0.2`.
|
||||
|
||||
|
||||
## objc-foundation [0.0.2] - 2015-09-03
|
||||
|
||||
### Added
|
||||
* `Any` bound on `INSObject`, because of a change in `objc` `v0.1.6`.
|
||||
|
||||
|
||||
## objc-foundation [0.0.1] - 2015-06-13
|
||||
|
||||
Initial release.
|
||||
|
||||
|
||||
[0.1.1]: https://github.com/madsmtm/objc2/compare/objc-foundation-0.1.0...objc-foundation-0.1.1
|
||||
[0.1.0]: https://github.com/madsmtm/objc2/compare/objc-foundation-0.0.4...objc-foundation-0.1.0
|
||||
[0.0.4]: https://github.com/madsmtm/objc2/compare/objc-foundation-0.0.3...objc-foundation-0.0.4
|
||||
[0.0.3]: https://github.com/madsmtm/objc2/compare/objc-foundation-0.0.2...objc-foundation-0.0.3
|
||||
[0.0.2]: https://github.com/madsmtm/objc2/compare/objc-foundation-0.0.1...objc-foundation-0.0.2
|
||||
[0.0.1]: https://github.com/madsmtm/objc2/releases/tag/objc-foundation-0.0.1
|
||||
92
third-party/vendor/objc2/Cargo.lock
generated
vendored
Normal file
92
third-party/vendor/objc2/Cargo.lock
generated
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "block-sys"
|
||||
version = "0.1.0-beta.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146"
|
||||
dependencies = [
|
||||
"objc-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block2"
|
||||
version = "0.2.0-alpha.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42"
|
||||
dependencies = [
|
||||
"block-sys",
|
||||
"objc2-encode",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
|
||||
[[package]]
|
||||
name = "iai"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71a816c97c42258aa5834d07590b718b4c9a598944cd39a52dc25b351185d678"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.140"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
|
||||
|
||||
[[package]]
|
||||
name = "malloc_buf"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f32c8c0575eee8637bf462087c00098fe16d6cb621f1abb6ebab4da414d57fd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc-sys"
|
||||
version = "0.2.0-beta.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2"
|
||||
version = "0.3.0-beta.3.patch-leaks.3"
|
||||
dependencies = [
|
||||
"block2",
|
||||
"iai",
|
||||
"malloc_buf",
|
||||
"objc-sys",
|
||||
"objc2-encode",
|
||||
"objc2-proc-macros",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-encode"
|
||||
version = "2.0.0-pre.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512"
|
||||
dependencies = [
|
||||
"objc-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-proc-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b907b825b8c265dd8c4581472273184e75c12825f67d31ba18c5c8cdb5d5431e"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79"
|
||||
153
third-party/vendor/objc2/Cargo.toml
vendored
Normal file
153
third-party/vendor/objc2/Cargo.toml
vendored
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
# 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"
|
||||
name = "objc2"
|
||||
version = "0.3.0-beta.3.patch-leaks.3"
|
||||
authors = [
|
||||
"Steven Sheldon",
|
||||
"Mads Marquart <mads@marquart.dk>",
|
||||
]
|
||||
description = "Objective-C interface and bindings to the Cocoa Foundation framework"
|
||||
documentation = "https://docs.rs/objc2/"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"objective-c",
|
||||
"macos",
|
||||
"ios",
|
||||
"objc_msgSend",
|
||||
"cocoa",
|
||||
]
|
||||
categories = [
|
||||
"api-bindings",
|
||||
"development-tools::ffi",
|
||||
"os::macos-apis",
|
||||
]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/madsmtm/objc2"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
default-target = "x86_64-apple-darwin"
|
||||
features = [
|
||||
"exception",
|
||||
"malloc",
|
||||
"block",
|
||||
"uuid",
|
||||
"unstable-docsrs",
|
||||
]
|
||||
targets = [
|
||||
"x86_64-apple-darwin",
|
||||
"aarch64-apple-darwin",
|
||||
"aarch64-apple-ios",
|
||||
"x86_64-apple-ios",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
"i686-unknown-linux-gnu",
|
||||
"x86_64-pc-windows-msvc",
|
||||
]
|
||||
|
||||
[[bench]]
|
||||
name = "autorelease"
|
||||
harness = false
|
||||
|
||||
[dependencies.block2]
|
||||
version = "=0.2.0-alpha.6"
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[dependencies.malloc_buf]
|
||||
version = "1.0"
|
||||
optional = true
|
||||
|
||||
[dependencies.objc-sys]
|
||||
version = "=0.2.0-beta.2"
|
||||
default-features = false
|
||||
|
||||
[dependencies.objc2-encode]
|
||||
version = "=2.0.0-pre.2"
|
||||
default-features = false
|
||||
|
||||
[dependencies.objc2-proc-macros]
|
||||
version = "0.1.0"
|
||||
optional = true
|
||||
|
||||
[dependencies.uuid]
|
||||
version = "1.1.2"
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies.iai]
|
||||
version = "0.1"
|
||||
|
||||
[features]
|
||||
alloc = [
|
||||
"objc2-encode/alloc",
|
||||
"objc-sys/alloc",
|
||||
"block2?/alloc",
|
||||
]
|
||||
apple = [
|
||||
"objc-sys/apple",
|
||||
"objc2-encode/apple",
|
||||
"block2?/apple",
|
||||
]
|
||||
block = ["block2"]
|
||||
catch-all = ["exception"]
|
||||
default = [
|
||||
"std",
|
||||
"apple",
|
||||
"foundation",
|
||||
]
|
||||
exception = ["objc-sys/unstable-exception"]
|
||||
foundation = []
|
||||
gnustep-1-7 = [
|
||||
"objc-sys/gnustep-1-7",
|
||||
"objc2-encode/gnustep-1-7",
|
||||
"block2?/gnustep-1-7",
|
||||
]
|
||||
gnustep-1-8 = [
|
||||
"gnustep-1-7",
|
||||
"objc-sys/gnustep-1-8",
|
||||
"objc2-encode/gnustep-1-8",
|
||||
"block2?/gnustep-1-8",
|
||||
]
|
||||
gnustep-1-9 = [
|
||||
"gnustep-1-8",
|
||||
"objc-sys/gnustep-1-9",
|
||||
"objc2-encode/gnustep-1-9",
|
||||
"block2?/gnustep-1-9",
|
||||
]
|
||||
gnustep-2-0 = [
|
||||
"gnustep-1-9",
|
||||
"objc-sys/gnustep-2-0",
|
||||
"objc2-encode/gnustep-2-0",
|
||||
"block2?/gnustep-2-0",
|
||||
]
|
||||
gnustep-2-1 = [
|
||||
"gnustep-2-0",
|
||||
"objc-sys/gnustep-2-1",
|
||||
"objc2-encode/gnustep-2-1",
|
||||
"block2?/gnustep-2-1",
|
||||
]
|
||||
malloc = ["malloc_buf"]
|
||||
std = [
|
||||
"alloc",
|
||||
"objc2-encode/std",
|
||||
"objc-sys/std",
|
||||
"block2?/std",
|
||||
]
|
||||
unstable-autoreleasesafe = []
|
||||
unstable-c-unwind = []
|
||||
unstable-docsrs = []
|
||||
unstable-static-class = ["objc2-proc-macros"]
|
||||
unstable-static-class-inlined = ["unstable-static-class"]
|
||||
unstable-static-sel = ["objc2-proc-macros"]
|
||||
unstable-static-sel-inlined = ["unstable-static-sel"]
|
||||
verify_message = ["malloc"]
|
||||
35
third-party/vendor/objc2/README.md
vendored
Normal file
35
third-party/vendor/objc2/README.md
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# `objc2`
|
||||
|
||||
[](https://crates.io/crates/objc2)
|
||||
[](../LICENSE.txt)
|
||||
[](https://docs.rs/objc2/)
|
||||
[](https://github.com/madsmtm/objc2/actions/workflows/ci.yml)
|
||||
|
||||
Objective-C interface and bindings to the `Foundation` framework in Rust.
|
||||
|
||||
Most of the core libraries and frameworks that are in use on Apple systems are
|
||||
written in Objective-C; this crate enables you to interract with those, and
|
||||
provides ready-made bindings for the `Foundation` framework in particular.
|
||||
|
||||
## Example
|
||||
|
||||
```rust
|
||||
use objc2::{class, msg_send, msg_send_id};
|
||||
use objc2::ffi::NSUInteger;
|
||||
use objc2::rc::{Id, Owned};
|
||||
use objc2::runtime::Object;
|
||||
|
||||
let cls = class!(NSObject);
|
||||
let obj: Id<Object, Owned> = unsafe { msg_send_id![cls, new] };
|
||||
|
||||
let hash: NSUInteger = unsafe { msg_send![&obj, hash] };
|
||||
println!("NSObject hash: {}", hash);
|
||||
```
|
||||
|
||||
See [the docs](https://docs.rs/objc2/) for a more thorough overview, or jump
|
||||
right into the [examples].
|
||||
|
||||
This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2),
|
||||
see that for related crates.
|
||||
|
||||
[examples]: https://github.com/madsmtm/objc2/tree/master/objc2/examples
|
||||
165
third-party/vendor/objc2/benches/autorelease.rs
vendored
Normal file
165
third-party/vendor/objc2/benches/autorelease.rs
vendored
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
use core::ffi::c_void;
|
||||
use std::mem::ManuallyDrop;
|
||||
|
||||
use objc2::rc::{autoreleasepool, Id, Shared};
|
||||
use objc2::runtime::{Class, Object, Sel};
|
||||
use objc2::{class, msg_send, sel};
|
||||
|
||||
const BYTES: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
|
||||
fn empty() {
|
||||
#[cfg(feature = "gnustep-1-7")]
|
||||
unsafe {
|
||||
objc2::__gnustep_hack::get_class_to_force_linkage()
|
||||
};
|
||||
}
|
||||
|
||||
fn pool_cleanup() {
|
||||
autoreleasepool(|_| {})
|
||||
}
|
||||
|
||||
fn class() -> &'static Class {
|
||||
class!(NSObject)
|
||||
}
|
||||
|
||||
fn sel() -> Sel {
|
||||
sel!(alloc)
|
||||
}
|
||||
|
||||
fn send_message() -> &'static Class {
|
||||
unsafe { msg_send![class!(NSObject), class] }
|
||||
}
|
||||
|
||||
fn alloc_nsobject() -> *mut Object {
|
||||
unsafe { msg_send![class!(NSObject), alloc] }
|
||||
}
|
||||
|
||||
fn new_nsobject() -> Id<Object, Shared> {
|
||||
let obj = alloc_nsobject();
|
||||
let obj: *mut Object = unsafe { msg_send![obj, init] };
|
||||
unsafe { Id::new(obj).unwrap_unchecked() }
|
||||
}
|
||||
|
||||
fn new_nsdata() -> Id<Object, Shared> {
|
||||
let bytes_ptr = BYTES.as_ptr() as *const c_void;
|
||||
let obj: *mut Object = unsafe { msg_send![class!(NSData), alloc] };
|
||||
let obj: *mut Object = unsafe {
|
||||
msg_send![
|
||||
obj,
|
||||
initWithBytes: bytes_ptr,
|
||||
length: BYTES.len(),
|
||||
]
|
||||
};
|
||||
unsafe { Id::new(obj).unwrap_unchecked() }
|
||||
}
|
||||
|
||||
fn new_leaked_nsdata() -> *const Object {
|
||||
Id::as_ptr(&*ManuallyDrop::new(new_nsdata()))
|
||||
}
|
||||
|
||||
fn autoreleased_nsdata() -> *const Object {
|
||||
// let bytes_ptr = BYTES.as_ptr() as *const c_void;
|
||||
// unsafe {
|
||||
// msg_send![
|
||||
// class!(NSData),
|
||||
// dataWithBytes: bytes_ptr,
|
||||
// length: BYTES.len(),
|
||||
// ]
|
||||
// }
|
||||
unsafe { msg_send![new_leaked_nsdata(), autorelease] }
|
||||
}
|
||||
|
||||
fn new_nsstring() -> Id<Object, Shared> {
|
||||
let obj: *mut Object = unsafe { msg_send![class!(NSString), alloc] };
|
||||
let obj: *mut Object = unsafe { msg_send![obj, init] };
|
||||
unsafe { Id::new(obj).unwrap_unchecked() }
|
||||
}
|
||||
|
||||
fn new_leaked_nsstring() -> *const Object {
|
||||
Id::as_ptr(&*ManuallyDrop::new(new_nsstring()))
|
||||
}
|
||||
|
||||
fn autoreleased_nsstring() -> *const Object {
|
||||
// unsafe { msg_send![class!(NSString), string] }
|
||||
unsafe { msg_send![new_leaked_nsstring(), autorelease] }
|
||||
}
|
||||
|
||||
fn retain_autoreleased(obj: *const Object) -> Id<Object, Shared> {
|
||||
unsafe { Id::retain_autoreleased((obj as *mut Object).cast()).unwrap_unchecked() }
|
||||
}
|
||||
|
||||
fn autoreleased_nsdata_pool_cleanup() -> *const Object {
|
||||
autoreleasepool(|_| autoreleased_nsdata())
|
||||
}
|
||||
|
||||
fn autoreleased_nsdata_fast_caller_cleanup() -> Id<Object, Shared> {
|
||||
retain_autoreleased(autoreleased_nsdata())
|
||||
}
|
||||
|
||||
fn autoreleased_nsdata_fast_caller_cleanup_pool_cleanup() -> Id<Object, Shared> {
|
||||
autoreleasepool(|_| retain_autoreleased(autoreleased_nsdata()))
|
||||
}
|
||||
|
||||
fn autoreleased_nsstring_pool_cleanup() -> *const Object {
|
||||
autoreleasepool(|_| autoreleased_nsstring())
|
||||
}
|
||||
|
||||
fn autoreleased_nsstring_fast_caller_cleanup() -> Id<Object, Shared> {
|
||||
retain_autoreleased(autoreleased_nsstring())
|
||||
}
|
||||
|
||||
fn autoreleased_nsstring_fast_caller_cleanup_pool_cleanup() -> Id<Object, Shared> {
|
||||
autoreleasepool(|_| retain_autoreleased(autoreleased_nsstring()))
|
||||
}
|
||||
|
||||
macro_rules! main_with_warmup {
|
||||
($($f:ident,)+) => {
|
||||
mod warmup_fns {
|
||||
$(
|
||||
#[inline(never)]
|
||||
pub fn $f() {
|
||||
let _ = iai::black_box(super::$f());
|
||||
}
|
||||
)+
|
||||
}
|
||||
|
||||
// Required to get DYLD to resolve the stubs on x86_64
|
||||
fn warmup() {
|
||||
$(
|
||||
warmup_fns::$f();
|
||||
)+
|
||||
}
|
||||
|
||||
iai::main! {
|
||||
warmup,
|
||||
$(
|
||||
$f,
|
||||
)+
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
main_with_warmup! {
|
||||
// Baseline
|
||||
empty,
|
||||
pool_cleanup,
|
||||
class,
|
||||
sel,
|
||||
send_message,
|
||||
alloc_nsobject,
|
||||
new_nsobject,
|
||||
// NSData
|
||||
new_nsdata,
|
||||
new_leaked_nsdata,
|
||||
autoreleased_nsdata,
|
||||
autoreleased_nsdata_pool_cleanup,
|
||||
autoreleased_nsdata_fast_caller_cleanup,
|
||||
autoreleased_nsdata_fast_caller_cleanup_pool_cleanup,
|
||||
// NSString
|
||||
new_nsstring,
|
||||
new_leaked_nsstring,
|
||||
autoreleased_nsstring,
|
||||
autoreleased_nsstring_pool_cleanup,
|
||||
autoreleased_nsstring_fast_caller_cleanup,
|
||||
autoreleased_nsstring_fast_caller_cleanup_pool_cleanup,
|
||||
}
|
||||
44
third-party/vendor/objc2/examples/basic_usage.rs
vendored
Normal file
44
third-party/vendor/objc2/examples/basic_usage.rs
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use objc2::foundation::{NSArray, NSDictionary, NSObject};
|
||||
use objc2::ns_string;
|
||||
use objc2::rc::autoreleasepool;
|
||||
|
||||
fn main() {
|
||||
// Create and compare NSObjects
|
||||
let obj = NSObject::new();
|
||||
#[allow(clippy::eq_op)]
|
||||
{
|
||||
println!("{:?} == {:?}? {:?}", obj, obj, obj == obj);
|
||||
}
|
||||
|
||||
let obj2 = NSObject::new();
|
||||
println!("{:?} == {:?}? {:?}", obj, obj2, obj == obj2);
|
||||
|
||||
// Create an NSArray from a Vec
|
||||
let objs = vec![obj, obj2];
|
||||
let array = NSArray::from_vec(objs);
|
||||
for obj in array.iter() {
|
||||
println!("{:?}", obj);
|
||||
}
|
||||
println!("{}", array.len());
|
||||
|
||||
// Turn the NSArray back into a Vec
|
||||
let mut objs = NSArray::into_vec(array);
|
||||
let obj = objs.pop().unwrap();
|
||||
|
||||
// Create a static NSString
|
||||
let string = ns_string!("Hello, world!");
|
||||
// Use an autoreleasepool to get the `str` contents of the NSString
|
||||
autoreleasepool(|pool| {
|
||||
println!("{}", string.as_str(pool));
|
||||
});
|
||||
// Or simply use the `Display` implementation
|
||||
let _s = string.to_string(); // Using ToString
|
||||
println!("{}", string); // Or Display directly
|
||||
|
||||
// Create a dictionary mapping strings to objects
|
||||
let keys = &[string];
|
||||
let vals = vec![obj];
|
||||
let dict = NSDictionary::from_keys_and_objects(keys, vals);
|
||||
println!("{:?}", dict.get(string));
|
||||
println!("{}", dict.len());
|
||||
}
|
||||
142
third-party/vendor/objc2/examples/class_with_lifetime.rs
vendored
Normal file
142
third-party/vendor/objc2/examples/class_with_lifetime.rs
vendored
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
//! A custom Objective-C class with a lifetime parameter.
|
||||
//!
|
||||
//! Note that we can't use the `declare_class!` macro for this, it doesn't
|
||||
//! support such use-cases. Instead, we'll declare the class manually!
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Once;
|
||||
|
||||
use objc2::declare::{ClassBuilder, Ivar, IvarType};
|
||||
use objc2::foundation::NSObject;
|
||||
use objc2::rc::{Id, Owned};
|
||||
use objc2::runtime::{Class, Object, Sel};
|
||||
use objc2::{msg_send, msg_send_id, sel};
|
||||
use objc2::{ClassType, Encoding, Message, RefEncode};
|
||||
|
||||
/// Helper type for the instance variable
|
||||
struct NumberIvar<'a> {
|
||||
// Doesn't actually matter what we put here, but we have to use the
|
||||
// lifetime parameter somehow
|
||||
p: PhantomData<&'a mut u8>,
|
||||
}
|
||||
|
||||
unsafe impl<'a> IvarType for NumberIvar<'a> {
|
||||
type Type = &'a mut u8;
|
||||
const NAME: &'static str = "_number_ptr";
|
||||
}
|
||||
|
||||
/// Struct that represents our custom object.
|
||||
#[repr(C)]
|
||||
pub struct MyObject<'a> {
|
||||
// Required to give MyObject the proper layout
|
||||
superclass: NSObject,
|
||||
// SAFETY: The ivar is declared below, and is properly initialized in the
|
||||
// designated initializer.
|
||||
//
|
||||
// Note! Attempting to acess the ivar before it has been initialized is
|
||||
// undefined behaviour!
|
||||
number: Ivar<NumberIvar<'a>>,
|
||||
}
|
||||
|
||||
unsafe impl RefEncode for MyObject<'_> {
|
||||
const ENCODING_REF: Encoding = Object::ENCODING_REF;
|
||||
}
|
||||
|
||||
unsafe impl Message for MyObject<'_> {}
|
||||
|
||||
impl<'a> MyObject<'a> {
|
||||
unsafe extern "C" fn init_with_ptr(
|
||||
&mut self,
|
||||
_cmd: Sel,
|
||||
ptr: Option<&'a mut u8>,
|
||||
) -> Option<&'a mut Self> {
|
||||
let this: Option<&mut Self> = unsafe { msg_send![super(self), init] };
|
||||
this.map(|this| {
|
||||
// Properly initialize the number reference
|
||||
Ivar::write(&mut this.number, ptr.expect("got NULL number ptr"));
|
||||
this
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new(number: &'a mut u8) -> Id<Self, Owned> {
|
||||
// SAFETY: The lifetime of the reference is properly bound to the
|
||||
// returned type
|
||||
unsafe {
|
||||
let obj = msg_send_id![Self::class(), alloc];
|
||||
msg_send_id![obj, initWithPtr: number]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self) -> &u8 {
|
||||
&self.number
|
||||
}
|
||||
|
||||
pub fn set(&mut self, number: u8) {
|
||||
**self.number = number;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> ClassType for MyObject<'a> {
|
||||
type Super = NSObject;
|
||||
const NAME: &'static str = "MyObject";
|
||||
|
||||
fn class() -> &'static Class {
|
||||
// TODO: Use std::lazy::LazyCell
|
||||
static REGISTER_CLASS: Once = Once::new();
|
||||
|
||||
REGISTER_CLASS.call_once(|| {
|
||||
let superclass = NSObject::class();
|
||||
let mut builder = ClassBuilder::new(Self::NAME, superclass).unwrap();
|
||||
|
||||
builder.add_static_ivar::<NumberIvar<'a>>();
|
||||
|
||||
unsafe {
|
||||
builder.add_method(
|
||||
sel!(initWithPtr:),
|
||||
Self::init_with_ptr as unsafe extern "C" fn(_, _, _) -> _,
|
||||
);
|
||||
}
|
||||
|
||||
let _cls = builder.register();
|
||||
});
|
||||
|
||||
Class::get("MyObject").unwrap()
|
||||
}
|
||||
|
||||
fn as_super(&self) -> &Self::Super {
|
||||
&self.superclass
|
||||
}
|
||||
|
||||
fn as_super_mut(&mut self) -> &mut Self::Super {
|
||||
&mut self.superclass
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut number = 54;
|
||||
let mut obj = MyObject::new(&mut number);
|
||||
|
||||
// It is not possible to convert to `Id<NSObject, Owned>` since that would
|
||||
// loose the lifetime information that `MyObject` stores
|
||||
// let obj = Id::into_super(obj);
|
||||
|
||||
println!("Number: {}", obj.get());
|
||||
|
||||
obj.set(7);
|
||||
// Won't compile, since `obj` holds a mutable reference to number
|
||||
// println!("Number: {}", number);
|
||||
println!("Number: {}", obj.get());
|
||||
|
||||
let obj = Id::into_shared(obj);
|
||||
let obj2 = obj.clone();
|
||||
|
||||
// We gave up ownership above, so can't edit the number any more!
|
||||
// obj.set(7);
|
||||
|
||||
println!("Number: {}", obj.get());
|
||||
println!("Number: {}", obj2.get());
|
||||
|
||||
drop(obj);
|
||||
drop(obj2);
|
||||
println!("Number: {}", number);
|
||||
}
|
||||
118
third-party/vendor/objc2/examples/delegate.rs
vendored
Normal file
118
third-party/vendor/objc2/examples/delegate.rs
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#![cfg_attr(not(all(feature = "apple", target_os = "macos")), allow(unused))]
|
||||
use objc2::declare::{Ivar, IvarDrop};
|
||||
use objc2::foundation::{NSCopying, NSObject, NSString};
|
||||
use objc2::rc::{Id, Shared};
|
||||
use objc2::runtime::Object;
|
||||
use objc2::{declare_class, extern_class, msg_send, msg_send_id, ns_string, ClassType};
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
#[link(name = "AppKit", kind = "framework")]
|
||||
extern "C" {}
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
extern_class!(
|
||||
#[derive(Debug)]
|
||||
struct NSResponder;
|
||||
|
||||
unsafe impl ClassType for NSResponder {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
declare_class!(
|
||||
#[derive(Debug)]
|
||||
struct CustomAppDelegate {
|
||||
pub ivar: u8,
|
||||
another_ivar: bool,
|
||||
box_ivar: IvarDrop<Box<i32>>,
|
||||
maybe_box_ivar: IvarDrop<Option<Box<i32>>>,
|
||||
id_ivar: IvarDrop<Id<NSString, Shared>>,
|
||||
maybe_id_ivar: IvarDrop<Option<Id<NSString, Shared>>>,
|
||||
}
|
||||
|
||||
unsafe impl ClassType for CustomAppDelegate {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSResponder;
|
||||
const NAME: &'static str = "MyCustomAppDelegate";
|
||||
}
|
||||
|
||||
unsafe impl CustomAppDelegate {
|
||||
#[sel(initWith:another:)]
|
||||
fn init_with(self: &mut Self, ivar: u8, another_ivar: bool) -> Option<&mut Self> {
|
||||
let this: Option<&mut Self> = unsafe { msg_send![super(self), init] };
|
||||
|
||||
// TODO: `ns_string` can't be used inside closures; investigate!
|
||||
let s = ns_string!("def");
|
||||
|
||||
this.map(|this| {
|
||||
Ivar::write(&mut this.ivar, ivar);
|
||||
*this.another_ivar = another_ivar;
|
||||
*this.maybe_box_ivar = None;
|
||||
*this.maybe_id_ivar = Some(s.copy());
|
||||
Ivar::write(&mut this.box_ivar, Box::new(2));
|
||||
Ivar::write(&mut this.id_ivar, NSString::from_str("abc"));
|
||||
this
|
||||
})
|
||||
}
|
||||
|
||||
#[sel(myClassMethod)]
|
||||
fn my_class_method() {
|
||||
println!("A class method!");
|
||||
}
|
||||
}
|
||||
|
||||
// For some reason, `NSApplicationDelegate` is not a "real" protocol we
|
||||
// can retrieve using `objc_getProtocol` - it seems it is created by
|
||||
// `clang` only when used in Objective-C...
|
||||
//
|
||||
// TODO: Investigate this!
|
||||
unsafe impl CustomAppDelegate {
|
||||
/// This is `unsafe` because it expects `sender` to be valid
|
||||
#[sel(applicationDidFinishLaunching:)]
|
||||
unsafe fn did_finish_launching(&self, sender: *mut Object) {
|
||||
println!("Did finish launching!");
|
||||
// Do something with `sender`
|
||||
dbg!(sender);
|
||||
}
|
||||
|
||||
/// Some comment before `sel`.
|
||||
#[sel(applicationWillTerminate:)]
|
||||
/// Some comment after `sel`.
|
||||
fn will_terminate(&self, _: *mut Object) {
|
||||
println!("Will terminate!");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
impl CustomAppDelegate {
|
||||
pub fn new(ivar: u8, another_ivar: bool) -> Id<Self, Shared> {
|
||||
let cls = Self::class();
|
||||
unsafe {
|
||||
msg_send_id![
|
||||
msg_send_id![cls, alloc],
|
||||
initWith: ivar,
|
||||
another: another_ivar,
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
fn main() {
|
||||
let delegate = CustomAppDelegate::new(42, true);
|
||||
|
||||
println!("{:?}", delegate);
|
||||
println!("{:?}", delegate.ivar);
|
||||
println!("{:?}", delegate.another_ivar);
|
||||
println!("{:?}", delegate.box_ivar);
|
||||
println!("{:?}", delegate.maybe_box_ivar);
|
||||
println!("{:?}", delegate.id_ivar);
|
||||
println!("{:?}", delegate.maybe_id_ivar);
|
||||
}
|
||||
|
||||
#[cfg(not(all(feature = "apple", target_os = "macos")))]
|
||||
fn main() {
|
||||
panic!("This example uses AppKit, which is only present on macOS");
|
||||
}
|
||||
58
third-party/vendor/objc2/examples/introspection.rs
vendored
Normal file
58
third-party/vendor/objc2/examples/introspection.rs
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
use objc2::foundation::NSObject;
|
||||
use objc2::runtime::Class;
|
||||
use objc2::{sel, ClassType, Encode};
|
||||
|
||||
fn main() {
|
||||
// Get the class representing `NSObject`
|
||||
let cls = NSObject::class();
|
||||
|
||||
// Inspect various properties of the class
|
||||
println!("NSObject superclass: {:?}", cls.superclass());
|
||||
println!("NSObject size: {}", cls.instance_size());
|
||||
println!(
|
||||
"-[NSObject alloc] would work: {}",
|
||||
cls.responds_to(sel!(alloc))
|
||||
);
|
||||
println!(
|
||||
"+[NSObject alloc] would work: {}",
|
||||
cls.metaclass().responds_to(sel!(alloc))
|
||||
);
|
||||
|
||||
// Inspect an instance variable on the class
|
||||
//
|
||||
// Note: You should not rely on the `isa` ivar being available,
|
||||
// this is only for demonstration.
|
||||
let ivar = cls
|
||||
.instance_variable("isa")
|
||||
.expect("No ivar with name 'isa' found on NSObject");
|
||||
println!(
|
||||
"Instance variable {} has type encoding {:?}",
|
||||
ivar.name(),
|
||||
ivar.type_encoding()
|
||||
);
|
||||
assert!(<*const Class>::ENCODING.equivalent_to_str(ivar.type_encoding()));
|
||||
|
||||
// Inspect a method of the class
|
||||
let method = cls.instance_method(sel!(hash)).unwrap();
|
||||
println!(
|
||||
"-[NSObject hash] takes {} parameters",
|
||||
method.arguments_count()
|
||||
);
|
||||
#[cfg(feature = "malloc")]
|
||||
{
|
||||
let hash_return = method.return_type();
|
||||
println!("-[NSObject hash] return type: {:?}", hash_return);
|
||||
assert!(usize::ENCODING.equivalent_to_str(&hash_return));
|
||||
}
|
||||
|
||||
// Create an instance
|
||||
let obj = NSObject::new();
|
||||
|
||||
println!("NSObject address: {:p}", obj);
|
||||
|
||||
// Access an ivar of the object
|
||||
//
|
||||
// As before, you should not rely on the `isa` ivar being available!
|
||||
let isa = unsafe { *obj.ivar::<*const Class>("isa") };
|
||||
println!("NSObject isa: {:?}", isa);
|
||||
}
|
||||
144
third-party/vendor/objc2/examples/nspasteboard.rs
vendored
Normal file
144
third-party/vendor/objc2/examples/nspasteboard.rs
vendored
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
//! Read from the global pasteboard, and write a new string into it.
|
||||
//!
|
||||
//! Works on macOS 10.7+
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![cfg_attr(not(all(feature = "apple", target_os = "macos")), allow(unused))]
|
||||
|
||||
use std::mem::ManuallyDrop;
|
||||
|
||||
use objc2::foundation::{NSArray, NSDictionary, NSInteger, NSObject, NSString};
|
||||
use objc2::rc::{Id, Shared};
|
||||
use objc2::runtime::{Class, Object};
|
||||
use objc2::{extern_class, msg_send, msg_send_id, ClassType};
|
||||
|
||||
type NSPasteboardType = NSString;
|
||||
type NSPasteboardReadingOptionKey = NSString;
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
#[link(name = "AppKit", kind = "framework")]
|
||||
extern "C" {
|
||||
/// <https://developer.apple.com/documentation/appkit/nspasteboardtypestring?language=objc>
|
||||
static NSPasteboardTypeString: Option<&'static NSPasteboardType>;
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
extern_class!(
|
||||
/// <https://developer.apple.com/documentation/appkit/nspasteboard?language=objc>
|
||||
pub struct NSPasteboard;
|
||||
|
||||
// SAFETY: NSPasteboard actually inherits from NSObject.
|
||||
unsafe impl ClassType for NSPasteboard {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
impl NSPasteboard {
|
||||
/// We return a `Shared` `Id` because `general` can easily be called
|
||||
/// again, and it would return the same object, resulting in two aliasing
|
||||
/// mutable references if we returned an `Owned` Id.
|
||||
///
|
||||
/// Besides, even if we could prevent this, there might be Objective-C
|
||||
/// code somewhere else that accesses this instance.
|
||||
///
|
||||
/// TODO: Is this safe to call outside the main thread?
|
||||
///
|
||||
/// <https://developer.apple.com/documentation/appkit/nspasteboard/1530091-generalpasteboard?language=objc>
|
||||
pub fn general() -> Id<Self, Shared> {
|
||||
unsafe { msg_send_id![Self::class(), generalPasteboard] }
|
||||
}
|
||||
|
||||
/// Simple, straightforward implementation
|
||||
///
|
||||
/// <https://developer.apple.com/documentation/appkit/nspasteboard/1533566-stringfortype?language=objc>
|
||||
pub fn text_impl_1(&self) -> Id<NSString, Shared> {
|
||||
let s = unsafe { NSPasteboardTypeString }.unwrap();
|
||||
unsafe { msg_send_id![self, stringForType: s] }
|
||||
}
|
||||
|
||||
/// More complex implementation using `readObjectsForClasses:options:`,
|
||||
/// intended to show some how some patterns might require more knowledge
|
||||
/// of nitty-gritty details.
|
||||
///
|
||||
/// <https://developer.apple.com/documentation/appkit/nspasteboard/1524454-readobjectsforclasses?language=objc>
|
||||
pub fn text_impl_2(&self) -> Id<NSString, Shared> {
|
||||
// The NSPasteboard API is a bit weird, it requires you to pass
|
||||
// classes as objects, which `objc2::foundation::NSArray` was not
|
||||
// really made for - so we convert the class to an `Object` type
|
||||
// instead. Also, we wrap it in `ManuallyDrop` because I'm not sure
|
||||
// how classes handle `release` calls?
|
||||
//
|
||||
// TODO: Investigate and find a better way to express this in objc2.
|
||||
let string_classes: ManuallyDrop<[Id<Object, Shared>; 1]> = {
|
||||
let cls: *const Class = NSString::class();
|
||||
let cls = cls as *mut Object;
|
||||
unsafe { ManuallyDrop::new([Id::new(cls).unwrap()]) }
|
||||
};
|
||||
// Temporary, see https://github.com/rust-lang/rust-clippy/issues/9101
|
||||
#[allow(unknown_lints, clippy::explicit_auto_deref)]
|
||||
let class_array = NSArray::from_slice(&*string_classes);
|
||||
let options = NSDictionary::new();
|
||||
let objects = unsafe { self.read_objects_for_classes(&class_array, &options) };
|
||||
|
||||
// TODO: Should perhaps return Id<Object, Shared>?
|
||||
let ptr: *const Object = objects.first().unwrap();
|
||||
|
||||
// And this part is weird as well, since we now have to convert the
|
||||
// object into an NSString, which we know it to be since that's what
|
||||
// we told `readObjectsForClasses:options:`.
|
||||
let ptr = ptr as *mut NSString;
|
||||
unsafe { Id::retain(ptr) }.unwrap()
|
||||
}
|
||||
|
||||
/// Defined here to make it easier to declare which types are expected.
|
||||
/// This is a common pattern that I can wholeheartedly recommend!
|
||||
///
|
||||
/// SAFETY: `class_array` must contain classes!
|
||||
unsafe fn read_objects_for_classes(
|
||||
&self,
|
||||
class_array: &NSArray<Object, Shared>,
|
||||
options: &NSDictionary<NSPasteboardReadingOptionKey, Object>,
|
||||
) -> Id<NSArray<Object, Shared>, Shared> {
|
||||
unsafe { msg_send_id![self, readObjectsForClasses: class_array, options: options] }
|
||||
}
|
||||
|
||||
/// This takes `&self` even though `writeObjects:` would seem to mutate
|
||||
/// the pasteboard. "What is going on?", you might rightfully ask!
|
||||
///
|
||||
/// We do this because we can't soundly get a mutable reference to the
|
||||
/// global `NSPasteboard` instance, see [`NSPasteboard::general`].
|
||||
///
|
||||
/// This is sound because `NSPasteboard` contains `NSObject`, which in
|
||||
/// turn contains `UnsafeCell`, allowing interior mutability.
|
||||
///
|
||||
/// <https://developer.apple.com/documentation/appkit/nspasteboard/1533599-clearcontents?language=objc>
|
||||
/// <https://developer.apple.com/documentation/appkit/nspasteboard/1525945-writeobjects?language=objc>
|
||||
pub fn set_text(&self, text: Id<NSString, Shared>) {
|
||||
let _: NSInteger = unsafe { msg_send![self, clearContents] };
|
||||
let string_array = NSArray::from_slice(&[text]);
|
||||
let res: bool = unsafe { msg_send![self, writeObjects: &*string_array] };
|
||||
if !res {
|
||||
panic!("Failed writing to pasteboard");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
fn main() {
|
||||
let pasteboard = NSPasteboard::general();
|
||||
let impl_1 = pasteboard.text_impl_1();
|
||||
let impl_2 = pasteboard.text_impl_2();
|
||||
println!("Pasteboard text from implementation 1 was: {:?}", impl_1);
|
||||
println!("Pasteboard text from implementation 2 was: {:?}", impl_2);
|
||||
assert_eq!(impl_1, impl_2);
|
||||
|
||||
let s = NSString::from_str("Hello, world!");
|
||||
pasteboard.set_text(s.clone());
|
||||
println!("Now the pasteboard text should be: {:?}", s);
|
||||
assert_eq!(s, pasteboard.text_impl_1());
|
||||
}
|
||||
|
||||
#[cfg(not(all(feature = "apple", target_os = "macos")))]
|
||||
fn main() {
|
||||
panic!("this example only works on macOS");
|
||||
}
|
||||
178
third-party/vendor/objc2/examples/speech_synthethis.rs
vendored
Normal file
178
third-party/vendor/objc2/examples/speech_synthethis.rs
vendored
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
//! Speak synthethized text.
|
||||
//!
|
||||
//! This uses `NSSpeechSynthesizer` on macOS, and `AVSpeechSynthesizer` on
|
||||
//! other Apple platforms. Note that `AVSpeechSynthesizer` _is_ available on
|
||||
//! macOS, but only since 10.15!
|
||||
//!
|
||||
//! TODO: Unsure about when to use `&mut` here?
|
||||
//!
|
||||
//! Works on macOS >= 10.7 and iOS > 7.0.
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![cfg_attr(feature = "gnustep-1-7", allow(unused))]
|
||||
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use objc2::foundation::{NSObject, NSString};
|
||||
use objc2::rc::{Id, Owned};
|
||||
use objc2::{extern_class, msg_send, msg_send_id, ns_string, ClassType};
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
mod appkit {
|
||||
use objc2::{foundation::NSCopying, rc::Shared};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[link(name = "AppKit", kind = "framework")]
|
||||
extern "C" {}
|
||||
|
||||
extern_class!(
|
||||
/// <https://developer.apple.com/documentation/appkit/nsspeechsynthesizer?language=objc>
|
||||
pub struct NSSpeechSynthesizer;
|
||||
|
||||
unsafe impl ClassType for NSSpeechSynthesizer {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
impl NSSpeechSynthesizer {
|
||||
// Uses default voice
|
||||
pub fn new() -> Id<Self, Owned> {
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
fn set_rate(&mut self, rate: f32) {
|
||||
unsafe { msg_send![self, setRate: rate] }
|
||||
}
|
||||
|
||||
fn set_volume(&mut self, volume: f32) {
|
||||
unsafe { msg_send![self, setVolume: volume] }
|
||||
}
|
||||
|
||||
fn start_speaking(&mut self, s: &NSString) {
|
||||
unsafe { msg_send![self, startSpeakingString: s] }
|
||||
}
|
||||
|
||||
pub fn speak(&mut self, utterance: &Utterance) {
|
||||
// Convert to the range 90-720 that `NSSpeechSynthesizer` seems to
|
||||
// support
|
||||
//
|
||||
// Note that you'd probably want a nonlinear conversion here to
|
||||
// make it match `AVSpeechSynthesizer`.
|
||||
self.set_rate(90.0 + (utterance.rate * (360.0 - 90.0)));
|
||||
self.set_volume(utterance.volume);
|
||||
self.start_speaking(&utterance.string);
|
||||
}
|
||||
|
||||
pub fn is_speaking(&self) -> bool {
|
||||
unsafe { msg_send![self, isSpeaking] }
|
||||
}
|
||||
}
|
||||
|
||||
// Shim to make NSSpeechSynthesizer work similar to AVSpeechSynthesizer
|
||||
pub struct Utterance {
|
||||
rate: f32,
|
||||
volume: f32,
|
||||
string: Id<NSString, Shared>,
|
||||
}
|
||||
|
||||
impl Utterance {
|
||||
pub fn new(string: &NSString) -> Self {
|
||||
Self {
|
||||
rate: 0.5,
|
||||
volume: 1.0,
|
||||
string: string.copy(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_rate(&mut self, rate: f32) {
|
||||
self.rate = rate;
|
||||
}
|
||||
|
||||
pub fn set_volume(&mut self, volume: f32) {
|
||||
self.volume = volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "apple", not(target_os = "macos")))]
|
||||
mod avfaudio {
|
||||
use super::*;
|
||||
|
||||
#[link(name = "AVFoundation", kind = "framework")]
|
||||
extern "C" {}
|
||||
|
||||
extern_class!(
|
||||
/// <https://developer.apple.com/documentation/avfaudio/avspeechsynthesizer?language=objc>
|
||||
#[derive(Debug)]
|
||||
pub struct AVSpeechSynthesizer;
|
||||
|
||||
unsafe impl ClassType for AVSpeechSynthesizer {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
impl AVSpeechSynthesizer {
|
||||
pub fn new() -> Id<Self, Owned> {
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
pub fn speak(&mut self, utterance: &AVSpeechUtterance) {
|
||||
unsafe { msg_send![self, speakUtterance: utterance] }
|
||||
}
|
||||
|
||||
pub fn is_speaking(&self) -> bool {
|
||||
unsafe { msg_send![self, isSpeaking] }
|
||||
}
|
||||
}
|
||||
|
||||
extern_class!(
|
||||
/// <https://developer.apple.com/documentation/avfaudio/avspeechutterance?language=objc>
|
||||
#[derive(Debug)]
|
||||
pub struct AVSpeechUtterance;
|
||||
|
||||
unsafe impl ClassType for AVSpeechUtterance {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
impl AVSpeechUtterance {
|
||||
pub fn new(string: &NSString) -> Id<Self, Owned> {
|
||||
unsafe { msg_send_id![msg_send_id![Self::class(), alloc], initWithString: string] }
|
||||
}
|
||||
|
||||
pub fn set_rate(&mut self, rate: f32) {
|
||||
unsafe { msg_send![self, setRate: rate] }
|
||||
}
|
||||
|
||||
pub fn set_volume(&mut self, volume: f32) {
|
||||
unsafe { msg_send![self, setVolume: volume] }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
use appkit::{NSSpeechSynthesizer as Synthesizer, Utterance};
|
||||
#[cfg(all(feature = "apple", not(target_os = "macos")))]
|
||||
use avfaudio::{AVSpeechSynthesizer as Synthesizer, AVSpeechUtterance as Utterance};
|
||||
|
||||
#[cfg(feature = "apple")]
|
||||
fn main() {
|
||||
let mut synthesizer = Synthesizer::new();
|
||||
let mut utterance = Utterance::new(ns_string!("Hello from Rust!"));
|
||||
utterance.set_rate(0.5);
|
||||
utterance.set_volume(0.5);
|
||||
synthesizer.speak(&utterance);
|
||||
|
||||
// Wait until speech has properly started up
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
// Wait until finished speaking
|
||||
while synthesizer.is_speaking() {
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gnustep-1-7")]
|
||||
fn main() {
|
||||
panic!("this example is only available on Apple targets");
|
||||
}
|
||||
705
third-party/vendor/objc2/src/__macro_helpers.rs
vendored
Normal file
705
third-party/vendor/objc2/src/__macro_helpers.rs
vendored
Normal file
|
|
@ -0,0 +1,705 @@
|
|||
use crate::__sel_inner;
|
||||
use crate::rc::{Allocated, Id, Ownership};
|
||||
use crate::runtime::{Class, Object, Sel};
|
||||
use crate::{Message, MessageArguments, MessageReceiver};
|
||||
|
||||
pub use crate::cache::CachedClass;
|
||||
pub use crate::cache::CachedSel;
|
||||
|
||||
pub use core::borrow::{Borrow, BorrowMut};
|
||||
pub use core::cell::UnsafeCell;
|
||||
pub use core::convert::{AsMut, AsRef};
|
||||
pub use core::mem::size_of;
|
||||
pub use core::ops::{Deref, DerefMut};
|
||||
pub use core::option::Option::{self, None, Some};
|
||||
pub use core::primitive::{bool, str, u8};
|
||||
pub use core::ptr::drop_in_place;
|
||||
pub use core::{compile_error, concat, panic, stringify};
|
||||
// TODO: Use `core::cell::LazyCell`
|
||||
pub use std::sync::Once;
|
||||
|
||||
// Common selectors.
|
||||
//
|
||||
// These are put here to deduplicate the cached selector, and when using
|
||||
// `unstable-static-sel`, the statics.
|
||||
//
|
||||
// Note that our assembly tests of `unstable-static-sel-inlined` output a GOT
|
||||
// entry for such accesses, but that is just a limitation of our tests - the
|
||||
// actual assembly is as one would expect.
|
||||
|
||||
#[inline]
|
||||
pub fn alloc() -> Sel {
|
||||
// SAFETY: Must have NUL byte
|
||||
__sel_inner!("alloc\0", "alloc")
|
||||
}
|
||||
#[inline]
|
||||
pub fn init() -> Sel {
|
||||
// SAFETY: Must have NUL byte
|
||||
__sel_inner!("init\0", "init")
|
||||
}
|
||||
#[inline]
|
||||
pub fn new() -> Sel {
|
||||
// SAFETY: Must have NUL byte
|
||||
__sel_inner!("new\0", "new")
|
||||
}
|
||||
|
||||
/// Helper for specifying the retain semantics for a given selector family.
|
||||
///
|
||||
/// Note that we can't actually check if a method is in a method family; only
|
||||
/// whether the _selector_ is in a _selector_ family.
|
||||
///
|
||||
/// The slight difference here is:
|
||||
/// - The method may be annotated with the `objc_method_family` attribute,
|
||||
/// which would cause it to be in a different family. That this is not the
|
||||
/// case is part of the `unsafe` contract of `msg_send_id!`.
|
||||
/// - The method may not obey the added restrictions of the method family.
|
||||
/// The added restrictions are:
|
||||
/// - `new`, `alloc`, `copy` and `mutableCopy`: The method must return a
|
||||
/// retainable object pointer type - we ensure this by making
|
||||
/// `message_send_id` return `Id`.
|
||||
/// - `init`: The method must be an instance method and must return an
|
||||
/// Objective-C pointer type - We ensure this by taking `Id<T, O>`, which
|
||||
/// means it can't be a class method!
|
||||
///
|
||||
/// While we're at it, we also limit a few other things to help the user out,
|
||||
/// like only allowing `&Class` in `new` - this is not strictly required by
|
||||
/// ARC though!
|
||||
///
|
||||
/// <https://clang.llvm.org/docs/AutomaticReferenceCounting.html#retainable-object-pointers-as-operands-and-arguments>
|
||||
pub struct RetainSemantics<
|
||||
// `new` family
|
||||
const NEW: bool,
|
||||
// `alloc` family
|
||||
const ALLOC: bool,
|
||||
// `init` family
|
||||
const INIT: bool,
|
||||
// `copy` or `mutableCopy` family
|
||||
const COPY_OR_MUT_COPY: bool,
|
||||
> {}
|
||||
|
||||
type New = RetainSemantics<true, false, false, false>;
|
||||
type Alloc = RetainSemantics<false, true, false, false>;
|
||||
type Init = RetainSemantics<false, false, true, false>;
|
||||
type CopyOrMutCopy = RetainSemantics<false, false, false, true>;
|
||||
type Other = RetainSemantics<false, false, false, false>;
|
||||
|
||||
pub trait MsgSendId<T, U: ?Sized, O: Ownership> {
|
||||
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<U, O>>(
|
||||
obj: T,
|
||||
sel: Sel,
|
||||
args: A,
|
||||
) -> R;
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Message, O: Ownership> MsgSendId<&'_ Class, T, O> for New {
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<T, O>>(
|
||||
cls: &Class,
|
||||
sel: Sel,
|
||||
args: A,
|
||||
) -> R {
|
||||
// SAFETY: Checked by caller
|
||||
let obj = unsafe { MessageReceiver::send_message(cls, sel, args) };
|
||||
// SAFETY: The selector is `new`, so this has +1 retain count
|
||||
let obj = unsafe { Id::new(obj) };
|
||||
R::maybe_unwrap::<Self>(obj, (cls, sel))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Message, O: Ownership> MsgSendId<&'_ Class, Allocated<T>, O> for Alloc {
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<Allocated<T>, O>>(
|
||||
cls: &Class,
|
||||
sel: Sel,
|
||||
args: A,
|
||||
) -> R {
|
||||
// SAFETY: Checked by caller
|
||||
let obj = unsafe { MessageReceiver::send_message(cls, sel, args) };
|
||||
// SAFETY: The selector is `alloc`, so this has +1 retain count
|
||||
let obj = unsafe { Id::new_allocated(obj) };
|
||||
R::maybe_unwrap::<Self>(obj, (cls, sel))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Message, O: Ownership> MsgSendId<Option<Id<Allocated<T>, O>>, T, O> for Init {
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<T, O>>(
|
||||
obj: Option<Id<Allocated<T>, O>>,
|
||||
sel: Sel,
|
||||
args: A,
|
||||
) -> R {
|
||||
let ptr = Id::option_into_ptr(obj.map(|obj| unsafe { Id::assume_init(obj) }));
|
||||
// SAFETY: `ptr` may be null here, but that's fine since the return
|
||||
// is `*mut T`, which is one of the few types where messages to nil is
|
||||
// allowed.
|
||||
//
|
||||
// We do this for efficiency, to avoid having a branch after every
|
||||
// `alloc`, that the user did not intend.
|
||||
let obj = unsafe { MessageReceiver::send_message(ptr, sel, args) };
|
||||
// SAFETY: The selector is `init`, so this has +1 retain count
|
||||
let obj = unsafe { Id::new(obj) };
|
||||
R::maybe_unwrap::<Self>(obj, (ptr.cast(), sel))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: MessageReceiver, U: ?Sized + Message, O: Ownership> MsgSendId<T, U, O> for CopyOrMutCopy {
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<U, O>>(
|
||||
obj: T,
|
||||
sel: Sel,
|
||||
args: A,
|
||||
) -> R {
|
||||
// SAFETY: Checked by caller
|
||||
let obj = unsafe { MessageReceiver::send_message(obj, sel, args) };
|
||||
// SAFETY: The selector is `copy` or `mutableCopy`, so this has +1
|
||||
// retain count
|
||||
let obj = unsafe { Id::new(obj) };
|
||||
R::maybe_unwrap::<Self>(obj, ())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: MessageReceiver, U: Message, O: Ownership> MsgSendId<T, U, O> for Other {
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<U, O>>(
|
||||
obj: T,
|
||||
sel: Sel,
|
||||
args: A,
|
||||
) -> R {
|
||||
let ptr = obj.__as_raw_receiver();
|
||||
// SAFETY: Checked by caller
|
||||
let obj = unsafe { MessageReceiver::send_message(ptr, sel, args) };
|
||||
// All code between the message send and the `retain_autoreleased`
|
||||
// must be able to be optimized away for this to work.
|
||||
|
||||
// SAFETY: The selector is not `new`, `alloc`, `init`, `copy` nor
|
||||
// `mutableCopy`, so the object must be manually retained.
|
||||
let obj = unsafe { Id::retain_autoreleased(obj) };
|
||||
|
||||
// SAFETY: The object is still valid after a message send to a
|
||||
// normal method - it would not be if the method was `init`.
|
||||
R::maybe_unwrap::<Self>(obj, (unsafe { ptr.as_ref() }, sel))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MaybeUnwrap<T: ?Sized, O: Ownership> {
|
||||
fn maybe_unwrap<'a, Failed: MsgSendIdFailed<'a>>(
|
||||
obj: Option<Id<T, O>>,
|
||||
args: Failed::Args,
|
||||
) -> Self;
|
||||
}
|
||||
|
||||
impl<T: ?Sized, O: Ownership> MaybeUnwrap<T, O> for Option<Id<T, O>> {
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn maybe_unwrap<'a, Failed: MsgSendIdFailed<'a>>(
|
||||
obj: Option<Id<T, O>>,
|
||||
_args: Failed::Args,
|
||||
) -> Self {
|
||||
obj
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized, O: Ownership> MaybeUnwrap<T, O> for Id<T, O> {
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn maybe_unwrap<'a, Failed: MsgSendIdFailed<'a>>(
|
||||
obj: Option<Id<T, O>>,
|
||||
args: Failed::Args,
|
||||
) -> Self {
|
||||
match obj {
|
||||
Some(obj) => obj,
|
||||
None => Failed::failed(args),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: It would have been much easier to do this kind of thing using
|
||||
// closures, but then `track_caller` doesn't work properly!
|
||||
pub trait MsgSendIdFailed<'a> {
|
||||
type Args;
|
||||
|
||||
fn failed(args: Self::Args) -> !;
|
||||
}
|
||||
|
||||
impl<'a> MsgSendIdFailed<'a> for New {
|
||||
type Args = (&'a Class, Sel);
|
||||
|
||||
#[cold]
|
||||
#[track_caller]
|
||||
fn failed((cls, sel): Self::Args) -> ! {
|
||||
if sel == new() {
|
||||
panic!("failed creating new instance of {:?}", cls)
|
||||
} else {
|
||||
panic!("failed creating new instance using +[{:?} {:?}]", cls, sel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MsgSendIdFailed<'a> for Alloc {
|
||||
type Args = (&'a Class, Sel);
|
||||
|
||||
#[cold]
|
||||
#[track_caller]
|
||||
fn failed((cls, sel): Self::Args) -> ! {
|
||||
if sel == alloc() {
|
||||
panic!("failed allocating {:?}", cls)
|
||||
} else {
|
||||
panic!("failed allocating with +[{:?} {:?}]", cls, sel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MsgSendIdFailed<'_> for Init {
|
||||
type Args = (*const Object, Sel);
|
||||
|
||||
#[cold]
|
||||
#[track_caller]
|
||||
fn failed((ptr, sel): Self::Args) -> ! {
|
||||
if ptr.is_null() {
|
||||
panic!("failed allocating object")
|
||||
} else {
|
||||
// We can't really display a more descriptive message here since the
|
||||
// object is consumed by `init` and may not be valid any more.
|
||||
if sel == init() {
|
||||
panic!("failed initializing object")
|
||||
} else {
|
||||
panic!("failed initializing object with -{:?}", sel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MsgSendIdFailed<'_> for CopyOrMutCopy {
|
||||
type Args = ();
|
||||
|
||||
#[cold]
|
||||
#[track_caller]
|
||||
fn failed(_: Self::Args) -> ! {
|
||||
panic!("failed copying object")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MsgSendIdFailed<'a> for Other {
|
||||
type Args = (Option<&'a Object>, Sel);
|
||||
|
||||
#[cold]
|
||||
#[track_caller]
|
||||
fn failed((obj, sel): Self::Args) -> ! {
|
||||
if let Some(obj) = obj {
|
||||
let cls = obj.class();
|
||||
panic!(
|
||||
"unexpected NULL returned from {}[{:?} {:?}]",
|
||||
if cls.is_metaclass() { "+" } else { "-" },
|
||||
cls,
|
||||
sel,
|
||||
)
|
||||
} else {
|
||||
panic!("unexpected NULL {:?}; receiver was NULL", sel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether a given selector is said to be in a given selector family.
|
||||
///
|
||||
/// <https://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-method-families>
|
||||
pub const fn in_selector_family(mut selector: &[u8], mut family: &[u8]) -> bool {
|
||||
// Skip leading underscores from selector
|
||||
loop {
|
||||
selector = match selector {
|
||||
[b'_', rest @ ..] => rest,
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
// Compare each character
|
||||
loop {
|
||||
(selector, family) = match (selector, family) {
|
||||
// Remaining items
|
||||
([s, selector @ ..], [f, family @ ..]) => {
|
||||
if *s == *f {
|
||||
// Next iteration
|
||||
(selector, family)
|
||||
} else {
|
||||
// Family does not begin with selector
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Equal
|
||||
([], []) => {
|
||||
return true;
|
||||
}
|
||||
// Selector can't be part of familiy if smaller than it
|
||||
([], _) => {
|
||||
return false;
|
||||
}
|
||||
// Remaining items in selector
|
||||
// -> ensure next character is not lowercase
|
||||
([s, ..], []) => {
|
||||
return !s.is_ascii_lowercase();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper struct for emitting the module info that macOS 32-bit requires.
|
||||
///
|
||||
/// <https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/CodeGen/CGObjCMac.cpp#L5211-L5234>
|
||||
#[repr(C)]
|
||||
pub struct ModuleInfo {
|
||||
version: usize,
|
||||
size: usize,
|
||||
name: *const u8,
|
||||
symtab: *const (),
|
||||
}
|
||||
|
||||
// SAFETY: ModuleInfo is immutable.
|
||||
unsafe impl Sync for ModuleInfo {}
|
||||
|
||||
impl ModuleInfo {
|
||||
/// This is hardcoded in clang as 7.
|
||||
const VERSION: usize = 7;
|
||||
|
||||
pub const fn new(name: *const u8) -> Self {
|
||||
Self {
|
||||
version: Self::VERSION,
|
||||
size: core::mem::size_of::<Self>(),
|
||||
name,
|
||||
// We don't expose any symbols
|
||||
symtab: core::ptr::null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use alloc::vec;
|
||||
use core::ptr;
|
||||
|
||||
#[cfg(feature = "objc2-proc-macros")]
|
||||
use crate::__hash_idents;
|
||||
use crate::foundation::{NSDictionary, NSObject, NSString, NSValue, NSZone};
|
||||
use crate::rc::{Owned, RcTestObject, Shared, ThreadTestData};
|
||||
use crate::runtime::Object;
|
||||
use crate::{class, msg_send_id, ns_string, ClassType};
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
let _obj: Id<Object, Shared> = unsafe { msg_send_id![NSObject::class(), new] };
|
||||
let _obj: Option<Id<Object, Shared>> = unsafe { msg_send_id![NSObject::class(), new] };
|
||||
}
|
||||
|
||||
#[test]
|
||||
// newScriptingObjectOfClass only available on macOS
|
||||
#[cfg_attr(not(all(feature = "apple", target_os = "macos")), ignore)]
|
||||
fn test_new_with_args() {
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
let object_class = RcTestObject::class();
|
||||
let key = ns_string!("");
|
||||
let contents_value: *const Object = ptr::null();
|
||||
let properties: Id<NSDictionary<NSString, Object>, _> =
|
||||
NSDictionary::from_keys_and_objects::<NSString>(&[], vec![]);
|
||||
|
||||
let _obj: Option<Id<Object, Shared>> = unsafe {
|
||||
msg_send_id![
|
||||
NSObject::class(),
|
||||
newScriptingObjectOfClass: object_class,
|
||||
forValueForKey: key,
|
||||
withContentsValue: contents_value,
|
||||
properties: &*properties,
|
||||
]
|
||||
};
|
||||
expected.alloc += 1;
|
||||
expected.init += 1;
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro_alloc() {
|
||||
let mut expected = ThreadTestData::current();
|
||||
let cls = RcTestObject::class();
|
||||
|
||||
let obj: Id<Allocated<RcTestObject>, Shared> = unsafe { msg_send_id![cls, alloc] };
|
||||
expected.alloc += 1;
|
||||
expected.assert_current();
|
||||
|
||||
drop(obj);
|
||||
expected.release += 1;
|
||||
expected.dealloc += 1;
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_alloc_with_zone() {
|
||||
let mut expected = ThreadTestData::current();
|
||||
let cls = RcTestObject::class();
|
||||
|
||||
let zone: *const NSZone = ptr::null();
|
||||
let _obj: Id<Allocated<RcTestObject>, Owned> =
|
||||
unsafe { msg_send_id![cls, allocWithZone: zone] };
|
||||
expected.alloc += 1;
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro_init() {
|
||||
let mut expected = ThreadTestData::current();
|
||||
let cls = RcTestObject::class();
|
||||
|
||||
let obj: Option<Id<Allocated<RcTestObject>, Shared>> = unsafe { msg_send_id![cls, alloc] };
|
||||
expected.alloc += 1;
|
||||
// Don't check allocation error
|
||||
let _obj: Id<RcTestObject, Shared> = unsafe { msg_send_id![obj, init] };
|
||||
expected.init += 1;
|
||||
expected.assert_current();
|
||||
|
||||
let obj: Option<Id<Allocated<RcTestObject>, Shared>> = unsafe { msg_send_id![cls, alloc] };
|
||||
expected.alloc += 1;
|
||||
// Check allocation error before init
|
||||
let obj = obj.unwrap();
|
||||
let _obj: Id<RcTestObject, Shared> = unsafe { msg_send_id![Some(obj), init] };
|
||||
expected.init += 1;
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro() {
|
||||
let mut expected = ThreadTestData::current();
|
||||
let cls = RcTestObject::class();
|
||||
crate::rc::autoreleasepool(|_| {
|
||||
let _obj: Id<RcTestObject, Owned> = unsafe { msg_send_id![cls, new] };
|
||||
expected.alloc += 1;
|
||||
expected.init += 1;
|
||||
expected.assert_current();
|
||||
|
||||
let obj = unsafe { msg_send_id![cls, alloc] };
|
||||
expected.alloc += 1;
|
||||
expected.assert_current();
|
||||
|
||||
let obj: Id<RcTestObject, Owned> = unsafe { msg_send_id![obj, init] };
|
||||
expected.init += 1;
|
||||
expected.assert_current();
|
||||
|
||||
let _copy: Id<RcTestObject, Shared> = unsafe { msg_send_id![&obj, copy] };
|
||||
expected.copy += 1;
|
||||
expected.alloc += 1;
|
||||
expected.init += 1;
|
||||
expected.assert_current();
|
||||
|
||||
let _mutable_copy: Id<RcTestObject, Shared> =
|
||||
unsafe { msg_send_id![&obj, mutableCopy] };
|
||||
expected.mutable_copy += 1;
|
||||
expected.alloc += 1;
|
||||
expected.init += 1;
|
||||
expected.assert_current();
|
||||
|
||||
let _self: Id<RcTestObject, Shared> = unsafe { msg_send_id![&obj, self] };
|
||||
expected.retain += 1;
|
||||
expected.assert_current();
|
||||
|
||||
let _desc: Option<Id<RcTestObject, Shared>> =
|
||||
unsafe { msg_send_id![&obj, description] };
|
||||
expected.assert_current();
|
||||
});
|
||||
expected.release += 5;
|
||||
expected.dealloc += 4;
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "failed creating new instance of NSValue"]
|
||||
// GNUStep instead returns an invalid instance that panics on accesses
|
||||
#[cfg_attr(feature = "gnustep-1-7", ignore)]
|
||||
fn new_nsvalue_fails() {
|
||||
let _val: Id<NSValue, Shared> = unsafe { msg_send_id![NSValue::class(), new] };
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "failed creating new instance using +[RcTestObject newReturningNull]"]
|
||||
fn test_new_with_null() {
|
||||
let _obj: Id<RcTestObject, Owned> =
|
||||
unsafe { msg_send_id![RcTestObject::class(), newReturningNull] };
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "failed allocating with +[RcTestObject allocReturningNull]"]
|
||||
fn test_alloc_with_null() {
|
||||
let _obj: Id<Allocated<RcTestObject>, Owned> =
|
||||
unsafe { msg_send_id![RcTestObject::class(), allocReturningNull] };
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "failed initializing object with -initReturningNull"]
|
||||
fn test_init_with_null() {
|
||||
let obj: Option<Id<Allocated<RcTestObject>, Owned>> =
|
||||
unsafe { msg_send_id![RcTestObject::class(), alloc] };
|
||||
let _obj: Id<RcTestObject, Owned> = unsafe { msg_send_id![obj, initReturningNull] };
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "failed allocating object"]
|
||||
#[cfg(not(feature = "verify_message"))] // Does NULL receiver checks
|
||||
fn test_init_with_null_receiver() {
|
||||
let obj: Option<Id<Allocated<RcTestObject>, Owned>> = None;
|
||||
let _obj: Id<RcTestObject, Owned> = unsafe { msg_send_id![obj, init] };
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "failed copying object"]
|
||||
fn test_copy_with_null() {
|
||||
let obj = Id::into_shared(RcTestObject::new());
|
||||
let _obj: Id<RcTestObject, Shared> = unsafe { msg_send_id![&obj, copyReturningNull] };
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "unexpected NULL returned from -[RcTestObject methodReturningNull]"]
|
||||
fn test_normal_with_null() {
|
||||
let obj = Id::into_shared(RcTestObject::new());
|
||||
let _obj: Id<RcTestObject, Shared> = unsafe { msg_send_id![&obj, methodReturningNull] };
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "unexpected NULL description; receiver was NULL"]
|
||||
#[cfg(not(feature = "verify_message"))] // Does NULL receiver checks
|
||||
fn test_normal_with_null_receiver() {
|
||||
let obj: *const NSObject = ptr::null();
|
||||
let _obj: Id<NSString, Shared> = unsafe { msg_send_id![obj, description] };
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_in_selector_family() {
|
||||
// Common cases
|
||||
|
||||
assert!(in_selector_family(b"alloc", b"alloc"));
|
||||
assert!(in_selector_family(b"allocWithZone:", b"alloc"));
|
||||
assert!(!in_selector_family(b"dealloc", b"alloc"));
|
||||
assert!(!in_selector_family(b"initialize", b"init"));
|
||||
assert!(!in_selector_family(b"decimalNumberWithDecimal:", b"init"));
|
||||
assert!(in_selector_family(b"initWithCapacity:", b"init"));
|
||||
assert!(in_selector_family(b"_initButPrivate:withParam:", b"init"));
|
||||
assert!(!in_selector_family(b"description", b"init"));
|
||||
assert!(!in_selector_family(b"inIT", b"init"));
|
||||
|
||||
assert!(!in_selector_family(b"init", b"copy"));
|
||||
assert!(!in_selector_family(b"copyingStuff:", b"copy"));
|
||||
assert!(in_selector_family(b"copyWithZone:", b"copy"));
|
||||
assert!(!in_selector_family(b"initWithArray:copyItems:", b"copy"));
|
||||
assert!(in_selector_family(b"copyItemAtURL:toURL:error:", b"copy"));
|
||||
|
||||
assert!(!in_selector_family(b"mutableCopying", b"mutableCopy"));
|
||||
assert!(in_selector_family(b"mutableCopyWithZone:", b"mutableCopy"));
|
||||
assert!(in_selector_family(b"mutableCopyWithZone:", b"mutableCopy"));
|
||||
|
||||
assert!(in_selector_family(
|
||||
b"newScriptingObjectOfClass:forValueForKey:withContentsValue:properties:",
|
||||
b"new"
|
||||
));
|
||||
assert!(in_selector_family(
|
||||
b"newScriptingObjectOfClass:forValueForKey:withContentsValue:properties:",
|
||||
b"new"
|
||||
));
|
||||
assert!(!in_selector_family(b"newsstandAssetDownload", b"new"));
|
||||
|
||||
// Trying to weed out edge-cases:
|
||||
|
||||
assert!(in_selector_family(b"__abcDef", b"abc"));
|
||||
assert!(in_selector_family(b"_abcDef", b"abc"));
|
||||
assert!(in_selector_family(b"abcDef", b"abc"));
|
||||
assert!(in_selector_family(b"___a", b"a"));
|
||||
assert!(in_selector_family(b"__a", b"a"));
|
||||
assert!(in_selector_family(b"_a", b"a"));
|
||||
assert!(in_selector_family(b"a", b"a"));
|
||||
|
||||
assert!(!in_selector_family(b"_abcdef", b"abc"));
|
||||
assert!(!in_selector_family(b"_abcdef", b"def"));
|
||||
assert!(!in_selector_family(b"_bcdef", b"abc"));
|
||||
assert!(!in_selector_family(b"a_bc", b"abc"));
|
||||
assert!(!in_selector_family(b"abcdef", b"abc"));
|
||||
assert!(!in_selector_family(b"abcdef", b"def"));
|
||||
assert!(!in_selector_family(b"abcdef", b"abb"));
|
||||
assert!(!in_selector_family(b"___", b"a"));
|
||||
assert!(!in_selector_family(b"_", b"a"));
|
||||
assert!(!in_selector_family(b"", b"a"));
|
||||
|
||||
assert!(in_selector_family(b"copy", b"copy"));
|
||||
assert!(in_selector_family(b"copy:", b"copy"));
|
||||
assert!(in_selector_family(b"copyMe", b"copy"));
|
||||
assert!(in_selector_family(b"_copy", b"copy"));
|
||||
assert!(in_selector_family(b"_copy:", b"copy"));
|
||||
assert!(in_selector_family(b"_copyMe", b"copy"));
|
||||
assert!(!in_selector_family(b"copying", b"copy"));
|
||||
assert!(!in_selector_family(b"copying:", b"copy"));
|
||||
assert!(!in_selector_family(b"_copying", b"copy"));
|
||||
assert!(!in_selector_family(b"Copy", b"copy"));
|
||||
assert!(!in_selector_family(b"COPY", b"copy"));
|
||||
|
||||
// Empty family (not supported)
|
||||
assert!(in_selector_family(b"___", b""));
|
||||
assert!(in_selector_family(b"__", b""));
|
||||
assert!(in_selector_family(b"_", b""));
|
||||
assert!(in_selector_family(b"", b""));
|
||||
assert!(!in_selector_family(b"_a", b""));
|
||||
assert!(!in_selector_family(b"a", b""));
|
||||
assert!(in_selector_family(b"_A", b""));
|
||||
assert!(in_selector_family(b"A", b""));
|
||||
}
|
||||
|
||||
mod test_trait_disambugated {
|
||||
use super::*;
|
||||
|
||||
trait Abc {
|
||||
fn send_message_id() {}
|
||||
}
|
||||
|
||||
impl<T> Abc for T {}
|
||||
|
||||
#[test]
|
||||
fn test_macro_still_works() {
|
||||
let cls = class!(NSObject);
|
||||
let _obj: Id<Object, Owned> = unsafe { msg_send_id![cls, new] };
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "objc2-proc-macros")]
|
||||
fn hash_idents_different() {
|
||||
assert_ne!(__hash_idents!(abc), __hash_idents!(def));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "objc2-proc-macros")]
|
||||
fn hash_idents_same_no_equal() {
|
||||
assert_ne!(__hash_idents!(abc), __hash_idents!(abc));
|
||||
assert_ne!(__hash_idents!(abc def ghi), __hash_idents!(abc def ghi));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "objc2-proc-macros")]
|
||||
fn hash_idents_exact_same_ident() {
|
||||
macro_rules! x {
|
||||
($x:ident) => {
|
||||
(__hash_idents!($x), __hash_idents!($x))
|
||||
};
|
||||
}
|
||||
let (ident1, ident2) = x!(abc);
|
||||
// This is a limitation of `__hash_idents`, ideally we'd like these
|
||||
// to be different!
|
||||
assert_eq!(ident1, ident2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
not(all(feature = "apple", target_os = "macos", target_arch = "x86")),
|
||||
ignore = "Only relevant on macOS 32-bit"
|
||||
)]
|
||||
fn ensure_size_of_module_info() {
|
||||
assert_eq!(core::mem::size_of::<ModuleInfo>(), 16);
|
||||
}
|
||||
}
|
||||
72
third-party/vendor/objc2/src/cache.rs
vendored
Normal file
72
third-party/vendor/objc2/src/cache.rs
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
use core::ptr;
|
||||
use core::sync::atomic::{AtomicPtr, Ordering};
|
||||
|
||||
use crate::ffi;
|
||||
use crate::runtime::{Class, Sel};
|
||||
|
||||
/// Allows storing a [`Sel`] in a static and lazily loading it.
|
||||
#[doc(hidden)]
|
||||
pub struct CachedSel {
|
||||
ptr: AtomicPtr<ffi::objc_selector>,
|
||||
}
|
||||
|
||||
impl CachedSel {
|
||||
/// Constructs a new [`CachedSel`].
|
||||
pub const fn new() -> CachedSel {
|
||||
CachedSel {
|
||||
ptr: AtomicPtr::new(ptr::null_mut()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the cached selector. If no selector is yet cached, registers
|
||||
/// one with the given name and stores it.
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn get(&self, name: &str) -> Sel {
|
||||
// `Relaxed` should be fine since `sel_registerName` is thread-safe.
|
||||
let ptr = self.ptr.load(Ordering::Relaxed);
|
||||
unsafe { Sel::from_ptr(ptr) }.unwrap_or_else(|| {
|
||||
// The panic inside `Sel::register_unchecked` is unfortunate, but
|
||||
// strict correctness is more important than speed
|
||||
|
||||
// SAFETY: Input is a non-null, NUL-terminated C-string pointer.
|
||||
//
|
||||
// We know this, because we construct it in `sel!` ourselves
|
||||
let sel = unsafe { Sel::register_unchecked(name.as_ptr().cast()) };
|
||||
self.ptr
|
||||
.store(sel.as_ptr() as *mut ffi::objc_selector, Ordering::Relaxed);
|
||||
sel
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows storing a [`Class`] reference in a static and lazily loading it.
|
||||
#[doc(hidden)]
|
||||
pub struct CachedClass {
|
||||
ptr: AtomicPtr<Class>,
|
||||
}
|
||||
|
||||
impl CachedClass {
|
||||
/// Constructs a new [`CachedClass`].
|
||||
pub const fn new() -> CachedClass {
|
||||
CachedClass {
|
||||
ptr: AtomicPtr::new(ptr::null_mut()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the cached class. If no class is yet cached, gets one with
|
||||
/// the given name and stores it.
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn get(&self, name: &str) -> Option<&'static Class> {
|
||||
// `Relaxed` should be fine since `objc_getClass` is thread-safe.
|
||||
let ptr = self.ptr.load(Ordering::Relaxed);
|
||||
if let Some(cls) = unsafe { ptr.as_ref() } {
|
||||
Some(cls)
|
||||
} else {
|
||||
let ptr: *const Class = unsafe { ffi::objc_getClass(name.as_ptr().cast()) }.cast();
|
||||
self.ptr.store(ptr as *mut Class, Ordering::Relaxed);
|
||||
unsafe { ptr.as_ref() }
|
||||
}
|
||||
}
|
||||
}
|
||||
94
third-party/vendor/objc2/src/class_type.rs
vendored
Normal file
94
third-party/vendor/objc2/src/class_type.rs
vendored
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
use crate::runtime::Class;
|
||||
use crate::Message;
|
||||
|
||||
/// Marks types that represent specific classes.
|
||||
///
|
||||
/// Usually it is enough to generically know that a type is messageable, e.g.
|
||||
/// [`rc::Id`][crate::rc::Id] works with any type that implements the
|
||||
/// [`Message`] trait. But often, you have an object that you know represents
|
||||
/// a specific Objective-C class - this trait allows you to communicate that
|
||||
/// to the rest of the type-system.
|
||||
///
|
||||
/// This is implemented automatically by the
|
||||
/// [`declare_class!`][crate::declare_class] and
|
||||
/// [`extern_class!`][crate::extern_class] macros.
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The class returned by [`Self::class`] must be a subclass of the class that
|
||||
/// [`Self::Super`] represents, and `as_super`/`as_super_mut` must be
|
||||
/// implemented correctly. Finally [`Self::NAME`] must be correct.
|
||||
///
|
||||
/// In pseudocode:
|
||||
/// ```ignore
|
||||
/// Self::class().superclass() == <Self::Super as ClassType>::class()
|
||||
/// Self::class().name() == Self::NAME
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Use the trait to access the [`Class`] of different objects.
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
/// use objc2::ClassType;
|
||||
/// use objc2::foundation::NSObject;
|
||||
/// // Get a class object representing `NSObject`
|
||||
/// let cls = <NSObject as ClassType>::class(); // Or just `NSObject::class()`
|
||||
/// ```
|
||||
///
|
||||
/// Use the [`extern_class!`][crate::extern_class] macro to implement this
|
||||
/// trait for a type.
|
||||
///
|
||||
/// ```ignore
|
||||
/// use objc2::{extern_class, ClassType};
|
||||
///
|
||||
/// extern_class!(
|
||||
/// struct MyClass;
|
||||
///
|
||||
/// unsafe impl ClassType for MyClass {
|
||||
/// type Super = NSObject;
|
||||
/// }
|
||||
/// );
|
||||
///
|
||||
/// let cls = MyClass::class();
|
||||
/// ```
|
||||
pub unsafe trait ClassType: Message {
|
||||
/// The superclass of this class.
|
||||
///
|
||||
/// If you have implemented [`Deref`] for your type, it is highly
|
||||
/// recommended that this is equal to [`Deref::Target`].
|
||||
///
|
||||
/// This may be [`runtime::Object`] if the class is a root class.
|
||||
///
|
||||
/// [`Deref`]: std::ops::Deref
|
||||
/// [`Deref::Target`]: std::ops::Deref::Target
|
||||
/// [`runtime::Object`]: crate::runtime::Object
|
||||
type Super: Message;
|
||||
|
||||
/// The name of the Objective-C class that this type represents.
|
||||
const NAME: &'static str;
|
||||
|
||||
/// Get a reference to the Objective-C class that this type represents.
|
||||
///
|
||||
/// May register the class with the runtime if it wasn't already.
|
||||
///
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This may panic if something went wrong with getting or declaring the
|
||||
/// class, e.g. if the program is not properly linked to the framework
|
||||
/// that defines the class.
|
||||
fn class() -> &'static Class;
|
||||
|
||||
/// Get an immutable reference to the superclass.
|
||||
// Note: It'd be safe to provide a default impl using transmute here if
|
||||
// we wanted to!
|
||||
fn as_super(&self) -> &Self::Super;
|
||||
|
||||
/// Get a mutable reference to the superclass.
|
||||
fn as_super_mut(&mut self) -> &mut Self::Super;
|
||||
}
|
||||
687
third-party/vendor/objc2/src/declare.rs
vendored
Normal file
687
third-party/vendor/objc2/src/declare.rs
vendored
Normal file
|
|
@ -0,0 +1,687 @@
|
|||
//! Functionality for dynamically declaring Objective-C classes.
|
||||
//!
|
||||
//! Classes can be declared using the [`ClassBuilder`] struct. Instance
|
||||
//! variables and methods can then be added before the class is ultimately
|
||||
//! registered.
|
||||
//!
|
||||
//! **Note**: You likely don't need the dynamicism that this module provides!
|
||||
//! Consider using the [`declare_class!`][crate::declare_class] macro instead.
|
||||
//!
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! The following example demonstrates declaring a class named `MyNumber` that
|
||||
//! has one ivar, a `u32` named `_number` and a few methods for constructor
|
||||
//! methods and methods for interfacing with the number.
|
||||
//!
|
||||
//! ```
|
||||
//! use objc2::declare::ClassBuilder;
|
||||
//! use objc2::foundation::NSObject;
|
||||
//! use objc2::rc::{Id, Owned};
|
||||
//! use objc2::runtime::{Class, Object, Sel};
|
||||
//! use objc2::{class, sel, msg_send, msg_send_id, ClassType};
|
||||
//! # #[cfg(feature = "gnustep-1-7")]
|
||||
//! # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
//!
|
||||
//! fn register_class() -> &'static Class {
|
||||
//! // Inherit from NSObject
|
||||
//! let mut builder = ClassBuilder::new("MyNumber", NSObject::class())
|
||||
//! .expect("a class with the name MyNumber likely already exists");
|
||||
//!
|
||||
//! // Add an instance variable of type `u32`
|
||||
//! builder.add_ivar::<u32>("_number");
|
||||
//!
|
||||
//! // Add an Objective-C method for initializing an instance with a number
|
||||
//! unsafe extern "C" fn init_with_number(
|
||||
//! this: &mut Object,
|
||||
//! _cmd: Sel,
|
||||
//! number: u32,
|
||||
//! ) -> Option<&mut Object> {
|
||||
//! let this: Option<&mut Object> = msg_send![super(this, NSObject::class()), init];
|
||||
//! this.map(|this| {
|
||||
//! // SAFETY: The ivar is added with the same type above
|
||||
//! this.set_ivar::<u32>("_number", number);
|
||||
//! this
|
||||
//! })
|
||||
//! }
|
||||
//! unsafe {
|
||||
//! builder.add_method(
|
||||
//! sel!(initWithNumber:),
|
||||
//! init_with_number as unsafe extern "C" fn(_, _, _) -> _,
|
||||
//! );
|
||||
//! }
|
||||
//!
|
||||
//! // Add convenience method for getting a new instance with the number
|
||||
//! extern "C" fn with_number(
|
||||
//! cls: &Class,
|
||||
//! _cmd: Sel,
|
||||
//! number: u32,
|
||||
//! ) -> *mut Object {
|
||||
//! let obj: Option<Id<Object, Owned>> = unsafe {
|
||||
//! msg_send_id![
|
||||
//! msg_send_id![cls, alloc],
|
||||
//! initWithNumber: number,
|
||||
//! ]
|
||||
//! };
|
||||
//! obj.map(|obj| obj.autorelease_return()).unwrap_or(std::ptr::null_mut())
|
||||
//! }
|
||||
//! unsafe {
|
||||
//! builder.add_class_method(
|
||||
//! sel!(withNumber:),
|
||||
//! with_number as extern "C" fn(_, _, _) -> _,
|
||||
//! );
|
||||
//! }
|
||||
//!
|
||||
//! // Add an Objective-C method for setting the number
|
||||
//! extern "C" fn my_number_set(this: &mut Object, _cmd: Sel, number: u32) {
|
||||
//! // SAFETY: The ivar is added with the same type above
|
||||
//! unsafe { this.set_ivar::<u32>("_number", number) }
|
||||
//! }
|
||||
//! unsafe {
|
||||
//! builder.add_method(sel!(setNumber:), my_number_set as extern "C" fn(_, _, _));
|
||||
//! }
|
||||
//!
|
||||
//! // Add an Objective-C method for getting the number
|
||||
//! extern "C" fn my_number_get(this: &Object, _cmd: Sel) -> u32 {
|
||||
//! // SAFETY: The ivar is added with the same type above
|
||||
//! unsafe { *this.ivar::<u32>("_number") }
|
||||
//! }
|
||||
//! unsafe {
|
||||
//! builder.add_method(sel!(number), my_number_get as extern "C" fn(_, _) -> _);
|
||||
//! }
|
||||
//!
|
||||
//! builder.register()
|
||||
//! }
|
||||
//!
|
||||
//! // Usage
|
||||
//!
|
||||
//! // Note: you should only do class registration once! This can be ensure
|
||||
//! // with `std::sync::Once` or the `once_cell` crate.
|
||||
//! let cls = register_class();
|
||||
//!
|
||||
//! let obj: Id<Object, Owned> = unsafe {
|
||||
//! msg_send_id![cls, withNumber: 42u32]
|
||||
//! };
|
||||
//!
|
||||
//! let n: u32 = unsafe { msg_send![&obj, number] };
|
||||
//! assert_eq!(n, 42);
|
||||
//!
|
||||
//! let _: () = unsafe { msg_send![&obj, setNumber: 12u32] };
|
||||
//! let n: u32 = unsafe { msg_send![&obj, number] };
|
||||
//! assert_eq!(n, 12);
|
||||
//! ```
|
||||
|
||||
mod ivar;
|
||||
mod ivar_drop;
|
||||
mod ivar_forwarding_impls;
|
||||
|
||||
use alloc::format;
|
||||
use alloc::string::ToString;
|
||||
use core::mem;
|
||||
use core::mem::ManuallyDrop;
|
||||
use core::ptr;
|
||||
use core::ptr::NonNull;
|
||||
use std::ffi::CString;
|
||||
|
||||
use crate::encode::{Encode, EncodeArguments, Encoding, RefEncode};
|
||||
use crate::ffi;
|
||||
use crate::runtime::{Bool, Class, Imp, Object, Protocol, Sel};
|
||||
use crate::sel;
|
||||
use crate::Message;
|
||||
|
||||
pub use ivar::{InnerIvarType, Ivar, IvarType};
|
||||
pub use ivar_drop::IvarDrop;
|
||||
|
||||
pub(crate) mod private {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
/// Types that can be used as the implementation of an Objective-C method.
|
||||
///
|
||||
/// This is a sealed trait that is implemented for a lot of `extern "C"`
|
||||
/// function pointer types.
|
||||
pub trait MethodImplementation: private::Sealed {
|
||||
/// The callee type of the method.
|
||||
type Callee: RefEncode + ?Sized;
|
||||
/// The return type of the method.
|
||||
type Ret: Encode;
|
||||
/// The argument types of the method.
|
||||
type Args: EncodeArguments;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn __imp(self) -> Imp;
|
||||
}
|
||||
|
||||
macro_rules! method_decl_impl {
|
||||
(@<$($l:lifetime),*> T, $r:ident, $f:ty, $($t:ident),*) => {
|
||||
impl<$($l,)* T, $r, $($t),*> private::Sealed for $f
|
||||
where
|
||||
T: Message + ?Sized,
|
||||
$r: Encode,
|
||||
$($t: Encode,)*
|
||||
{}
|
||||
|
||||
impl<$($l,)* T, $r, $($t),*> MethodImplementation for $f
|
||||
where
|
||||
T: Message + ?Sized,
|
||||
$r: Encode,
|
||||
$($t: Encode,)*
|
||||
{
|
||||
type Callee = T;
|
||||
type Ret = $r;
|
||||
type Args = ($($t,)*);
|
||||
|
||||
fn __imp(self) -> Imp {
|
||||
unsafe { mem::transmute(self) }
|
||||
}
|
||||
}
|
||||
};
|
||||
(@<$($l:lifetime),*> Class, $r:ident, $f:ty, $($t:ident),*) => {
|
||||
impl<$($l,)* $r, $($t),*> private::Sealed for $f
|
||||
where
|
||||
$r: Encode,
|
||||
$($t: Encode,)*
|
||||
{}
|
||||
|
||||
impl<$($l,)* $r, $($t),*> MethodImplementation for $f
|
||||
where
|
||||
$r: Encode,
|
||||
$($t: Encode,)*
|
||||
{
|
||||
type Callee = Class;
|
||||
type Ret = $r;
|
||||
type Args = ($($t,)*);
|
||||
|
||||
fn __imp(self) -> Imp {
|
||||
unsafe { mem::transmute(self) }
|
||||
}
|
||||
}
|
||||
};
|
||||
(# $abi:literal; $($t:ident),*) => {
|
||||
method_decl_impl!(@<'a> T, R, extern $abi fn(&'a T, Sel $(, $t)*) -> R, $($t),*);
|
||||
method_decl_impl!(@<'a> T, R, extern $abi fn(&'a mut T, Sel $(, $t)*) -> R, $($t),*);
|
||||
method_decl_impl!(@<> T, R, unsafe extern $abi fn(*const T, Sel $(, $t)*) -> R, $($t),*);
|
||||
method_decl_impl!(@<> T, R, unsafe extern $abi fn(*mut T, Sel $(, $t)*) -> R, $($t),*);
|
||||
method_decl_impl!(@<'a> T, R, unsafe extern $abi fn(&'a T, Sel $(, $t)*) -> R, $($t),*);
|
||||
method_decl_impl!(@<'a> T, R, unsafe extern $abi fn(&'a mut T, Sel $(, $t)*) -> R, $($t),*);
|
||||
|
||||
method_decl_impl!(@<'a> Class, R, extern $abi fn(&'a Class, Sel $(, $t)*) -> R, $($t),*);
|
||||
method_decl_impl!(@<> Class, R, unsafe extern $abi fn(*const Class, Sel $(, $t)*) -> R, $($t),*);
|
||||
method_decl_impl!(@<'a> Class, R, unsafe extern $abi fn(&'a Class, Sel $(, $t)*) -> R, $($t),*);
|
||||
};
|
||||
($($t:ident),*) => {
|
||||
method_decl_impl!(# "C"; $($t),*);
|
||||
#[cfg(feature = "unstable-c-unwind")]
|
||||
method_decl_impl!(# "C-unwind"; $($t),*);
|
||||
};
|
||||
}
|
||||
|
||||
method_decl_impl!();
|
||||
method_decl_impl!(A);
|
||||
method_decl_impl!(A, B);
|
||||
method_decl_impl!(A, B, C);
|
||||
method_decl_impl!(A, B, C, D);
|
||||
method_decl_impl!(A, B, C, D, E);
|
||||
method_decl_impl!(A, B, C, D, E, F);
|
||||
method_decl_impl!(A, B, C, D, E, F, G);
|
||||
method_decl_impl!(A, B, C, D, E, F, G, H);
|
||||
method_decl_impl!(A, B, C, D, E, F, G, H, I);
|
||||
method_decl_impl!(A, B, C, D, E, F, G, H, I, J);
|
||||
method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K);
|
||||
method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K, L);
|
||||
|
||||
fn count_args(sel: Sel) -> usize {
|
||||
sel.name().chars().filter(|&c| c == ':').count()
|
||||
}
|
||||
|
||||
fn method_type_encoding(ret: &Encoding, args: &[Encoding]) -> CString {
|
||||
// First two arguments are always self and the selector
|
||||
let mut types = format!("{}{}{}", ret, <*mut Object>::ENCODING, Sel::ENCODING);
|
||||
for enc in args {
|
||||
use core::fmt::Write;
|
||||
write!(&mut types, "{}", enc).unwrap();
|
||||
}
|
||||
CString::new(types).unwrap()
|
||||
}
|
||||
|
||||
fn log2_align_of<T>() -> u8 {
|
||||
let align = mem::align_of::<T>();
|
||||
// Alignments are required to be powers of 2
|
||||
debug_assert!(align.count_ones() == 1);
|
||||
// log2 of a power of 2 is the number of trailing zeros
|
||||
align.trailing_zeros() as u8
|
||||
}
|
||||
|
||||
/// A type for declaring a new class and adding new methods and ivars to it
|
||||
/// before registering it.
|
||||
#[derive(Debug)]
|
||||
pub struct ClassBuilder {
|
||||
// Note: Don't ever construct a &mut Class, since it is possible to get
|
||||
// this pointer using `Class::classes`!
|
||||
cls: NonNull<Class>,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[deprecated = "Use `ClassBuilder` instead."]
|
||||
pub type ClassDecl = ClassBuilder;
|
||||
|
||||
// SAFETY: The stuff that touch global state does so using locks internally.
|
||||
//
|
||||
// Modifying the class itself can only be done through `&mut`, so Sync is
|
||||
// safe (e.g. we can't accidentally call `add_ivar` at the same time from two
|
||||
// different threads).
|
||||
//
|
||||
// (Though actually, that would be safe since the entire runtime is locked
|
||||
// when doing so...).
|
||||
//
|
||||
// Finally, there are no requirements that the class must be registered on the
|
||||
// same thread that allocated it (so Send is safe).
|
||||
unsafe impl Send for ClassBuilder {}
|
||||
unsafe impl Sync for ClassBuilder {}
|
||||
|
||||
impl ClassBuilder {
|
||||
fn as_mut_ptr(&mut self) -> *mut ffi::objc_class {
|
||||
self.cls.as_ptr().cast()
|
||||
}
|
||||
|
||||
fn with_superclass(name: &str, superclass: Option<&Class>) -> Option<Self> {
|
||||
let name = CString::new(name).unwrap();
|
||||
let super_ptr = superclass.map_or(ptr::null(), |c| c).cast();
|
||||
let cls = unsafe { ffi::objc_allocateClassPair(super_ptr, name.as_ptr(), 0) };
|
||||
NonNull::new(cls.cast()).map(|cls| Self { cls })
|
||||
}
|
||||
|
||||
/// Constructs a [`ClassBuilder`] with the given name and superclass.
|
||||
///
|
||||
/// Returns [`None`] if the class couldn't be allocated, or a class with
|
||||
/// that name already exist.
|
||||
pub fn new(name: &str, superclass: &Class) -> Option<Self> {
|
||||
Self::with_superclass(name, Some(superclass))
|
||||
}
|
||||
|
||||
/// Constructs a [`ClassBuilder`] declaring a new root class with the
|
||||
/// given name.
|
||||
///
|
||||
/// Returns [`None`] if the class couldn't be allocated.
|
||||
///
|
||||
/// An implementation for `+initialize` must also be given; the runtime
|
||||
/// calls this method for all classes, so it must be defined on root
|
||||
/// classes.
|
||||
///
|
||||
/// Note that implementing a root class is not a simple endeavor!
|
||||
/// For example, your class probably cannot be passed to Cocoa code unless
|
||||
/// the entire `NSObject` protocol is implemented.
|
||||
/// Functionality it expects, like implementations of `-retain` and
|
||||
/// `-release` used by ARC, will not be present otherwise.
|
||||
pub fn root<F>(name: &str, intitialize_fn: F) -> Option<Self>
|
||||
where
|
||||
F: MethodImplementation<Callee = Class, Args = (), Ret = ()>,
|
||||
{
|
||||
Self::with_superclass(name, None).map(|mut this| {
|
||||
unsafe { this.add_class_method(sel!(initialize), intitialize_fn) };
|
||||
this
|
||||
})
|
||||
}
|
||||
|
||||
/// Adds a method with the given name and implementation.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the method wasn't sucessfully added or if the selector and
|
||||
/// function take different numbers of arguments.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that the types match those that are expected
|
||||
/// when the method is invoked from Objective-C.
|
||||
pub unsafe fn add_method<T, F>(&mut self, sel: Sel, func: F)
|
||||
where
|
||||
T: Message + ?Sized,
|
||||
F: MethodImplementation<Callee = T>,
|
||||
{
|
||||
let encs = F::Args::ENCODINGS;
|
||||
let sel_args = count_args(sel);
|
||||
assert_eq!(
|
||||
sel_args,
|
||||
encs.len(),
|
||||
"Selector {:?} accepts {} arguments, but function accepts {}",
|
||||
sel,
|
||||
sel_args,
|
||||
encs.len(),
|
||||
);
|
||||
|
||||
let types = method_type_encoding(&F::Ret::ENCODING, encs);
|
||||
let success = Bool::from_raw(unsafe {
|
||||
ffi::class_addMethod(
|
||||
self.as_mut_ptr(),
|
||||
sel.as_ptr(),
|
||||
Some(func.__imp()),
|
||||
types.as_ptr(),
|
||||
)
|
||||
});
|
||||
assert!(success.as_bool(), "Failed to add method {:?}", sel);
|
||||
}
|
||||
|
||||
fn metaclass_mut(&mut self) -> *mut ffi::objc_class {
|
||||
unsafe { ffi::object_getClass(self.as_mut_ptr().cast()) as *mut ffi::objc_class }
|
||||
}
|
||||
|
||||
/// Adds a class method with the given name and implementation.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the method wasn't sucessfully added or if the selector and
|
||||
/// function take different numbers of arguments.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that the types match those that are expected
|
||||
/// when the method is invoked from Objective-C.
|
||||
pub unsafe fn add_class_method<F>(&mut self, sel: Sel, func: F)
|
||||
where
|
||||
F: MethodImplementation<Callee = Class>,
|
||||
{
|
||||
let encs = F::Args::ENCODINGS;
|
||||
let sel_args = count_args(sel);
|
||||
assert_eq!(
|
||||
sel_args,
|
||||
encs.len(),
|
||||
"Selector {:?} accepts {} arguments, but function accepts {}",
|
||||
sel,
|
||||
sel_args,
|
||||
encs.len(),
|
||||
);
|
||||
|
||||
let types = method_type_encoding(&F::Ret::ENCODING, encs);
|
||||
let success = Bool::from_raw(unsafe {
|
||||
ffi::class_addMethod(
|
||||
self.metaclass_mut(),
|
||||
sel.as_ptr(),
|
||||
Some(func.__imp()),
|
||||
types.as_ptr(),
|
||||
)
|
||||
});
|
||||
assert!(success.as_bool(), "Failed to add class method {:?}", sel);
|
||||
}
|
||||
|
||||
/// Adds an ivar with type `T` and the provided name.
|
||||
///
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the ivar wasn't successfully added for some reason - this usually
|
||||
/// happens if there already was an ivar with that name.
|
||||
pub fn add_ivar<T: Encode>(&mut self, name: &str) {
|
||||
// SAFETY: The encoding is correct
|
||||
unsafe { self.add_ivar_inner::<T>(name, &T::ENCODING) }
|
||||
}
|
||||
|
||||
unsafe fn add_ivar_inner<T>(&mut self, name: &str, encoding: &Encoding) {
|
||||
let c_name = CString::new(name).unwrap();
|
||||
let encoding = CString::new(encoding.to_string()).unwrap();
|
||||
let size = mem::size_of::<T>();
|
||||
let align = log2_align_of::<T>();
|
||||
let success = Bool::from_raw(unsafe {
|
||||
ffi::class_addIvar(
|
||||
self.as_mut_ptr(),
|
||||
c_name.as_ptr(),
|
||||
size,
|
||||
align,
|
||||
encoding.as_ptr(),
|
||||
)
|
||||
});
|
||||
assert!(success.as_bool(), "Failed to add ivar {}", name);
|
||||
}
|
||||
|
||||
/// Adds an instance variable from an [`IvarType`].
|
||||
///
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Same as [`ClassBuilder::add_ivar`].
|
||||
pub fn add_static_ivar<T: IvarType>(&mut self) {
|
||||
// SAFETY: The encoding is correct
|
||||
unsafe {
|
||||
self.add_ivar_inner::<<T::Type as InnerIvarType>::__Inner>(
|
||||
T::NAME,
|
||||
&T::Type::__ENCODING,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds the given protocol to self.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the protocol wasn't successfully added.
|
||||
pub fn add_protocol(&mut self, proto: &Protocol) {
|
||||
let success = unsafe { ffi::class_addProtocol(self.as_mut_ptr(), proto.as_ptr()) };
|
||||
let success = Bool::from_raw(success).as_bool();
|
||||
assert!(success, "Failed to add protocol {:?}", proto);
|
||||
}
|
||||
|
||||
// fn add_property(&self, name: &str, attributes: &[ffi::objc_property_attribute_t]);
|
||||
|
||||
/// Registers the [`ClassBuilder`], consuming it, and returns a reference
|
||||
/// to the newly registered [`Class`].
|
||||
pub fn register(self) -> &'static Class {
|
||||
// Forget self, otherwise the class will be disposed in drop
|
||||
let mut this = ManuallyDrop::new(self);
|
||||
unsafe { ffi::objc_registerClassPair(this.as_mut_ptr()) };
|
||||
unsafe { this.cls.as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ClassBuilder {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::objc_disposeClassPair(self.as_mut_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// A type for declaring a new protocol and adding new methods to it
|
||||
/// before registering it.
|
||||
#[derive(Debug)]
|
||||
pub struct ProtocolBuilder {
|
||||
proto: NonNull<Protocol>,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[deprecated = "Use `ProtocolBuilder` instead."]
|
||||
pub type ProtocolDecl = ProtocolBuilder;
|
||||
|
||||
// SAFETY: Similar to ClassBuilder
|
||||
unsafe impl Send for ProtocolBuilder {}
|
||||
unsafe impl Sync for ProtocolBuilder {}
|
||||
|
||||
impl ProtocolBuilder {
|
||||
fn as_mut_ptr(&mut self) -> *mut ffi::objc_protocol {
|
||||
self.proto.as_ptr().cast()
|
||||
}
|
||||
|
||||
/// Constructs a [`ProtocolBuilder`] with the given name.
|
||||
///
|
||||
/// Returns [`None`] if the protocol couldn't be allocated.
|
||||
pub fn new(name: &str) -> Option<Self> {
|
||||
let c_name = CString::new(name).unwrap();
|
||||
let proto = unsafe { ffi::objc_allocateProtocol(c_name.as_ptr()) };
|
||||
NonNull::new(proto.cast()).map(|proto| Self { proto })
|
||||
}
|
||||
|
||||
fn add_method_description_common<Args, Ret>(
|
||||
&mut self,
|
||||
sel: Sel,
|
||||
is_required: bool,
|
||||
is_instance_method: bool,
|
||||
) where
|
||||
Args: EncodeArguments,
|
||||
Ret: Encode,
|
||||
{
|
||||
let encs = Args::ENCODINGS;
|
||||
let sel_args = count_args(sel);
|
||||
assert_eq!(
|
||||
sel_args,
|
||||
encs.len(),
|
||||
"Selector {:?} accepts {} arguments, but function accepts {}",
|
||||
sel,
|
||||
sel_args,
|
||||
encs.len(),
|
||||
);
|
||||
let types = method_type_encoding(&Ret::ENCODING, encs);
|
||||
unsafe {
|
||||
ffi::protocol_addMethodDescription(
|
||||
self.as_mut_ptr(),
|
||||
sel.as_ptr(),
|
||||
types.as_ptr(),
|
||||
Bool::new(is_required).as_raw(),
|
||||
Bool::new(is_instance_method).as_raw(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds an instance method declaration with a given description.
|
||||
pub fn add_method_description<Args, Ret>(&mut self, sel: Sel, is_required: bool)
|
||||
where
|
||||
Args: EncodeArguments,
|
||||
Ret: Encode,
|
||||
{
|
||||
self.add_method_description_common::<Args, Ret>(sel, is_required, true)
|
||||
}
|
||||
|
||||
/// Adds a class method declaration with a given description.
|
||||
pub fn add_class_method_description<Args, Ret>(&mut self, sel: Sel, is_required: bool)
|
||||
where
|
||||
Args: EncodeArguments,
|
||||
Ret: Encode,
|
||||
{
|
||||
self.add_method_description_common::<Args, Ret>(sel, is_required, false)
|
||||
}
|
||||
|
||||
/// Adds a requirement on another protocol.
|
||||
pub fn add_protocol(&mut self, proto: &Protocol) {
|
||||
unsafe {
|
||||
ffi::protocol_addProtocol(self.as_mut_ptr(), proto.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers the [`ProtocolBuilder`], consuming it and returning a reference
|
||||
/// to the newly registered [`Protocol`].
|
||||
pub fn register(mut self) -> &'static Protocol {
|
||||
unsafe {
|
||||
ffi::objc_registerProtocol(self.as_mut_ptr());
|
||||
self.proto.as_ref()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::msg_send;
|
||||
use crate::test_utils;
|
||||
|
||||
#[test]
|
||||
fn test_classbuilder_duplicate() {
|
||||
let cls = test_utils::custom_class();
|
||||
let builder = ClassBuilder::new("TestClassBuilderDuplicate", cls).unwrap();
|
||||
let _ = builder.register();
|
||||
|
||||
assert!(ClassBuilder::new("TestClassBuilderDuplicate", cls).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "Failed to add ivar xyz"]
|
||||
fn duplicate_ivar() {
|
||||
let cls = test_utils::custom_class();
|
||||
let builder = ClassBuilder::new("TestClassBuilderDuplicateIvar", cls).unwrap();
|
||||
// ManuallyDrop to work around GNUStep issue
|
||||
let mut builder = ManuallyDrop::new(builder);
|
||||
|
||||
builder.add_ivar::<i32>("xyz");
|
||||
// Should panic:
|
||||
builder.add_ivar::<i32>("xyz");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "Failed to add method xyz"]
|
||||
fn duplicate_method() {
|
||||
let cls = test_utils::custom_class();
|
||||
let builder = ClassBuilder::new("TestClassBuilderDuplicateMethod", cls).unwrap();
|
||||
let mut builder = ManuallyDrop::new(builder);
|
||||
|
||||
extern "C" fn xyz(_this: &Object, _cmd: Sel) {}
|
||||
|
||||
unsafe {
|
||||
builder.add_method(sel!(xyz), xyz as extern "C" fn(_, _));
|
||||
// Should panic:
|
||||
builder.add_method(sel!(xyz), xyz as extern "C" fn(_, _));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "Failed to add protocol NSObject"]
|
||||
fn duplicate_protocol() {
|
||||
let cls = test_utils::custom_class();
|
||||
let builder = ClassBuilder::new("TestClassBuilderDuplicateProtocol", cls).unwrap();
|
||||
let mut builder = ManuallyDrop::new(builder);
|
||||
|
||||
let protocol = Protocol::get("NSObject").unwrap();
|
||||
|
||||
builder.add_protocol(protocol);
|
||||
// Should panic:
|
||||
builder.add_protocol(protocol);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
feature = "gnustep-1-7",
|
||||
ignore = "Dropping ClassBuilder has weird threading side effects on GNUStep"
|
||||
)]
|
||||
fn test_classbuilder_drop() {
|
||||
let cls = test_utils::custom_class();
|
||||
let builder = ClassBuilder::new("TestClassBuilderDrop", cls).unwrap();
|
||||
drop(builder);
|
||||
// After we dropped the class, we can create a new one with the same name:
|
||||
let _builder = ClassBuilder::new("TestClassBuilderDrop", cls).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_class() {
|
||||
// Registering the custom class is in test_utils
|
||||
let mut obj = test_utils::custom_object();
|
||||
let _: () = unsafe { msg_send![&mut obj, setFoo: 13u32] };
|
||||
let result: u32 = unsafe { msg_send![&obj, foo] };
|
||||
assert_eq!(result, 13);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "malloc")]
|
||||
fn test_in_all_classes() {
|
||||
fn is_present(cls: *const Class) -> bool {
|
||||
// Check whether the class is present in Class::classes()
|
||||
Class::classes()
|
||||
.into_iter()
|
||||
.find(|&item| ptr::eq(cls, *item))
|
||||
.is_some()
|
||||
}
|
||||
|
||||
let superclass = test_utils::custom_class();
|
||||
let builder = ClassBuilder::new("TestFetchWhileCreatingClass", superclass).unwrap();
|
||||
|
||||
if cfg!(all(feature = "apple", target_arch = "x86_64")) {
|
||||
// It is IMO a bug that it is present here!
|
||||
assert!(is_present(builder.cls.as_ptr()));
|
||||
} else {
|
||||
assert!(!is_present(builder.cls.as_ptr()));
|
||||
}
|
||||
|
||||
let cls = builder.register();
|
||||
assert!(is_present(cls));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_class_method() {
|
||||
let cls = test_utils::custom_class();
|
||||
let result: u32 = unsafe { msg_send![cls, classFoo] };
|
||||
assert_eq!(result, 7);
|
||||
}
|
||||
}
|
||||
422
third-party/vendor/objc2/src/declare/ivar.rs
vendored
Normal file
422
third-party/vendor/objc2/src/declare/ivar.rs
vendored
Normal file
|
|
@ -0,0 +1,422 @@
|
|||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::ptr::{self, NonNull};
|
||||
|
||||
use crate::encode::{EncodeConvert, Encoding};
|
||||
use crate::runtime::{ivar_offset, Object};
|
||||
|
||||
pub(crate) mod private {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
/// Types that may be used in ivars.
|
||||
///
|
||||
/// This may be either:
|
||||
/// - [`bool`].
|
||||
/// - [`IvarDrop<T>`][super::IvarDrop].
|
||||
/// - Something that implements [`Encode`][crate::Encode].
|
||||
///
|
||||
/// 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!
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// You cannot rely on any safety guarantees from this.
|
||||
pub unsafe trait InnerIvarType: private::Sealed {
|
||||
#[doc(hidden)]
|
||||
const __ENCODING: Encoding;
|
||||
|
||||
// SAFETY: It must be safe to transmute from `__Inner` to `Output`.
|
||||
#[doc(hidden)]
|
||||
type __Inner;
|
||||
|
||||
/// The type that an `Ivar` containing this will dereference to.
|
||||
///
|
||||
/// E.g. `Ivar<IvarDrop<Box<u8>>>` will deref to `Box<u8>`.
|
||||
type Output;
|
||||
|
||||
// SAFETY: The __Inner type must be safe to drop even if zero-initialized.
|
||||
#[doc(hidden)]
|
||||
const __MAY_DROP: bool;
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe fn __to_ref(inner: &Self::__Inner) -> &Self::Output;
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe fn __to_mut(inner: &mut Self::__Inner) -> &mut Self::Output;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn __to_ptr(inner: NonNull<Self::__Inner>) -> NonNull<Self::Output>;
|
||||
}
|
||||
|
||||
impl<T: EncodeConvert> private::Sealed for T {}
|
||||
unsafe impl<T: EncodeConvert> InnerIvarType for T {
|
||||
const __ENCODING: Encoding = <Self as EncodeConvert>::__ENCODING;
|
||||
type __Inner = Self;
|
||||
type Output = Self;
|
||||
// Note: We explicitly tell `Ivar` that it shouldn't do anything to drop,
|
||||
// since if the object was deallocated before an `init` method was called,
|
||||
// the ivar would not have been initialized properly!
|
||||
//
|
||||
// For example in the case of `NonNull<u8>`, it would be zero-initialized
|
||||
// which is an invalid state for that.
|
||||
const __MAY_DROP: bool = false;
|
||||
|
||||
#[inline]
|
||||
unsafe fn __to_ref(inner: &Self::__Inner) -> &Self::Output {
|
||||
inner
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn __to_mut(inner: &mut Self::__Inner) -> &mut Self::Output {
|
||||
inner
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn __to_ptr(inner: NonNull<Self::__Inner>) -> NonNull<Self::Output> {
|
||||
inner
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper trait for defining instance variables.
|
||||
///
|
||||
/// This should be implemented for an empty marker type, which can then be
|
||||
/// used within [`Ivar`] to refer to the instance variable.
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Really, [`Ivar`] should be marked as `unsafe`, but since we can't do that
|
||||
/// we'll mark this trait as `unsafe` instead. See [`Ivar`] for safety
|
||||
/// requirements.
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create an instance variable `myCustomIvar` with type `i32`.
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::declare::IvarType;
|
||||
///
|
||||
/// // Helper type
|
||||
/// struct MyCustomIvar;
|
||||
///
|
||||
/// unsafe impl IvarType for MyCustomIvar {
|
||||
/// type Type = i32;
|
||||
/// const NAME: &'static str = "myCustomIvar";
|
||||
/// }
|
||||
///
|
||||
/// // `Ivar<MyCustomIvar>` can now be used
|
||||
/// ```
|
||||
pub unsafe trait IvarType {
|
||||
/// The type of the instance variable.
|
||||
type Type: InnerIvarType;
|
||||
/// The name of the instance variable.
|
||||
const NAME: &'static str;
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe fn __offset(ptr: NonNull<Object>) -> isize {
|
||||
let obj = unsafe { ptr.as_ref() };
|
||||
ivar_offset(obj.class(), Self::NAME, &Self::Type::__ENCODING)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type over a custom instance variable.
|
||||
///
|
||||
/// This type is not meant to be constructed by itself, it must reside within
|
||||
/// another struct meant to represent an Objective-C object.
|
||||
///
|
||||
/// On [`Deref`] it then uses the [`IvarType::NAME`] string to access the ivar
|
||||
/// of the containing object.
|
||||
///
|
||||
/// Note that this is not ([currently][zst-hack]) allowed by [stacked
|
||||
/// borrows][sb], but due to [`Object`] being a zero-sized type such that we
|
||||
/// don't have provenance over the ivars anyhow, this should be just as sound
|
||||
/// as normal instance variable access.
|
||||
///
|
||||
/// [sb]: https://github.com/rust-lang/unsafe-code-guidelines/blob/e21202c60c7be03dd2ab016ada92fb5305d40438/wip/stacked-borrows.md
|
||||
/// [zst-hack]: https://github.com/rust-lang/unsafe-code-guidelines/issues/305
|
||||
///
|
||||
///
|
||||
/// # `bool` handling
|
||||
///
|
||||
/// This does _not_ perform a conversion step between [`bool`] and the
|
||||
/// Objective-C `BOOL`; use [`runtime::Bool`][crate::runtime::Bool] when you
|
||||
/// want your instance variable to be accessible from other Objective-C code.
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This must be used within a type that act as an Objective-C object. In
|
||||
/// particular, this is never safe to have on the stack by itself.
|
||||
///
|
||||
/// Additionally, the instance variable described by `T` must be available on
|
||||
/// the specific instance, and be of the exact same type. When declaring the
|
||||
/// object yourself, you can ensure this using
|
||||
/// [`ClassBuilder::add_static_ivar`].
|
||||
///
|
||||
/// Finally, two ivars with the same name must not be used on the same object.
|
||||
///
|
||||
/// [`ClassBuilder::add_static_ivar`]: crate::declare::ClassBuilder::add_static_ivar
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::declare::{Ivar, IvarType};
|
||||
/// use objc2::runtime::Object;
|
||||
/// #
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// // Declare ivar with given type and name
|
||||
/// struct MyCustomIvar;
|
||||
/// unsafe impl IvarType for MyCustomIvar {
|
||||
/// type Type = i32;
|
||||
/// const NAME: &'static str = "myCustomIvar";
|
||||
/// }
|
||||
///
|
||||
/// // Custom object
|
||||
/// #[repr(C)]
|
||||
/// pub struct MyObject {
|
||||
/// inner: Object,
|
||||
/// // SAFETY: The instance variable is used within an object, and it is
|
||||
/// // properly declared below.
|
||||
/// my_ivar: Ivar<MyCustomIvar>,
|
||||
/// }
|
||||
///
|
||||
/// # use objc2::class;
|
||||
/// # use objc2::declare::ClassBuilder;
|
||||
/// # let mut builder = ClassBuilder::new("MyObject", class!(NSObject)).unwrap();
|
||||
/// // Declare the class and add the instance variable to it
|
||||
/// builder.add_static_ivar::<MyCustomIvar>();
|
||||
/// # let _cls = builder.register();
|
||||
///
|
||||
/// let obj: MyObject;
|
||||
/// // You can now access `obj.my_ivar`
|
||||
/// ```
|
||||
///
|
||||
/// See also the `declare_ivar.rs` example.
|
||||
#[repr(C)]
|
||||
// Must not be `Copy` nor `Clone`!
|
||||
pub struct Ivar<T: IvarType> {
|
||||
/// Make this type allowed in `repr(C)`
|
||||
inner: [u8; 0],
|
||||
/// For proper variance and auto traits
|
||||
item: PhantomData<<T::Type as InnerIvarType>::Output>,
|
||||
}
|
||||
|
||||
impl<T: IvarType> Drop for Ivar<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if <T::Type as InnerIvarType>::__MAY_DROP {
|
||||
// SAFETY: We drop the inner type, which is guaranteed by
|
||||
// `__MAY_DROP` to always be safe to drop.
|
||||
unsafe { ptr::drop_in_place(self.as_inner_mut_ptr().as_ptr()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IvarType> Ivar<T> {
|
||||
/// Get a pointer to the instance variable.
|
||||
///
|
||||
/// Note that if the ivar has already been initialized, you can simply
|
||||
/// use the `Deref` implementation to get a reference.
|
||||
///
|
||||
/// This is similar to [`MaybeUninit::as_ptr`], see that for usage
|
||||
/// instructions.
|
||||
pub fn as_ptr(this: &Self) -> *const <T::Type as InnerIvarType>::Output {
|
||||
T::Type::__to_ptr(this.as_inner_ptr()).as_ptr()
|
||||
}
|
||||
|
||||
fn as_inner_ptr(&self) -> NonNull<<T::Type as InnerIvarType>::__Inner> {
|
||||
let ptr: NonNull<Object> = NonNull::from(self).cast();
|
||||
|
||||
// SAFETY: The user ensures that this is placed in a struct that can
|
||||
// be reinterpreted as an `Object`. Since `Ivar` can never be
|
||||
// constructed by itself (and is neither Copy nor Clone), we know that
|
||||
// it is guaranteed to _stay_ in said struct.
|
||||
//
|
||||
// Even if the user were to do `mem::swap`, the `Ivar` has a unique
|
||||
// type (and does not hold any data), so that wouldn't break anything.
|
||||
//
|
||||
// Note: We technically don't have provenance over the object, nor the
|
||||
// ivar, but the object doesn't have provenance over the ivar either,
|
||||
// so that is fine.
|
||||
let offset = unsafe { T::__offset(ptr) };
|
||||
// SAFETY: The offset is valid
|
||||
unsafe { Object::ivar_at_offset::<<T::Type as InnerIvarType>::__Inner>(ptr, offset) }
|
||||
}
|
||||
|
||||
/// Get a mutable pointer to the instance variable.
|
||||
///
|
||||
/// This is useful when you want to initialize the ivar inside an `init`
|
||||
/// method (where it may otherwise not have been safely initialized yet).
|
||||
///
|
||||
/// Note that if the ivar has already been initialized, you can simply
|
||||
/// use the `DerefMut` implementation to get a mutable reference.
|
||||
///
|
||||
/// This is similar to [`MaybeUninit::as_mut_ptr`], see that for usage
|
||||
/// instructions.
|
||||
pub fn as_mut_ptr(this: &mut Self) -> *mut <T::Type as InnerIvarType>::Output {
|
||||
T::Type::__to_ptr(this.as_inner_mut_ptr()).as_ptr()
|
||||
}
|
||||
|
||||
fn as_inner_mut_ptr(&mut self) -> NonNull<<T::Type as InnerIvarType>::__Inner> {
|
||||
let ptr: NonNull<Object> = NonNull::from(self).cast();
|
||||
|
||||
// SAFETY: Same as `as_inner_ptr`
|
||||
let offset = unsafe { T::__offset(ptr) };
|
||||
// SAFETY: The offset is valid
|
||||
unsafe { Object::ivar_at_offset::<<T::Type as InnerIvarType>::__Inner>(ptr, offset) }
|
||||
}
|
||||
|
||||
/// Sets the value of the instance variable.
|
||||
///
|
||||
/// This is useful when you want to initialize the ivar inside an `init`
|
||||
/// method (where it may otherwise not have been safely initialized yet).
|
||||
///
|
||||
/// This is similar to [`MaybeUninit::write`], see that for usage
|
||||
/// instructions.
|
||||
pub fn write(
|
||||
this: &mut Self,
|
||||
val: <T::Type as InnerIvarType>::Output,
|
||||
) -> &mut <T::Type as InnerIvarType>::Output {
|
||||
let ptr: *mut MaybeUninit<<T::Type as InnerIvarType>::Output> =
|
||||
Self::as_mut_ptr(this).cast();
|
||||
let ivar = unsafe { ptr.as_mut().unwrap_unchecked() };
|
||||
ivar.write(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IvarType> Deref for Ivar<T> {
|
||||
type Target = <T::Type as InnerIvarType>::Output;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
// SAFETY: User ensures that the `Ivar<T>` is only used when the ivar
|
||||
// exists, has the correct type, and has been properly initialized.
|
||||
//
|
||||
// Since all accesses to a particular ivar only goes through one
|
||||
// `Ivar`, if we have `&Ivar` we know that `&T` is safe.
|
||||
unsafe { T::Type::__to_ref(self.as_inner_ptr().as_ref()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IvarType> DerefMut for Ivar<T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
// SAFETY: User ensures that the `Ivar<T>` is only used when the ivar
|
||||
// exists, has the correct type, and has been properly initialized.
|
||||
//
|
||||
// Safe as mutable because there is only one access to a
|
||||
// particular ivar at a time (since we have `&mut self`).
|
||||
|
||||
// Note: We're careful not to create `&mut Object` because the user
|
||||
// might have two mutable references to different ivars, as such:
|
||||
//
|
||||
// ```
|
||||
// #[repr(C)]
|
||||
// struct X {
|
||||
// inner: Object,
|
||||
// ivar1: Ivar<Ivar1>,
|
||||
// ivar2: Ivar<Ivar2>,
|
||||
// }
|
||||
//
|
||||
// let mut x: X;
|
||||
// let ivar1: &mut Ivar<Ivar1> = &mut x.ivar1;
|
||||
// let ivar2: &mut Ivar<Ivar2> = &mut x.ivar2;
|
||||
// ```
|
||||
//
|
||||
// And using `mut` would create aliasing mutable reference to the
|
||||
// object.
|
||||
unsafe { T::Type::__to_mut(self.as_inner_mut_ptr().as_mut()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Format as a pointer to the instance variable.
|
||||
impl<T: IvarType> fmt::Pointer for Ivar<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Pointer::fmt(&Self::as_ptr(self), f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::mem;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use super::*;
|
||||
use crate::foundation::NSObject;
|
||||
use crate::rc::{Id, Owned};
|
||||
use crate::{declare_class, msg_send, msg_send_id, test_utils, ClassType, MessageReceiver};
|
||||
|
||||
struct TestIvar;
|
||||
|
||||
unsafe impl IvarType for TestIvar {
|
||||
type Type = u32;
|
||||
const NAME: &'static str = "_foo";
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct IvarTestObject {
|
||||
inner: Object,
|
||||
foo: Ivar<TestIvar>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_traits() {
|
||||
fn assert_auto_traits<T: Unpin + UnwindSafe + RefUnwindSafe + Sized + Send + Sync>() {}
|
||||
assert_auto_traits::<Ivar<TestIvar>>();
|
||||
|
||||
// Ensure that `Ivar` is zero-sized
|
||||
assert_eq!(mem::size_of::<Ivar<TestIvar>>(), 0);
|
||||
assert_eq!(mem::align_of::<Ivar<TestIvar>>(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn access_ivar() {
|
||||
let mut obj = test_utils::custom_object();
|
||||
let _: () = unsafe { msg_send![&mut obj, setFoo: 42u32] };
|
||||
|
||||
let obj = unsafe {
|
||||
obj.__as_raw_receiver()
|
||||
.cast::<IvarTestObject>()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
};
|
||||
assert_eq!(*obj.foo, 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ensure_custom_drop_is_possible() {
|
||||
static HAS_RUN_DEALLOC: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
declare_class!(
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct CustomDrop {
|
||||
ivar: u8,
|
||||
}
|
||||
|
||||
unsafe impl ClassType for CustomDrop {
|
||||
type Super = NSObject;
|
||||
}
|
||||
|
||||
unsafe impl CustomDrop {
|
||||
#[sel(dealloc)]
|
||||
fn dealloc(&mut self) {
|
||||
HAS_RUN_DEALLOC.store(true, Ordering::SeqCst);
|
||||
unsafe { msg_send![super(self), dealloc] }
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let _: Id<CustomDrop, Owned> = unsafe { msg_send_id![CustomDrop::class(), new] };
|
||||
|
||||
assert!(HAS_RUN_DEALLOC.load(Ordering::SeqCst));
|
||||
}
|
||||
}
|
||||
333
third-party/vendor/objc2/src/declare/ivar_drop.rs
vendored
Normal file
333
third-party/vendor/objc2/src/declare/ivar_drop.rs
vendored
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
use alloc::boxed::Box;
|
||||
use core::ffi::c_void;
|
||||
use core::marker::PhantomData;
|
||||
use core::ptr::NonNull;
|
||||
|
||||
use crate::encode::{EncodeConvert, Encoding};
|
||||
use crate::rc::{Id, Ownership};
|
||||
use crate::Message;
|
||||
|
||||
use super::InnerIvarType;
|
||||
|
||||
/// A helper type to allow putting certain types that may drop into ivars.
|
||||
///
|
||||
/// This is used to work around current limitations in the type system.
|
||||
/// Consider this type "temporary" in the sense that one day it may just
|
||||
/// become `type IvarDrop<T> = T`.
|
||||
///
|
||||
/// This currently works with the following types:
|
||||
/// - `Box<T>`
|
||||
/// - `Option<Box<T>>`
|
||||
/// - `Id<T, O>`
|
||||
/// - `Option<Id<T, O>>`
|
||||
///
|
||||
/// Further may be added when the standard library guarantees their layout.
|
||||
///
|
||||
/// See `examples/delegate.rs` for usage.
|
||||
pub struct IvarDrop<T> {
|
||||
/// For proper variance and auto traits.
|
||||
p: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Sized> super::ivar::private::Sealed for IvarDrop<Box<T>> {}
|
||||
// SAFETY: The memory layout of `Box<T: Sized>` is guaranteed to be a pointer:
|
||||
// <https://doc.rust-lang.org/1.62.1/std/boxed/index.html#memory-layout>
|
||||
//
|
||||
// The user ensures that the Box has been initialized in an `init` method
|
||||
// before being used.
|
||||
unsafe impl<T: Sized> InnerIvarType for IvarDrop<Box<T>> {
|
||||
// Note that we use `*const c_void` and not `*const T` to allow _any_
|
||||
// type, not just types that can be encoded by Objective-C
|
||||
const __ENCODING: Encoding = <*const c_void as EncodeConvert>::__ENCODING;
|
||||
|
||||
type __Inner = Option<Box<T>>;
|
||||
type Output = Box<T>;
|
||||
|
||||
const __MAY_DROP: bool = true;
|
||||
|
||||
#[inline]
|
||||
unsafe fn __to_ref(inner: &Self::__Inner) -> &Self::Output {
|
||||
match inner {
|
||||
Some(inner) => inner,
|
||||
None => unsafe { box_unreachable() },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn __to_mut(inner: &mut Self::__Inner) -> &mut Self::Output {
|
||||
match inner {
|
||||
Some(inner) => inner,
|
||||
None => unsafe { box_unreachable() },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn __to_ptr(inner: NonNull<Self::__Inner>) -> NonNull<Self::Output> {
|
||||
inner.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sized> super::ivar::private::Sealed for IvarDrop<Option<Box<T>>> {}
|
||||
// SAFETY: `Option<Box<T>>` guarantees the null-pointer optimization, so for
|
||||
// `T: Sized` the layout is just a pointer:
|
||||
// <https://doc.rust-lang.org/1.62.1/std/option/index.html#representation>
|
||||
//
|
||||
// This is valid to initialize as all-zeroes, so the user doesn't have to do
|
||||
// anything to initialize it.
|
||||
unsafe impl<T: Sized> InnerIvarType for IvarDrop<Option<Box<T>>> {
|
||||
const __ENCODING: Encoding = <*const c_void as EncodeConvert>::__ENCODING;
|
||||
|
||||
type __Inner = Option<Box<T>>;
|
||||
type Output = Option<Box<T>>;
|
||||
|
||||
const __MAY_DROP: bool = true;
|
||||
|
||||
#[inline]
|
||||
unsafe fn __to_ref(this: &Self::__Inner) -> &Self::Output {
|
||||
this
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn __to_mut(this: &mut Self::__Inner) -> &mut Self::Output {
|
||||
this
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn __to_ptr(inner: NonNull<Self::__Inner>) -> NonNull<Self::Output> {
|
||||
inner.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message, O: Ownership> super::ivar::private::Sealed for IvarDrop<Id<T, O>> {}
|
||||
// SAFETY: `Id` is `NonNull<T>`, and hence safe to store as a pointer.
|
||||
//
|
||||
// The user ensures that the Id has been initialized in an `init` method
|
||||
// before being used.
|
||||
//
|
||||
// Note: We could technically do `impl InnerIvarType for Ivar<Id<T, O>>`
|
||||
// directly today, but since we can't do so for `Box` (because that is
|
||||
// `#[fundamental]`), I think it makes sense to handle them similarly.
|
||||
unsafe impl<T: Message, O: Ownership> InnerIvarType for IvarDrop<Id<T, O>> {
|
||||
const __ENCODING: Encoding = <*const T as EncodeConvert>::__ENCODING;
|
||||
|
||||
type __Inner = Option<Id<T, O>>;
|
||||
type Output = Id<T, O>;
|
||||
|
||||
const __MAY_DROP: bool = true;
|
||||
|
||||
#[inline]
|
||||
unsafe fn __to_ref(inner: &Self::__Inner) -> &Self::Output {
|
||||
match inner {
|
||||
Some(inner) => inner,
|
||||
None => unsafe { id_unreachable() },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn __to_mut(inner: &mut Self::__Inner) -> &mut Self::Output {
|
||||
match inner {
|
||||
Some(inner) => inner,
|
||||
None => unsafe { id_unreachable() },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn __to_ptr(inner: NonNull<Self::__Inner>) -> NonNull<Self::Output> {
|
||||
inner.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message, O: Ownership> super::ivar::private::Sealed for IvarDrop<Option<Id<T, O>>> {}
|
||||
// SAFETY: `Id<T, O>` guarantees the null-pointer optimization.
|
||||
//
|
||||
// This is valid to initialize as all-zeroes, so the user doesn't have to do
|
||||
// anything to initialize it.
|
||||
unsafe impl<T: Message, O: Ownership> InnerIvarType for IvarDrop<Option<Id<T, O>>> {
|
||||
const __ENCODING: Encoding = <*const T as EncodeConvert>::__ENCODING;
|
||||
|
||||
type __Inner = Option<Id<T, O>>;
|
||||
type Output = Option<Id<T, O>>;
|
||||
|
||||
const __MAY_DROP: bool = true;
|
||||
|
||||
#[inline]
|
||||
unsafe fn __to_ref(this: &Self::__Inner) -> &Self::Output {
|
||||
this
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn __to_mut(this: &mut Self::__Inner) -> &mut Self::Output {
|
||||
this
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn __to_ptr(inner: NonNull<Self::__Inner>) -> NonNull<Self::Output> {
|
||||
inner.cast()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Allow the following once their layout is guaranteed by `std`:
|
||||
// - Arc<T>
|
||||
// - Option<Arc<T>>
|
||||
// - sync::Weak<T>
|
||||
// - Rc<T>
|
||||
// - Option<Rc<T>>
|
||||
// - rc::Weak<T>
|
||||
// - Vec<T>
|
||||
// - String
|
||||
|
||||
// TODO: Allow `WeakId` once we figure out how to allow it being initialized
|
||||
// by default.
|
||||
|
||||
#[inline]
|
||||
unsafe fn id_unreachable() -> ! {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
unreachable!("an Id in instance variables must always be initialized before use!")
|
||||
}
|
||||
// SAFETY: Checked by caller
|
||||
#[cfg(not(debug_assertions))]
|
||||
unsafe {
|
||||
core::hint::unreachable_unchecked()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn box_unreachable() -> ! {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
unreachable!("a Box in instance variables must always be initialized before use!")
|
||||
}
|
||||
// SAFETY: Checked by caller
|
||||
#[cfg(not(debug_assertions))]
|
||||
unsafe {
|
||||
core::hint::unreachable_unchecked()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::declare::{Ivar, IvarType};
|
||||
use crate::foundation::NSObject;
|
||||
use crate::rc::{Allocated, Owned, RcTestObject, Shared, ThreadTestData};
|
||||
use crate::runtime::Object;
|
||||
use crate::{declare_class, msg_send, msg_send_id, ClassType};
|
||||
|
||||
struct TestIvar1;
|
||||
unsafe impl IvarType for TestIvar1 {
|
||||
type Type = IvarDrop<Box<u8>>;
|
||||
const NAME: &'static str = "_abc";
|
||||
}
|
||||
|
||||
struct TestIvar2;
|
||||
unsafe impl IvarType for TestIvar2 {
|
||||
type Type = IvarDrop<Option<Box<u8>>>;
|
||||
const NAME: &'static str = "_abc";
|
||||
}
|
||||
|
||||
struct TestIvar3;
|
||||
unsafe impl IvarType for TestIvar3 {
|
||||
type Type = IvarDrop<Id<Object, Shared>>;
|
||||
const NAME: &'static str = "_abc";
|
||||
}
|
||||
|
||||
struct TestIvar4;
|
||||
unsafe impl IvarType for TestIvar4 {
|
||||
type Type = IvarDrop<Option<Id<Object, Owned>>>;
|
||||
const NAME: &'static str = "_abc";
|
||||
}
|
||||
|
||||
declare_class!(
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct IvarTester {
|
||||
ivar1: IvarDrop<Id<RcTestObject, Shared>>,
|
||||
ivar2: IvarDrop<Option<Id<RcTestObject, Owned>>>,
|
||||
ivar3: IvarDrop<Box<Id<RcTestObject, Owned>>>,
|
||||
ivar4: IvarDrop<Option<Box<Id<RcTestObject, Owned>>>>,
|
||||
}
|
||||
|
||||
unsafe impl ClassType for IvarTester {
|
||||
type Super = NSObject;
|
||||
}
|
||||
|
||||
unsafe impl IvarTester {
|
||||
#[sel(init)]
|
||||
fn init(&mut self) -> Option<&mut Self> {
|
||||
let this: Option<&mut Self> = unsafe { msg_send![super(self), init] };
|
||||
this.map(|this| {
|
||||
Ivar::write(&mut this.ivar1, Id::into_shared(RcTestObject::new()));
|
||||
*this.ivar2 = Some(RcTestObject::new());
|
||||
Ivar::write(&mut this.ivar3, Box::new(RcTestObject::new()));
|
||||
*this.ivar4 = Some(Box::new(RcTestObject::new()));
|
||||
this
|
||||
})
|
||||
}
|
||||
|
||||
#[sel(initInvalid)]
|
||||
fn init_invalid(&mut self) -> Option<&mut Self> {
|
||||
// Don't actually initialize anything here; this creates an
|
||||
// invalid instance, where accessing the two ivars `ivar1`
|
||||
// and `ivar3` is UB
|
||||
unsafe { msg_send![super(self), init] }
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn test_alloc_dealloc() {
|
||||
let expected = ThreadTestData::current();
|
||||
|
||||
let obj: Id<Allocated<IvarTester>, Owned> =
|
||||
unsafe { msg_send_id![IvarTester::class(), alloc] };
|
||||
expected.assert_current();
|
||||
|
||||
drop(obj);
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_init_drop() {
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
let mut obj: Id<IvarTester, Owned> = unsafe { msg_send_id![IvarTester::class(), new] };
|
||||
expected.alloc += 4;
|
||||
expected.init += 4;
|
||||
expected.assert_current();
|
||||
|
||||
*obj.ivar1 = (*obj.ivar1).clone();
|
||||
expected.retain += 1;
|
||||
expected.release += 1;
|
||||
expected.assert_current();
|
||||
|
||||
*obj.ivar2 = None;
|
||||
expected.release += 1;
|
||||
expected.dealloc += 1;
|
||||
expected.assert_current();
|
||||
|
||||
drop(obj);
|
||||
expected.release += 3;
|
||||
expected.dealloc += 3;
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(debug_assertions), ignore = "only panics in debug mode")]
|
||||
#[should_panic = "an Id in instance variables must always be initialized before use"]
|
||||
fn test_init_invalid_ref() {
|
||||
let obj: Id<IvarTester, Owned> =
|
||||
unsafe { msg_send_id![msg_send_id![IvarTester::class(), alloc], initInvalid] };
|
||||
|
||||
std::println!("{:?}", obj.ivar1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(debug_assertions), ignore = "only panics in debug mode")]
|
||||
#[should_panic = "an Id in instance variables must always be initialized before use"]
|
||||
fn test_init_invalid_mut() {
|
||||
let mut obj: Id<IvarTester, Owned> =
|
||||
unsafe { msg_send_id![msg_send_id![IvarTester::class(), alloc], initInvalid] };
|
||||
|
||||
*obj.ivar1 = RcTestObject::new().into();
|
||||
}
|
||||
}
|
||||
334
third-party/vendor/objc2/src/declare/ivar_forwarding_impls.rs
vendored
Normal file
334
third-party/vendor/objc2/src/declare/ivar_forwarding_impls.rs
vendored
Normal file
|
|
@ -0,0 +1,334 @@
|
|||
//! Trivial forwarding impls on `Ivar`.
|
||||
//!
|
||||
//! Kept here to keep `ivar.rs` free from this boilerplate.
|
||||
//!
|
||||
//! `#[inline]` is used where the standard library `Box` uses it.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
// use alloc::borrow;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt;
|
||||
use core::future::Future;
|
||||
use core::hash;
|
||||
use core::iter::FusedIterator;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
|
||||
use super::{Ivar, IvarType};
|
||||
|
||||
impl<T: IvarType> PartialEq for Ivar<T>
|
||||
where
|
||||
<Self as Deref>::Target: PartialEq,
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
(**self).eq(&**other)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::partialeq_ne_impl)]
|
||||
fn ne(&self, other: &Self) -> bool {
|
||||
(**self).ne(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IvarType> Eq for Ivar<T> where <Self as Deref>::Target: Eq {}
|
||||
|
||||
impl<T: IvarType> PartialOrd for Ivar<T>
|
||||
where
|
||||
<Self as Deref>::Target: PartialOrd,
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
(**self).partial_cmp(&**other)
|
||||
}
|
||||
#[inline]
|
||||
fn lt(&self, other: &Self) -> bool {
|
||||
(**self).lt(&**other)
|
||||
}
|
||||
#[inline]
|
||||
fn le(&self, other: &Self) -> bool {
|
||||
(**self).le(&**other)
|
||||
}
|
||||
#[inline]
|
||||
fn ge(&self, other: &Self) -> bool {
|
||||
(**self).ge(&**other)
|
||||
}
|
||||
#[inline]
|
||||
fn gt(&self, other: &Self) -> bool {
|
||||
(**self).gt(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IvarType> Ord for Ivar<T>
|
||||
where
|
||||
<Self as Deref>::Target: Ord,
|
||||
{
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
(**self).cmp(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IvarType> hash::Hash for Ivar<T>
|
||||
where
|
||||
<Self as Deref>::Target: hash::Hash,
|
||||
{
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
(**self).hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IvarType> hash::Hasher for Ivar<T>
|
||||
where
|
||||
<Self as Deref>::Target: hash::Hasher,
|
||||
{
|
||||
fn finish(&self) -> u64 {
|
||||
(**self).finish()
|
||||
}
|
||||
fn write(&mut self, bytes: &[u8]) {
|
||||
(**self).write(bytes)
|
||||
}
|
||||
fn write_u8(&mut self, i: u8) {
|
||||
(**self).write_u8(i)
|
||||
}
|
||||
fn write_u16(&mut self, i: u16) {
|
||||
(**self).write_u16(i)
|
||||
}
|
||||
fn write_u32(&mut self, i: u32) {
|
||||
(**self).write_u32(i)
|
||||
}
|
||||
fn write_u64(&mut self, i: u64) {
|
||||
(**self).write_u64(i)
|
||||
}
|
||||
fn write_u128(&mut self, i: u128) {
|
||||
(**self).write_u128(i)
|
||||
}
|
||||
fn write_usize(&mut self, i: usize) {
|
||||
(**self).write_usize(i)
|
||||
}
|
||||
fn write_i8(&mut self, i: i8) {
|
||||
(**self).write_i8(i)
|
||||
}
|
||||
fn write_i16(&mut self, i: i16) {
|
||||
(**self).write_i16(i)
|
||||
}
|
||||
fn write_i32(&mut self, i: i32) {
|
||||
(**self).write_i32(i)
|
||||
}
|
||||
fn write_i64(&mut self, i: i64) {
|
||||
(**self).write_i64(i)
|
||||
}
|
||||
fn write_i128(&mut self, i: i128) {
|
||||
(**self).write_i128(i)
|
||||
}
|
||||
fn write_isize(&mut self, i: isize) {
|
||||
(**self).write_isize(i)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IvarType> fmt::Display for Ivar<T>
|
||||
where
|
||||
<Self as Deref>::Target: fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IvarType> fmt::Debug for Ivar<T>
|
||||
where
|
||||
<Self as Deref>::Target: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: IvarType> Iterator for Ivar<I>
|
||||
where
|
||||
<Self as Deref>::Target: Iterator,
|
||||
{
|
||||
type Item = <<Self as Deref>::Target as Iterator>::Item;
|
||||
fn next(&mut self) -> Option<<<Self as Deref>::Target as Iterator>::Item> {
|
||||
(**self).next()
|
||||
}
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(**self).size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: IvarType> DoubleEndedIterator for Ivar<I>
|
||||
where
|
||||
<Self as Deref>::Target: DoubleEndedIterator,
|
||||
{
|
||||
fn next_back(&mut self) -> Option<<<Self as Deref>::Target as Iterator>::Item> {
|
||||
(**self).next_back()
|
||||
}
|
||||
fn nth_back(&mut self, n: usize) -> Option<<<Self as Deref>::Target as Iterator>::Item> {
|
||||
(**self).nth_back(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: IvarType> ExactSizeIterator for Ivar<I>
|
||||
where
|
||||
<Self as Deref>::Target: ExactSizeIterator,
|
||||
{
|
||||
fn len(&self) -> usize {
|
||||
(**self).len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: IvarType> FusedIterator for Ivar<I> where <Self as Deref>::Target: FusedIterator {}
|
||||
|
||||
// impl<T: IvarType> borrow::Borrow<<Self as Deref>::Target> for Ivar<T> {
|
||||
// fn borrow(&self) -> &<Self as Deref>::Target {
|
||||
// Deref::deref(self)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl<T: IvarType> borrow::BorrowMut<<Self as Deref>::Target> for Ivar<T> {
|
||||
// fn borrow_mut(&mut self) -> &mut <Self as Deref>::Target {
|
||||
// DerefMut::deref_mut(self)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<T: IvarType> AsRef<<Self as Deref>::Target> for Ivar<T> {
|
||||
fn as_ref(&self) -> &<Self as Deref>::Target {
|
||||
Deref::deref(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IvarType> AsMut<<Self as Deref>::Target> for Ivar<T> {
|
||||
fn as_mut(&mut self) -> &mut <Self as Deref>::Target {
|
||||
DerefMut::deref_mut(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IvarType> Error for Ivar<T>
|
||||
where
|
||||
<Self as Deref>::Target: Error,
|
||||
{
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
(**self).source()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IvarType> io::Read for Ivar<T>
|
||||
where
|
||||
<Self as Deref>::Target: io::Read,
|
||||
{
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
(**self).read(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
(**self).read_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
(**self).read_to_end(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
(**self).read_to_string(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
(**self).read_exact(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IvarType> io::Write for Ivar<T>
|
||||
where
|
||||
<Self as Deref>::Target: io::Write,
|
||||
{
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
(**self).write(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
(**self).write_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
(**self).flush()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
(**self).write_all(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
(**self).write_fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IvarType> io::Seek for Ivar<T>
|
||||
where
|
||||
<Self as Deref>::Target: io::Seek,
|
||||
{
|
||||
#[inline]
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
||||
(**self).seek(pos)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
(**self).stream_position()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IvarType> io::BufRead for Ivar<T>
|
||||
where
|
||||
<Self as Deref>::Target: io::BufRead,
|
||||
{
|
||||
#[inline]
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
(**self).fill_buf()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn consume(&mut self, amt: usize) {
|
||||
(**self).consume(amt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
(**self).read_until(byte, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
(**self).read_line(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IvarType> Future for Ivar<T>
|
||||
where
|
||||
<Self as Deref>::Target: Future + Unpin,
|
||||
{
|
||||
type Output = <<Self as Deref>::Target as Future>::Output;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
<<Self as Deref>::Target as Future>::poll(Pin::new(&mut *self), cx)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: impl Fn traits, CoerceUnsized, Stream and so on when stabilized
|
||||
307
third-party/vendor/objc2/src/exception.rs
vendored
Normal file
307
third-party/vendor/objc2/src/exception.rs
vendored
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
//! Objective-C's @throw and @try/@catch.
|
||||
//!
|
||||
//! By default, if the [`msg_send!`] macro causes an exception to be thrown,
|
||||
//! this will unwind into Rust, resulting in undefined behavior. However, this
|
||||
//! crate has an `"catch-all"` feature which, when enabled, wraps each
|
||||
//! [`msg_send!`] in a `@catch` and panics if an exception is caught,
|
||||
//! preventing Objective-C from unwinding into Rust.
|
||||
//!
|
||||
//! The `@try`/`@catch` functionality in this module is only available when
|
||||
//! the `"exception"` feature is enabled.
|
||||
//!
|
||||
//! See the following links for more information:
|
||||
//! - [Exception Programming Topics for Cocoa](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Exceptions/Exceptions.html)
|
||||
//! - [The Objective-C Programming Language - Exception Handling](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocExceptionHandling.html)
|
||||
//! - [Exception Handling in LLVM](https://llvm.org/docs/ExceptionHandling.html)
|
||||
//!
|
||||
//! [`msg_send!`]: crate::msg_send
|
||||
|
||||
// TODO: Test this with panic=abort, and ensure that the code-size is
|
||||
// reasonable in that case.
|
||||
|
||||
#[cfg(feature = "exception")]
|
||||
use core::ffi::c_void;
|
||||
use core::fmt;
|
||||
#[cfg(feature = "exception")]
|
||||
use core::mem;
|
||||
use core::ops::Deref;
|
||||
use core::panic::RefUnwindSafe;
|
||||
use core::panic::UnwindSafe;
|
||||
#[cfg(feature = "exception")]
|
||||
use core::ptr;
|
||||
use objc2_encode::Encoding;
|
||||
use objc2_encode::RefEncode;
|
||||
use std::error::Error;
|
||||
|
||||
#[cfg(feature = "exception")]
|
||||
use crate::ffi;
|
||||
#[cfg(feature = "exception")]
|
||||
use crate::rc::{Id, Shared};
|
||||
use crate::runtime::Object;
|
||||
use crate::Message;
|
||||
|
||||
/// An Objective-C exception.
|
||||
///
|
||||
/// While highly recommended that any exceptions you intend to throw are
|
||||
/// subclasses of `NSException`, this is not required by the runtime (similar
|
||||
/// to how Rust can panic with arbitary payloads using [`panic_any`]).
|
||||
///
|
||||
/// [`panic_any`]: std::panic::panic_any
|
||||
#[repr(transparent)]
|
||||
pub struct Exception(Object);
|
||||
|
||||
unsafe impl RefEncode for Exception {
|
||||
const ENCODING_REF: Encoding = Encoding::Object;
|
||||
}
|
||||
|
||||
unsafe impl Message for Exception {}
|
||||
|
||||
impl Deref for Exception {
|
||||
type Target = Object;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Object {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Object> for Exception {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &Object {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// Note: We can't implement `Send` nor `Sync` since the exception could be
|
||||
// anything!
|
||||
|
||||
impl fmt::Debug for Exception {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "exception ")?;
|
||||
|
||||
// Attempt to present a somewhat usable error message if the
|
||||
// `foundation` feature is enabled
|
||||
#[cfg(feature = "foundation")]
|
||||
if crate::foundation::NSException::is_nsexception(self) {
|
||||
// SAFETY: Just checked that object is an NSException
|
||||
let obj: *const Self = self;
|
||||
let obj = unsafe {
|
||||
obj.cast::<crate::foundation::NSException>()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
};
|
||||
return write!(f, "{:?}", obj);
|
||||
}
|
||||
|
||||
// Fall back to `Object` Debug
|
||||
write!(f, "{:?}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Exception {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
#[cfg(feature = "foundation")]
|
||||
if crate::foundation::NSException::is_nsexception(self) {
|
||||
// SAFETY: Just checked that object is an NSException
|
||||
let obj: *const Self = self;
|
||||
let obj = unsafe {
|
||||
obj.cast::<crate::foundation::NSException>()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
};
|
||||
if let Some(reason) = obj.reason() {
|
||||
return write!(f, "{}", reason);
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, "unknown exception")
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for Exception {}
|
||||
|
||||
impl UnwindSafe for Exception {}
|
||||
impl RefUnwindSafe for Exception {}
|
||||
|
||||
/// Throws an Objective-C exception.
|
||||
///
|
||||
/// This is the Objective-C equivalent of Rust's [`panic!`].
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This unwinds from Objective-C, and the exception must be caught using an
|
||||
/// Objective-C exception handler like [`catch`] (and specifically not
|
||||
/// [`catch_unwind`]).
|
||||
///
|
||||
/// This also invokes undefined behaviour until `C-unwind` is stabilized, see
|
||||
/// [RFC-2945] - you can try this out on nightly using the `unstable-c-unwind`
|
||||
/// feature flag.
|
||||
///
|
||||
/// [`catch_unwind`]: std::panic::catch_unwind
|
||||
/// [RFC-2945]: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html
|
||||
#[inline]
|
||||
#[cfg(feature = "exception")] // For consistency, not strictly required
|
||||
pub unsafe fn throw(exception: Id<Exception, Shared>) -> ! {
|
||||
let ptr = exception.0.as_ptr() as *mut ffi::objc_object;
|
||||
// SAFETY: Object is valid and non-null (nil exceptions are not valid in
|
||||
// the old runtime).
|
||||
unsafe { ffi::objc_exception_throw(ptr) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "exception")]
|
||||
unsafe fn try_no_ret<F: FnOnce()>(closure: F) -> Result<(), Option<Id<Exception, Shared>>> {
|
||||
#[cfg(not(feature = "unstable-c-unwind"))]
|
||||
let f = {
|
||||
extern "C" fn try_objc_execute_closure<F>(closure: &mut Option<F>)
|
||||
where
|
||||
F: FnOnce(),
|
||||
{
|
||||
// This is always passed Some, so it's safe to unwrap
|
||||
let closure = closure.take().unwrap();
|
||||
closure();
|
||||
}
|
||||
|
||||
let f: extern "C" fn(&mut Option<F>) = try_objc_execute_closure;
|
||||
let f: extern "C" fn(*mut c_void) = unsafe { mem::transmute(f) };
|
||||
f
|
||||
};
|
||||
|
||||
#[cfg(feature = "unstable-c-unwind")]
|
||||
let f = {
|
||||
extern "C-unwind" fn try_objc_execute_closure<F>(closure: &mut Option<F>)
|
||||
where
|
||||
F: FnOnce(),
|
||||
{
|
||||
// This is always passed Some, so it's safe to unwrap
|
||||
let closure = closure.take().unwrap();
|
||||
closure();
|
||||
}
|
||||
|
||||
let f: extern "C-unwind" fn(&mut Option<F>) = try_objc_execute_closure;
|
||||
let f: extern "C-unwind" fn(*mut c_void) = unsafe { mem::transmute(f) };
|
||||
f
|
||||
};
|
||||
|
||||
// Wrap the closure in an Option so it can be taken
|
||||
let mut closure = Some(closure);
|
||||
let context: *mut Option<F> = &mut closure;
|
||||
let context = context.cast();
|
||||
|
||||
let mut exception = ptr::null_mut();
|
||||
let success = unsafe { ffi::rust_objc_sys_0_2_try_catch_exception(f, context, &mut exception) };
|
||||
|
||||
if success == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
// SAFETY:
|
||||
// The exception is always a valid object or NULL.
|
||||
//
|
||||
// The ownership is safe as Shared; Objective-C code throwing an
|
||||
// exception knows that they don't hold sole access to that exception
|
||||
// instance any more, and Rust code is forbidden by requiring a Shared
|
||||
// Id in `throw` (instead of just a shared reference, which could have
|
||||
// come from an Owned Id).
|
||||
Err(unsafe { Id::new(exception.cast()) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to execute the given closure and catches an Objective-C exception
|
||||
/// if one is thrown.
|
||||
///
|
||||
/// This is the Objective-C equivalent of Rust's [`catch_unwind`].
|
||||
/// Accordingly, if your Rust code is compiled with `panic=abort` this cannot
|
||||
/// catch the exception.
|
||||
///
|
||||
/// Returns a `Result` that is either `Ok` if the closure succeeded without an
|
||||
/// exception being thrown, or an `Err` with the exception. The exception is
|
||||
/// automatically released.
|
||||
///
|
||||
/// The exception is `None` in the extremely exceptional case that the
|
||||
/// exception object is `nil`. This should basically never happen, but is
|
||||
/// technically possible on some systems with `@throw nil`.
|
||||
///
|
||||
/// [`catch_unwind`]: std::panic::catch_unwind
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The given closure must not panic (e.g. normal Rust unwinding into this
|
||||
/// causes undefined behaviour).
|
||||
///
|
||||
/// Additionally, this unwinds through the closure from Objective-C, which is
|
||||
/// undefined behaviour until `C-unwind` is stabilized, see [RFC-2945] - you
|
||||
/// can try this out on nightly using the `unstable-c-unwind` feature flag.
|
||||
///
|
||||
/// [RFC-2945]: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html
|
||||
#[cfg(feature = "exception")]
|
||||
pub unsafe fn catch<R>(
|
||||
closure: impl FnOnce() -> R + UnwindSafe,
|
||||
) -> Result<R, Option<Id<Exception, Shared>>> {
|
||||
let mut value = None;
|
||||
let value_ref = &mut value;
|
||||
let closure = move || {
|
||||
*value_ref = Some(closure());
|
||||
};
|
||||
let result = unsafe { try_no_ret(closure) };
|
||||
// If the try succeeded, this was set so it's safe to unwrap
|
||||
result.map(|()| value.unwrap())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "exception")]
|
||||
mod tests {
|
||||
use alloc::format;
|
||||
use alloc::string::ToString;
|
||||
|
||||
use super::*;
|
||||
use crate::{class, msg_send_id};
|
||||
|
||||
#[test]
|
||||
fn test_catch() {
|
||||
let mut s = "Hello".to_string();
|
||||
let result = unsafe {
|
||||
catch(move || {
|
||||
s.push_str(", World!");
|
||||
s
|
||||
})
|
||||
};
|
||||
assert_eq!(result.unwrap(), "Hello, World!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
all(feature = "apple", target_os = "macos", target_arch = "x86"),
|
||||
ignore = "`NULL` exceptions are invalid on 32-bit / w. fragile runtime"
|
||||
)]
|
||||
fn test_catch_null() {
|
||||
let s = "Hello".to_string();
|
||||
let result = unsafe {
|
||||
catch(move || {
|
||||
if !s.is_empty() {
|
||||
ffi::objc_exception_throw(ptr::null_mut())
|
||||
}
|
||||
s.len()
|
||||
})
|
||||
};
|
||||
assert!(result.unwrap_err().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_throw_catch_object() {
|
||||
let obj: Id<Exception, Shared> = unsafe { msg_send_id![class!(NSObject), new] };
|
||||
// TODO: Investigate why this is required on GNUStep!
|
||||
let _obj2 = obj.clone();
|
||||
let ptr: *const Exception = &*obj;
|
||||
|
||||
let result = unsafe { catch(|| throw(obj)) };
|
||||
let obj = result.unwrap_err().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
format!("{:?}", obj),
|
||||
format!("exception <NSObject: {:p}>", ptr)
|
||||
);
|
||||
|
||||
assert!(ptr::eq(&*obj, ptr));
|
||||
}
|
||||
}
|
||||
523
third-party/vendor/objc2/src/foundation/__ns_string.rs
vendored
Normal file
523
third-party/vendor/objc2/src/foundation/__ns_string.rs
vendored
Normal file
|
|
@ -0,0 +1,523 @@
|
|||
//! Macro for making a static NSString.
|
||||
//!
|
||||
//! This basically does what clang does, see:
|
||||
//! - Apple: <https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/CodeGen/CodeGenModule.cpp#L5057-L5249>
|
||||
//! - GNUStep 2.0 (not yet supported): <https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/CodeGen/CGObjCGNU.cpp#L973-L1118>
|
||||
//! - Other (not yet supported): <https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/CodeGen/CGObjCGNU.cpp#L2471-L2507>
|
||||
//!
|
||||
//! Note that this uses the `CFString` static, while `clang` has support for
|
||||
//! generating a pure `NSString`. We don't support that yet (since I don't
|
||||
//! know the use-case), but we definitely could!
|
||||
//! See: <https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/CodeGen/CGObjCMac.cpp#L2007-L2068>
|
||||
use core::ffi::c_void;
|
||||
use core::mem::ManuallyDrop;
|
||||
use core::ptr;
|
||||
use core::sync::atomic::{AtomicPtr, Ordering};
|
||||
|
||||
use crate::foundation::NSString;
|
||||
use crate::rc::Id;
|
||||
use crate::runtime::Class;
|
||||
|
||||
// This is defined in CoreFoundation, but we don't emit a link attribute
|
||||
// here because it is already linked via Foundation.
|
||||
//
|
||||
// Although this is a "private" (underscored) symbol, it is directly
|
||||
// referenced in Objective-C binaries. So it's safe for us to reference.
|
||||
extern "C" {
|
||||
pub static __CFConstantStringClassReference: Class;
|
||||
}
|
||||
|
||||
/// Structure used to describe a constant `CFString`.
|
||||
///
|
||||
/// This struct is the same as [`CF_CONST_STRING`], which contains
|
||||
/// [`CFRuntimeBase`]. While the documentation clearly says that the ABI of
|
||||
/// `CFRuntimeBase` should not be relied on, we can rely on it as long as we
|
||||
/// only do it with regards to `CFString` (because `clang` does this as well).
|
||||
///
|
||||
/// [`CFRuntimeBase`]: <https://github.com/apple-oss-distributions/CF/blob/CF-1153.18/CFRuntime.h#L216-L228>
|
||||
/// [`CF_CONST_STRING`]: <https://github.com/apple-oss-distributions/CF/blob/CF-1153.18/CFInternal.h#L332-L336>
|
||||
#[repr(C)]
|
||||
pub struct CFConstString {
|
||||
isa: &'static Class,
|
||||
// Important that we don't just use `usize` here, since that would be
|
||||
// wrong on big-endian systems!
|
||||
cfinfo: u32,
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
_rc: u32,
|
||||
data: *const c_void,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
// Required to place in a `static`.
|
||||
unsafe impl Sync for CFConstString {}
|
||||
|
||||
impl CFConstString {
|
||||
// From `CFString.c`:
|
||||
// <https://github.com/apple-oss-distributions/CF/blob/CF-1153.18/CFString.c#L184-L212>
|
||||
// > !!! Note: Constant CFStrings use the bit patterns:
|
||||
// > C8 (11001000 = default allocator, not inline, not freed contents; 8-bit; has NULL byte; doesn't have length; is immutable)
|
||||
// > D0 (11010000 = default allocator, not inline, not freed contents; Unicode; is immutable)
|
||||
// > The bit usages should not be modified in a way that would effect these bit patterns.
|
||||
//
|
||||
// Hence CoreFoundation guarantees that these two are always valid.
|
||||
//
|
||||
// The `CFTypeID` of `CFStringRef` is guaranteed to always be 7:
|
||||
// <https://github.com/apple-oss-distributions/CF/blob/CF-1153.18/CFRuntime.c#L982>
|
||||
const FLAGS_ASCII: u32 = 0x07_C8;
|
||||
const FLAGS_UTF16: u32 = 0x07_D0;
|
||||
|
||||
pub const unsafe fn new_ascii(isa: &'static Class, data: &'static [u8]) -> Self {
|
||||
Self {
|
||||
isa,
|
||||
cfinfo: Self::FLAGS_ASCII,
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
_rc: 0,
|
||||
data: data.as_ptr().cast(),
|
||||
// The length does not include the trailing NUL.
|
||||
len: data.len() - 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub const unsafe fn new_utf16(isa: &'static Class, data: &'static [u16]) -> Self {
|
||||
Self {
|
||||
isa,
|
||||
cfinfo: Self::FLAGS_UTF16,
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
_rc: 0,
|
||||
data: data.as_ptr().cast(),
|
||||
// The length does not include the trailing NUL.
|
||||
len: data.len() - 1,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn as_nsstring_const(&self) -> &NSString {
|
||||
let ptr: *const Self = self;
|
||||
unsafe { &*ptr.cast::<NSString>() }
|
||||
}
|
||||
|
||||
// This is deliberately not `const` to prevent the result from being used
|
||||
// in other statics, since not all platforms support that (yet).
|
||||
#[inline]
|
||||
pub fn as_nsstring(&self) -> &NSString {
|
||||
self.as_nsstring_const()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if `bytes` is entirely ASCII with no interior NULs.
|
||||
pub const fn is_ascii_no_nul(bytes: &[u8]) -> bool {
|
||||
let mut i = 0;
|
||||
while i < bytes.len() {
|
||||
let byte = bytes[i];
|
||||
if !byte.is_ascii() || byte == b'\0' {
|
||||
return false;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub struct Utf16Char {
|
||||
pub repr: [u16; 2],
|
||||
pub len: usize,
|
||||
}
|
||||
|
||||
impl Utf16Char {
|
||||
const fn encode(ch: u32) -> Self {
|
||||
if ch <= 0xffff {
|
||||
Self {
|
||||
repr: [ch as u16, 0],
|
||||
len: 1,
|
||||
}
|
||||
} else {
|
||||
let payload = ch - 0x10000;
|
||||
let hi = (payload >> 10) | 0xd800;
|
||||
let lo = (payload & 0x3ff) | 0xdc00;
|
||||
Self {
|
||||
repr: [hi as u16, lo as u16],
|
||||
len: 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn as_slice(&self) -> &[u16] {
|
||||
&self.repr[..self.len]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EncodeUtf16Iter {
|
||||
str: &'static [u8],
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl EncodeUtf16Iter {
|
||||
pub const fn new(str: &'static [u8]) -> Self {
|
||||
Self { str, index: 0 }
|
||||
}
|
||||
|
||||
pub const fn next(self) -> Option<(Self, Utf16Char)> {
|
||||
if self.index >= self.str.len() {
|
||||
None
|
||||
} else {
|
||||
let (index, ch) = decode_utf8(self.str, self.index);
|
||||
Some((Self { index, ..self }, Utf16Char::encode(ch)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// (&str bytes, index) -> (new index, decoded char)
|
||||
const fn decode_utf8(s: &[u8], i: usize) -> (usize, u32) {
|
||||
let b0 = s[i];
|
||||
match b0 {
|
||||
// one-byte seq
|
||||
0b0000_0000..=0b0111_1111 => {
|
||||
let decoded = b0 as u32;
|
||||
(i + 1, decoded)
|
||||
}
|
||||
// two-byte seq
|
||||
0b1100_0000..=0b1101_1111 => {
|
||||
let decoded = ((b0 as u32 & 0x1f) << 6) | (s[i + 1] as u32 & 0x3f);
|
||||
(i + 2, decoded)
|
||||
}
|
||||
// 3 byte seq
|
||||
0b1110_0000..=0b1110_1111 => {
|
||||
let decoded = ((b0 as u32 & 0x0f) << 12)
|
||||
| ((s[i + 1] as u32 & 0x3f) << 6)
|
||||
| (s[i + 2] as u32 & 0x3f);
|
||||
(i + 3, decoded)
|
||||
}
|
||||
// 4 byte seq
|
||||
0b1111_0000..=0b1111_0111 => {
|
||||
let decoded = ((b0 as u32 & 0x07) << 18)
|
||||
| ((s[i + 1] as u32 & 0x3f) << 12)
|
||||
| ((s[i + 2] as u32 & 0x3f) << 6)
|
||||
| (s[i + 3] as u32 & 0x3f);
|
||||
(i + 4, decoded)
|
||||
}
|
||||
// continuation bytes, or never-valid bytes.
|
||||
0b1000_0000..=0b1011_1111 | 0b1111_1000..=0b1111_1111 => {
|
||||
panic!("Encountered invalid bytes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows storing a [`NSString`] in a static and lazily loading it.
|
||||
#[doc(hidden)]
|
||||
pub struct CachedNSString {
|
||||
ptr: AtomicPtr<NSString>,
|
||||
}
|
||||
|
||||
impl CachedNSString {
|
||||
/// Constructs a new [`CachedNSString`].
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
ptr: AtomicPtr::new(ptr::null_mut()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the cached NSString. If no string is yet cached, creates one
|
||||
/// with the given name and stores it.
|
||||
#[inline]
|
||||
pub fn get(&self, s: &str) -> &'static NSString {
|
||||
// TODO: Investigate if we can use weaker orderings.
|
||||
let ptr = self.ptr.load(Ordering::SeqCst);
|
||||
// SAFETY: The pointer is either NULL, or has been created below.
|
||||
unsafe { ptr.as_ref() }.unwrap_or_else(|| {
|
||||
// "Forget" about releasing the string, effectively promoting it
|
||||
// to a static.
|
||||
let s = ManuallyDrop::new(NSString::from_str(s));
|
||||
let ptr = Id::as_ptr(&s);
|
||||
self.ptr.store(ptr as *mut NSString, Ordering::SeqCst);
|
||||
// SAFETY: The pointer is valid, and will always be valid, since
|
||||
// we haven't released it.
|
||||
unsafe { ptr.as_ref().unwrap_unchecked() }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an [`NSString`][`crate::foundation::NSString`] from a static string.
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// This macro takes a either a `"string"` literal or `const` string slice as
|
||||
/// the argument, and produces a `&'static NSString`:
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::ns_string;
|
||||
/// use objc2::foundation::NSString;
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
/// let hello: &'static NSString = ns_string!("hello");
|
||||
/// assert_eq!(hello.to_string(), "hello");
|
||||
///
|
||||
/// const WORLD: &str = "world";
|
||||
/// let world = ns_string!(WORLD);
|
||||
/// assert_eq!(world.to_string(), WORLD);
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// # Unicode Strings
|
||||
///
|
||||
/// An NSString can contain strings with many different encodings, including
|
||||
/// ASCII, UTF-8, UTF-16, and so on. This macro automatically converts your
|
||||
/// string to the most efficient encoding, you don't have to do anything!
|
||||
///
|
||||
/// ```
|
||||
/// # use objc2::ns_string;
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
/// let hello_ru = ns_string!("Привет");
|
||||
/// assert_eq!(hello_ru.to_string(), "Привет");
|
||||
/// ```
|
||||
///
|
||||
/// Note that because this is implemented with `const` evaluation, massive
|
||||
/// strings can increase compile time, and may even hit the `const` evaluation
|
||||
/// limit.
|
||||
///
|
||||
///
|
||||
/// # NUL handling
|
||||
///
|
||||
/// Strings containing ASCII NUL is allowed, the NUL is preserved as one would
|
||||
/// expect:
|
||||
///
|
||||
/// ```
|
||||
/// # use objc2::ns_string;
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
/// let example = ns_string!("example\0");
|
||||
/// assert_eq!(example.to_string(), "example\0");
|
||||
///
|
||||
/// let example = ns_string!("exa\0mple");
|
||||
/// assert_eq!(example.to_string(), "exa\0mple");
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// # Runtime Cost
|
||||
///
|
||||
/// None.
|
||||
///
|
||||
/// The result is equivalent to `@"string"` syntax in Objective-C.
|
||||
///
|
||||
/// Because of that, this should be preferred over [`NSString::from_str`]
|
||||
/// where possible.
|
||||
///
|
||||
/// [`NSString::from_str`]: crate::foundation::NSString::from_str
|
||||
#[cfg(feature = "foundation")] // For auto_doc_cfg
|
||||
#[macro_export]
|
||||
macro_rules! ns_string {
|
||||
($s:expr) => {{
|
||||
// Immediately place in constant for better UI
|
||||
const INPUT: &str = $s;
|
||||
$crate::__ns_string_inner!(INPUT)
|
||||
}};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "apple")]
|
||||
#[macro_export]
|
||||
macro_rules! __ns_string_inner {
|
||||
($inp:ident) => {{
|
||||
const X: &[u8] = $inp.as_bytes();
|
||||
$crate::__ns_string_inner!(@inner X);
|
||||
// Return &'static NSString
|
||||
CFSTRING.as_nsstring()
|
||||
}};
|
||||
(@inner $inp:ident) => {
|
||||
// Note: We create both the ASCII + NUL and the UTF-16 + NUL versions
|
||||
// of the string, since we can't conditionally create a static.
|
||||
//
|
||||
// Since we don't add the `#[used]` attribute, Rust can fairly easily
|
||||
// figure out that one of the variants are never used, and simply
|
||||
// exclude it.
|
||||
|
||||
// Convert the input slice to a C-style string with a NUL byte.
|
||||
//
|
||||
// The section is the same as what clang sets, see:
|
||||
// https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/CodeGen/CodeGenModule.cpp#L5192
|
||||
#[link_section = "__TEXT,__cstring,cstring_literals"]
|
||||
static ASCII: [u8; $inp.len() + 1] = {
|
||||
// Zero-fill with $inp.len() + 1
|
||||
let mut res: [u8; $inp.len() + 1] = [0; $inp.len() + 1];
|
||||
let mut i = 0;
|
||||
// Fill with data from $inp
|
||||
while i < $inp.len() {
|
||||
res[i] = $inp[i];
|
||||
i += 1;
|
||||
}
|
||||
// Now contains $inp + '\0'
|
||||
res
|
||||
};
|
||||
|
||||
// The full UTF-16 contents along with the written length.
|
||||
const UTF16_FULL: (&[u16; $inp.len()], usize) = {
|
||||
let mut out = [0u16; $inp.len()];
|
||||
let mut iter = $crate::foundation::__ns_string::EncodeUtf16Iter::new($inp);
|
||||
let mut written = 0;
|
||||
|
||||
while let Some((state, chars)) = iter.next() {
|
||||
iter = state;
|
||||
out[written] = chars.repr[0];
|
||||
written += 1;
|
||||
|
||||
if chars.len > 1 {
|
||||
out[written] = chars.repr[1];
|
||||
written += 1;
|
||||
}
|
||||
}
|
||||
|
||||
(&{ out }, written)
|
||||
};
|
||||
|
||||
// Convert the slice to an UTF-16 array + a final NUL byte.
|
||||
//
|
||||
// The section is the same as what clang sets, see:
|
||||
// https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/CodeGen/CodeGenModule.cpp#L5193
|
||||
#[link_section = "__TEXT,__ustring"]
|
||||
static UTF16: [u16; UTF16_FULL.1 + 1] = {
|
||||
// Zero-fill with UTF16_FULL.1 + 1
|
||||
let mut res: [u16; UTF16_FULL.1 + 1] = [0; UTF16_FULL.1 + 1];
|
||||
let mut i = 0;
|
||||
// Fill with data from UTF16_FULL.0 up until UTF16_FULL.1
|
||||
while i < UTF16_FULL.1 {
|
||||
res[i] = UTF16_FULL.0[i];
|
||||
i += 1;
|
||||
}
|
||||
// Now contains UTF16_FULL.1 + NUL
|
||||
res
|
||||
};
|
||||
|
||||
// Create the constant string structure, and store it in a static
|
||||
// within a special section.
|
||||
//
|
||||
// The section is the same as what clang sets, see:
|
||||
// https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/CodeGen/CodeGenModule.cpp#L5243
|
||||
#[link_section = "__DATA,__cfstring"]
|
||||
static CFSTRING: $crate::foundation::__ns_string::CFConstString = unsafe {
|
||||
if $crate::foundation::__ns_string::is_ascii_no_nul($inp) {
|
||||
// This is technically an optimization (UTF-16 strings are
|
||||
// always valid), but it's a fairly important one!
|
||||
$crate::foundation::__ns_string::CFConstString::new_ascii(
|
||||
&$crate::foundation::__ns_string::__CFConstantStringClassReference,
|
||||
&ASCII,
|
||||
)
|
||||
} else {
|
||||
$crate::foundation::__ns_string::CFConstString::new_utf16(
|
||||
&$crate::foundation::__ns_string::__CFConstantStringClassReference,
|
||||
&UTF16,
|
||||
)
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(feature = "apple"))]
|
||||
#[macro_export]
|
||||
macro_rules! __ns_string_inner {
|
||||
($inp:ident) => {{
|
||||
use $crate::foundation::__ns_string::CachedNSString;
|
||||
static CACHED_NSSTRING: CachedNSString = CachedNSString::new();
|
||||
CACHED_NSSTRING.get($inp)
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::string::ToString;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_is_ascii() {
|
||||
assert!(is_ascii_no_nul(b"a"));
|
||||
assert!(is_ascii_no_nul(b"abc"));
|
||||
|
||||
assert!(!is_ascii_no_nul(b"\xff"));
|
||||
|
||||
assert!(!is_ascii_no_nul(b"\0"));
|
||||
assert!(!is_ascii_no_nul(b"a\0b"));
|
||||
assert!(!is_ascii_no_nul(b"ab\0"));
|
||||
assert!(!is_ascii_no_nul(b"a\0b\0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_utf8() {
|
||||
for c in '\u{0}'..=core::char::MAX {
|
||||
let mut buf;
|
||||
for off in 0..4 {
|
||||
// Ensure we see garbage if we read outside bounds.
|
||||
buf = [0xff; 8];
|
||||
let len = c.encode_utf8(&mut buf[off..(off + 4)]).len();
|
||||
let (end_idx, decoded) = decode_utf8(&buf, off);
|
||||
assert_eq!(
|
||||
(end_idx, decoded),
|
||||
(off + len, c as u32),
|
||||
"failed for U+{code:04X} ({ch:?}) encoded as {buf:#x?} over {range:?}",
|
||||
code = c as u32,
|
||||
ch = c,
|
||||
buf = &buf[off..(off + len)],
|
||||
range = off..(off + len),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_utf16() {
|
||||
for c in '\u{0}'..=core::char::MAX {
|
||||
assert_eq!(
|
||||
c.encode_utf16(&mut [0u16; 2]),
|
||||
Utf16Char::encode(c as u32).as_slice(),
|
||||
"failed for U+{:04X} ({:?})",
|
||||
c as u32,
|
||||
c
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ns_string() {
|
||||
macro_rules! test {
|
||||
($($s:expr,)+) => {$({
|
||||
let s1 = ns_string!($s);
|
||||
let s2 = NSString::from_str($s);
|
||||
|
||||
assert_eq!(s1, s1);
|
||||
assert_eq!(s1, &*s2);
|
||||
|
||||
assert_eq!(s1.to_string(), $s);
|
||||
assert_eq!(s2.to_string(), $s);
|
||||
})+};
|
||||
}
|
||||
|
||||
test! {
|
||||
"",
|
||||
"asdf",
|
||||
"🦀",
|
||||
"🏳️🌈",
|
||||
"𝄞music",
|
||||
"abcd【e】fg",
|
||||
"abcd⒠fg",
|
||||
"ääääh",
|
||||
"lööps, bröther?",
|
||||
"\u{fffd} \u{fffd} \u{fffd}",
|
||||
"讓每個人都能打造出。",
|
||||
"\0",
|
||||
"\0\x01\x02\x03\x04\x05\x06\x07\x08\x09",
|
||||
// "\u{feff}", // TODO
|
||||
include_str!("__ns_string.rs"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ns_string_in_unsafe() {
|
||||
// Test that the `unused_unsafe` lint doesn't trigger
|
||||
let s = unsafe {
|
||||
let s: *const NSString = ns_string!("abc");
|
||||
&*s
|
||||
};
|
||||
assert_eq!(s.to_string(), "abc");
|
||||
}
|
||||
}
|
||||
506
third-party/vendor/objc2/src/foundation/array.rs
vendored
Normal file
506
third-party/vendor/objc2/src/foundation/array.rs
vendored
Normal file
|
|
@ -0,0 +1,506 @@
|
|||
use alloc::vec::Vec;
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Index, IndexMut, Range};
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
use super::{
|
||||
NSCopying, NSEnumerator, NSFastEnumeration, NSFastEnumerator, NSMutableArray, NSMutableCopying,
|
||||
NSObject, NSRange,
|
||||
};
|
||||
use crate::rc::{DefaultId, Id, Owned, Ownership, Shared, SliceId};
|
||||
use crate::runtime::{Class, Object};
|
||||
use crate::{ClassType, Message, __inner_extern_class, extern_methods, msg_send, msg_send_id};
|
||||
|
||||
__inner_extern_class!(
|
||||
/// An immutable ordered collection of objects.
|
||||
///
|
||||
/// This is the Objective-C equivalent of a "boxed slice" (`Box<[T]>`),
|
||||
/// so effectively a `Vec<T>` where you can't change the number of
|
||||
/// elements.
|
||||
///
|
||||
/// The type of the contained objects is described by the generic
|
||||
/// parameter `T`, and the ownership of the objects is described with the
|
||||
/// generic parameter `O`.
|
||||
///
|
||||
///
|
||||
/// # Ownership
|
||||
///
|
||||
/// While `NSArray` _itself_ is immutable, i.e. the number of objects it
|
||||
/// contains can't change, it is still possible to modify the contained
|
||||
/// objects themselves, if you know you're the sole owner of them -
|
||||
/// quite similar to how you can modify elements in `Box<[T]>`.
|
||||
///
|
||||
/// To mutate the contained objects the ownership must be `O = Owned`. A
|
||||
/// summary of what the different "types" of arrays allow you to do can be
|
||||
/// found below. `Array` refers to either `NSArray` or `NSMutableArray`.
|
||||
/// - `Id<NSMutableArray<T, Owned>, Owned>`: Allows you to mutate the
|
||||
/// objects, and the array itself.
|
||||
/// - `Id<NSMutableArray<T, Shared>, Owned>`: Allows you to mutate the
|
||||
/// array itself, but not it's contents.
|
||||
/// - `Id<NSArray<T, Owned>, Owned>`: Allows you to mutate the objects,
|
||||
/// but not the array itself.
|
||||
/// - `Id<NSArray<T, Shared>, Owned>`: Effectively the same as the below.
|
||||
/// - `Id<Array<T, Shared>, Shared>`: Allows you to copy the array, but
|
||||
/// does not allow you to modify it in any way.
|
||||
/// - `Id<Array<T, Owned>, Shared>`: Pretty useless compared to the
|
||||
/// others, avoid this.
|
||||
///
|
||||
/// See [Apple's documentation][apple-doc].
|
||||
///
|
||||
/// [apple-doc]: https://developer.apple.com/documentation/foundation/nsarray?language=objc
|
||||
// `T: PartialEq` bound correct because `NSArray` does deep (instead of
|
||||
// shallow) equality comparisons.
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct NSArray<T: Message, O: Ownership = Shared> {
|
||||
item: PhantomData<Id<T, O>>,
|
||||
notunwindsafe: PhantomData<&'static mut ()>,
|
||||
}
|
||||
|
||||
unsafe impl<T: Message, O: Ownership> ClassType for NSArray<T, O> {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
// SAFETY: Same as Id<T, O> (which is what NSArray effectively stores).
|
||||
unsafe impl<T: Message + Sync + Send> Sync for NSArray<T, Shared> {}
|
||||
unsafe impl<T: Message + Sync + Send> Send for NSArray<T, Shared> {}
|
||||
unsafe impl<T: Message + Sync> Sync for NSArray<T, Owned> {}
|
||||
unsafe impl<T: Message + Send> Send for NSArray<T, Owned> {}
|
||||
|
||||
// Also same as Id<T, O>
|
||||
impl<T: Message + RefUnwindSafe, O: Ownership> RefUnwindSafe for NSArray<T, O> {}
|
||||
impl<T: Message + RefUnwindSafe> UnwindSafe for NSArray<T, Shared> {}
|
||||
impl<T: Message + UnwindSafe> UnwindSafe for NSArray<T, Owned> {}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) unsafe fn with_objects<T: Message + ?Sized, R: Message, O: Ownership>(
|
||||
cls: &Class,
|
||||
objects: &[&T],
|
||||
) -> Id<R, O> {
|
||||
unsafe {
|
||||
msg_send_id![
|
||||
msg_send_id![cls, alloc],
|
||||
initWithObjects: objects.as_ptr(),
|
||||
count: objects.len(),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
extern_methods!(
|
||||
/// Generic creation methods.
|
||||
unsafe impl<T: Message, O: Ownership> NSArray<T, O> {
|
||||
/// Get an empty array.
|
||||
pub fn new() -> Id<Self, Shared> {
|
||||
// SAFETY:
|
||||
// - `new` may not create a new object, but instead return a shared
|
||||
// instance. We remedy this by returning `Id<Self, Shared>`.
|
||||
// - `O` don't actually matter here! E.g. `NSArray<T, Owned>` is
|
||||
// perfectly legal, since the array doesn't have any elements, and
|
||||
// hence the notion of ownership over the elements is void.
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
pub fn from_vec(vec: Vec<Id<T, O>>) -> Id<Self, O> {
|
||||
// SAFETY:
|
||||
// `initWithObjects:` may choose to deduplicate arrays (I could
|
||||
// imagine it having a special case for arrays with one `NSNumber`
|
||||
// object), and returning mutable references to those would be
|
||||
// unsound!
|
||||
// However, when we know that we have ownership over the variables, we
|
||||
// also know that there cannot be another array in existence with the
|
||||
// same objects, so `Id<NSArray<T, Owned>, Owned>` is safe to return.
|
||||
//
|
||||
// In essence, we can choose between always returning `Id<T, Shared>`
|
||||
// or `Id<T, O>`, and the latter is probably the most useful, as we
|
||||
// would like to know when we're the only owner of the array, to
|
||||
// allow mutation of the array's items.
|
||||
unsafe { with_objects(Self::class(), vec.as_slice_ref()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Creation methods that produce shared arrays.
|
||||
unsafe impl<T: Message> NSArray<T, Shared> {
|
||||
pub fn from_slice(slice: &[Id<T, Shared>]) -> Id<Self, Shared> {
|
||||
// SAFETY: Taking `&T` would not be sound, since the `&T` could come
|
||||
// from an `Id<T, Owned>` that would now no longer be owned!
|
||||
//
|
||||
// (Note that NSArray internally retains all the objects it is given,
|
||||
// effectively making the safety requirements the same as
|
||||
// `Id::retain`).
|
||||
unsafe { with_objects(Self::class(), slice.as_slice_ref()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic accessor methods.
|
||||
unsafe impl<T: Message, O: Ownership> NSArray<T, O> {
|
||||
#[doc(alias = "count")]
|
||||
#[sel(count)]
|
||||
pub fn len(&self) -> usize;
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
#[sel(objectAtIndex:)]
|
||||
unsafe fn get_unchecked(&self, index: usize) -> &T;
|
||||
|
||||
#[doc(alias = "objectAtIndex:")]
|
||||
pub fn get(&self, index: usize) -> Option<&T> {
|
||||
// TODO: Replace this check with catching the thrown NSRangeException
|
||||
if index < self.len() {
|
||||
// SAFETY: The index is checked to be in bounds.
|
||||
Some(unsafe { self.get_unchecked(index) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "firstObject")]
|
||||
#[sel(firstObject)]
|
||||
pub fn first(&self) -> Option<&T>;
|
||||
|
||||
#[doc(alias = "lastObject")]
|
||||
#[sel(lastObject)]
|
||||
pub fn last(&self) -> Option<&T>;
|
||||
|
||||
#[doc(alias = "objectEnumerator")]
|
||||
pub fn iter(&self) -> NSEnumerator<'_, T> {
|
||||
unsafe {
|
||||
let result: *mut Object = msg_send![self, objectEnumerator];
|
||||
NSEnumerator::from_ptr(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[sel(getObjects:range:)]
|
||||
unsafe fn get_objects(&self, ptr: *mut &T, range: NSRange);
|
||||
|
||||
pub fn objects_in_range(&self, range: Range<usize>) -> Vec<&T> {
|
||||
let range = NSRange::from(range);
|
||||
let mut vec = Vec::with_capacity(range.length);
|
||||
unsafe {
|
||||
self.get_objects(vec.as_mut_ptr(), range);
|
||||
vec.set_len(range.length);
|
||||
}
|
||||
vec
|
||||
}
|
||||
|
||||
pub fn to_vec(&self) -> Vec<&T> {
|
||||
self.objects_in_range(0..self.len())
|
||||
}
|
||||
|
||||
// TODO: Take Id<Self, Self::ItemOwnership> ?
|
||||
pub fn into_vec(array: Id<Self, Owned>) -> Vec<Id<T, O>> {
|
||||
array
|
||||
.to_vec()
|
||||
.into_iter()
|
||||
.map(|obj| unsafe { Id::retain(obj as *const T as *mut T).unwrap_unchecked() })
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Accessor methods that work on shared arrays.
|
||||
unsafe impl<T: Message> NSArray<T, Shared> {
|
||||
#[doc(alias = "objectAtIndex:")]
|
||||
pub fn get_retained(&self, index: usize) -> Id<T, Shared> {
|
||||
let obj = self.get(index).unwrap();
|
||||
// SAFETY: The object is originally shared (see `where` bound).
|
||||
unsafe { Id::retain_autoreleased(obj as *const T as *mut T).unwrap_unchecked() }
|
||||
}
|
||||
|
||||
pub fn to_shared_vec(&self) -> Vec<Id<T, Shared>> {
|
||||
self.to_vec()
|
||||
.into_iter()
|
||||
.map(|obj| unsafe { Id::retain(obj as *const T as *mut T).unwrap_unchecked() })
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Accessor methods that work on owned arrays.
|
||||
unsafe impl<T: Message> NSArray<T, Owned> {
|
||||
#[doc(alias = "objectAtIndex:")]
|
||||
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
|
||||
// TODO: Replace this check with catching the thrown NSRangeException
|
||||
if index < self.len() {
|
||||
// SAFETY: The index is checked to be in bounds.
|
||||
Some(unsafe { msg_send![self, objectAtIndex: index] })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "firstObject")]
|
||||
#[sel(firstObject)]
|
||||
pub fn first_mut(&mut self) -> Option<&mut T>;
|
||||
|
||||
#[doc(alias = "lastObject")]
|
||||
#[sel(lastObject)]
|
||||
pub fn last_mut(&mut self) -> Option<&mut T>;
|
||||
}
|
||||
);
|
||||
|
||||
/// This is implemented as a shallow copy.
|
||||
///
|
||||
/// As such, it is only possible when the array's contents are `Shared`.
|
||||
unsafe impl<T: Message> NSCopying for NSArray<T, Shared> {
|
||||
type Ownership = Shared;
|
||||
type Output = NSArray<T, Shared>;
|
||||
}
|
||||
|
||||
/// This is implemented as a shallow copy.
|
||||
unsafe impl<T: Message> NSMutableCopying for NSArray<T, Shared> {
|
||||
type Output = NSMutableArray<T, Shared>;
|
||||
}
|
||||
|
||||
impl<T: Message> alloc::borrow::ToOwned for NSArray<T, Shared> {
|
||||
type Owned = Id<NSArray<T, Shared>, Shared>;
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
self.copy()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Message, O: Ownership> NSFastEnumeration for NSArray<T, O> {
|
||||
type Item = T;
|
||||
}
|
||||
|
||||
impl<'a, T: Message, O: Ownership> IntoIterator for &'a NSArray<T, O> {
|
||||
type Item = &'a T;
|
||||
type IntoIter = NSFastEnumerator<'a, NSArray<T, O>>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter_fast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message, O: Ownership> Index<usize> for NSArray<T, O> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, index: usize) -> &T {
|
||||
self.get(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message> IndexMut<usize> for NSArray<T, Owned> {
|
||||
fn index_mut(&mut self, index: usize) -> &mut T {
|
||||
self.get_mut(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message, O: Ownership> DefaultId for NSArray<T, O> {
|
||||
type Ownership = Shared;
|
||||
|
||||
#[inline]
|
||||
fn default_id() -> Id<Self, Self::Ownership> {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug + Message, O: Ownership> fmt::Debug for NSArray<T, O> {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_list().entries(self.iter_fast()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::format;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use super::*;
|
||||
use crate::foundation::{NSNumber, NSString};
|
||||
use crate::rc::{RcTestObject, ThreadTestData};
|
||||
|
||||
fn sample_array(len: usize) -> Id<NSArray<NSObject, Owned>, Owned> {
|
||||
let mut vec = Vec::with_capacity(len);
|
||||
for _ in 0..len {
|
||||
vec.push(NSObject::new());
|
||||
}
|
||||
NSArray::from_vec(vec)
|
||||
}
|
||||
|
||||
fn sample_number_array(len: u8) -> Id<NSArray<NSNumber, Shared>, Shared> {
|
||||
let mut vec = Vec::with_capacity(len as usize);
|
||||
for i in 0..len {
|
||||
vec.push(NSNumber::new_u8(i));
|
||||
}
|
||||
NSArray::from_vec(vec)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_two_empty() {
|
||||
let _empty_array1 = NSArray::<NSObject>::new();
|
||||
let _empty_array2 = NSArray::<NSObject>::new();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_len() {
|
||||
let empty_array = NSArray::<NSObject>::new();
|
||||
assert_eq!(empty_array.len(), 0);
|
||||
|
||||
let array = sample_array(4);
|
||||
assert_eq!(array.len(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equality() {
|
||||
let array1 = sample_array(3);
|
||||
let array2 = sample_array(3);
|
||||
assert_ne!(array1, array2);
|
||||
|
||||
let array1 = sample_number_array(3);
|
||||
let array2 = sample_number_array(3);
|
||||
assert_eq!(array1, array2);
|
||||
|
||||
let array1 = sample_number_array(3);
|
||||
let array2 = sample_number_array(4);
|
||||
assert_ne!(array1, array2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let obj = sample_number_array(0);
|
||||
assert_eq!(format!("{:?}", obj), "[]");
|
||||
let obj = sample_number_array(3);
|
||||
assert_eq!(format!("{:?}", obj), "[0, 1, 2]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get() {
|
||||
let array = sample_array(4);
|
||||
assert_ne!(array.get(0), array.get(3));
|
||||
assert_eq!(array.first(), array.get(0));
|
||||
assert_eq!(array.last(), array.get(3));
|
||||
|
||||
let empty_array = <NSArray<NSObject>>::new();
|
||||
assert!(empty_array.first().is_none());
|
||||
assert!(empty_array.last().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_retains_stored() {
|
||||
let obj = Id::into_shared(RcTestObject::new());
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
let input = [obj.clone(), obj.clone()];
|
||||
expected.retain += 2;
|
||||
expected.assert_current();
|
||||
|
||||
let array = NSArray::from_slice(&input);
|
||||
expected.retain += 2;
|
||||
expected.assert_current();
|
||||
|
||||
let _obj = array.first().unwrap();
|
||||
expected.assert_current();
|
||||
|
||||
drop(array);
|
||||
expected.release += 2;
|
||||
expected.assert_current();
|
||||
|
||||
let array = NSArray::from_vec(Vec::from(input));
|
||||
expected.retain += 2;
|
||||
expected.release += 2;
|
||||
expected.assert_current();
|
||||
|
||||
let _obj = array.get(0).unwrap();
|
||||
let _obj = array.get(1).unwrap();
|
||||
assert!(array.get(2).is_none());
|
||||
expected.assert_current();
|
||||
|
||||
drop(array);
|
||||
expected.release += 2;
|
||||
expected.assert_current();
|
||||
|
||||
drop(obj);
|
||||
expected.release += 1;
|
||||
expected.dealloc += 1;
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nscopying_uses_retain() {
|
||||
let obj = Id::into_shared(RcTestObject::new());
|
||||
let array = NSArray::from_slice(&[obj]);
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
let _copy = array.copy();
|
||||
expected.assert_current();
|
||||
|
||||
let _copy = array.mutable_copy();
|
||||
expected.retain += 1;
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
feature = "apple",
|
||||
ignore = "this works differently on different framework versions"
|
||||
)]
|
||||
fn test_iter_no_retain() {
|
||||
let obj = Id::into_shared(RcTestObject::new());
|
||||
let array = NSArray::from_slice(&[obj]);
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
let iter = array.iter();
|
||||
expected.retain += 0;
|
||||
expected.assert_current();
|
||||
|
||||
assert_eq!(iter.count(), 1);
|
||||
expected.autorelease += 0;
|
||||
expected.assert_current();
|
||||
|
||||
let iter = array.iter_fast();
|
||||
assert_eq!(iter.count(), 1);
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iter() {
|
||||
let array = sample_array(4);
|
||||
|
||||
assert_eq!(array.iter().count(), 4);
|
||||
assert!(array
|
||||
.iter()
|
||||
.enumerate()
|
||||
.all(|(i, obj)| Some(obj) == array.get(i)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_objects_in_range() {
|
||||
let array = sample_array(4);
|
||||
|
||||
let middle_objs = array.objects_in_range(1..3);
|
||||
assert_eq!(middle_objs.len(), 2);
|
||||
assert_eq!(middle_objs[0], array.get(1).unwrap());
|
||||
assert_eq!(middle_objs[1], array.get(2).unwrap());
|
||||
|
||||
let empty_objs = array.objects_in_range(1..1);
|
||||
assert!(empty_objs.is_empty());
|
||||
|
||||
let all_objs = array.objects_in_range(0..4);
|
||||
assert_eq!(all_objs.len(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_vec() {
|
||||
let array = sample_array(4);
|
||||
|
||||
let vec = NSArray::into_vec(array);
|
||||
assert_eq!(vec.len(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generic_ownership_traits() {
|
||||
fn assert_partialeq<T: PartialEq>() {}
|
||||
|
||||
assert_partialeq::<NSArray<NSString, Shared>>();
|
||||
assert_partialeq::<NSArray<NSString, Owned>>();
|
||||
|
||||
fn test_ownership_implies_partialeq<O: Ownership>() {
|
||||
assert_partialeq::<NSArray<NSString, O>>();
|
||||
}
|
||||
|
||||
test_ownership_implies_partialeq::<Shared>();
|
||||
test_ownership_implies_partialeq::<Owned>();
|
||||
}
|
||||
}
|
||||
212
third-party/vendor/objc2/src/foundation/attributed_string.rs
vendored
Normal file
212
third-party/vendor/objc2/src/foundation/attributed_string.rs
vendored
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
use core::fmt;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
use super::{
|
||||
NSCopying, NSDictionary, NSMutableAttributedString, NSMutableCopying, NSObject, NSString,
|
||||
};
|
||||
use crate::rc::{DefaultId, Id, Shared};
|
||||
use crate::runtime::Object;
|
||||
use crate::{extern_class, extern_methods, msg_send_id, ClassType};
|
||||
|
||||
extern_class!(
|
||||
/// A string that has associated attributes for portions of its text.
|
||||
///
|
||||
/// Examples of attributes could be: Visual style, hyperlinks, or
|
||||
/// accessibility data.
|
||||
///
|
||||
/// Conceptually, each UTF-16 code unit in an attributed string has its
|
||||
/// own collection of attributes - most often though
|
||||
///
|
||||
/// Only the most basic functionality is defined here, the `AppKit`
|
||||
/// framework contains most of the extension methods.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsattributedstring?language=objc).
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct NSAttributedString;
|
||||
|
||||
unsafe impl ClassType for NSAttributedString {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
// SAFETY: `NSAttributedString` is immutable and `NSMutableAttributedString`
|
||||
// can only be mutated from `&mut` methods.
|
||||
unsafe impl Sync for NSAttributedString {}
|
||||
unsafe impl Send for NSAttributedString {}
|
||||
|
||||
// Same reasoning as `NSString`.
|
||||
impl UnwindSafe for NSAttributedString {}
|
||||
impl RefUnwindSafe for NSAttributedString {}
|
||||
|
||||
/// Attributes that you can apply to text in an attributed string.
|
||||
pub type NSAttributedStringKey = NSString;
|
||||
|
||||
extern_methods!(
|
||||
/// Creating attributed strings.
|
||||
unsafe impl NSAttributedString {
|
||||
/// Construct an empty attributed string.
|
||||
pub fn new() -> Id<Self, Shared> {
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
/// Creates a new attributed string from the given string and attributes.
|
||||
///
|
||||
/// The attributes are associated with every UTF-16 code unit in the
|
||||
/// string.
|
||||
#[doc(alias = "initWithString:")]
|
||||
pub fn new_with_attributes(
|
||||
string: &NSString,
|
||||
// TODO: Mutability of the dictionary should be (Shared, Shared)
|
||||
attributes: &NSDictionary<NSAttributedStringKey, Object>,
|
||||
) -> Id<Self, Shared> {
|
||||
unsafe {
|
||||
let obj = msg_send_id![Self::class(), alloc];
|
||||
msg_send_id![obj, initWithString: string, attributes: attributes]
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new attributed string without any attributes.
|
||||
#[doc(alias = "initWithString:")]
|
||||
pub fn from_nsstring(string: &NSString) -> Id<Self, Shared> {
|
||||
unsafe {
|
||||
let obj = msg_send_id![Self::class(), alloc];
|
||||
msg_send_id![obj, initWithString: string]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Querying.
|
||||
unsafe impl NSAttributedString {
|
||||
// TODO: Lifetimes?
|
||||
pub fn string(&self) -> Id<NSString, Shared> {
|
||||
unsafe { msg_send_id![self, string] }
|
||||
}
|
||||
|
||||
/// Alias for `self.string().len_utf16()`.
|
||||
#[doc(alias = "length")]
|
||||
#[sel(length)]
|
||||
pub fn len_utf16(&self) -> usize;
|
||||
|
||||
// /// TODO
|
||||
// ///
|
||||
// /// See [Apple's documentation on Effective and Maximal Ranges][doc].
|
||||
// ///
|
||||
// /// [doc]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/AttributedStrings/Tasks/AccessingAttrs.html#//apple_ref/doc/uid/20000161-SW2
|
||||
// #[doc(alias = "attributesAtIndex:effectiveRange:")]
|
||||
// pub fn attributes_in_effective_range(
|
||||
// &self,
|
||||
// index: usize,
|
||||
// range: Range<usize>,
|
||||
// ) -> Id<Self, Shared> {
|
||||
// let range = NSRange::from(range);
|
||||
// todo!()
|
||||
// }
|
||||
//
|
||||
// attributesAtIndex:longestEffectiveRange:inRange:
|
||||
|
||||
// TODO: attributedSubstringFromRange:
|
||||
// TODO: enumerateAttributesInRange:options:usingBlock:
|
||||
}
|
||||
);
|
||||
|
||||
impl DefaultId for NSAttributedString {
|
||||
type Ownership = Shared;
|
||||
|
||||
#[inline]
|
||||
fn default_id() -> Id<Self, Self::Ownership> {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl NSCopying for NSAttributedString {
|
||||
type Ownership = Shared;
|
||||
type Output = NSAttributedString;
|
||||
}
|
||||
|
||||
unsafe impl NSMutableCopying for NSAttributedString {
|
||||
type Output = NSMutableAttributedString;
|
||||
}
|
||||
|
||||
impl alloc::borrow::ToOwned for NSAttributedString {
|
||||
type Owned = Id<NSAttributedString, Shared>;
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
self.copy()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NSAttributedString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Use -[NSAttributedString description] since it is pretty good
|
||||
let obj: &NSObject = self;
|
||||
fmt::Debug::fmt(obj, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::string::ToString;
|
||||
use alloc::{format, vec};
|
||||
|
||||
use super::*;
|
||||
use crate::rc::{autoreleasepool, Owned};
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
let s = NSAttributedString::new();
|
||||
assert_eq!(&s.string().to_string(), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_bound_to_attributed() {
|
||||
let attr_s = {
|
||||
let source = NSString::from_str("Hello world!");
|
||||
NSAttributedString::from_nsstring(&source)
|
||||
};
|
||||
let s = autoreleasepool(|_| attr_s.string());
|
||||
assert_eq!(s.len(), 12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_nsstring() {
|
||||
let s = NSAttributedString::from_nsstring(&NSString::from_str("abc"));
|
||||
assert_eq!(&s.string().to_string(), "abc");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_copy() {
|
||||
let s1 = NSAttributedString::from_nsstring(&NSString::from_str("abc"));
|
||||
let s2 = s1.copy();
|
||||
// NSAttributedString performs this optimization in GNUStep's runtime,
|
||||
// but not in Apple's; so we don't test for it!
|
||||
// assert_eq!(Id::as_ptr(&s1), Id::as_ptr(&s2));
|
||||
assert!(s2.is_kind_of::<NSAttributedString>());
|
||||
|
||||
let s3 = s1.mutable_copy();
|
||||
assert_ne!(Id::as_ptr(&s1), Id::as_ptr(&s3).cast());
|
||||
assert!(s3.is_kind_of::<NSMutableAttributedString>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let s = NSAttributedString::from_nsstring(&NSString::from_str("abc"));
|
||||
let expected = if cfg!(feature = "gnustep-1-7") {
|
||||
"abc{}"
|
||||
} else {
|
||||
"abc{\n}"
|
||||
};
|
||||
assert_eq!(format!("{:?}", s), expected);
|
||||
|
||||
let obj: Id<Object, Owned> = unsafe { Id::cast(NSObject::new()) };
|
||||
let ptr: *const Object = &*obj;
|
||||
let s = NSAttributedString::new_with_attributes(
|
||||
&NSString::from_str("abc"),
|
||||
&NSDictionary::from_keys_and_objects(&[&*NSString::from_str("test")], vec![obj]),
|
||||
);
|
||||
let expected = if cfg!(feature = "gnustep-1-7") {
|
||||
format!("abc{{test = \"<NSObject: {:?}>\"; }}", ptr)
|
||||
} else {
|
||||
format!("abc{{\n test = \"<NSObject: {:?}>\";\n}}", ptr)
|
||||
};
|
||||
assert_eq!(format!("{:?}", s), expected);
|
||||
}
|
||||
}
|
||||
73
third-party/vendor/objc2/src/foundation/bundle.rs
vendored
Normal file
73
third-party/vendor/objc2/src/foundation/bundle.rs
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
use core::fmt;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
use super::{NSCopying, NSDictionary, NSObject, NSString};
|
||||
use crate::rc::{Id, Shared};
|
||||
use crate::{extern_class, extern_methods, msg_send_id, ns_string, ClassType};
|
||||
|
||||
extern_class!(
|
||||
/// A representation of the code and resources stored in a bundle
|
||||
/// directory on disk.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsbundle?language=objc).
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct NSBundle;
|
||||
|
||||
unsafe impl ClassType for NSBundle {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
// SAFETY: Bundles are documented as thread-safe.
|
||||
unsafe impl Sync for NSBundle {}
|
||||
unsafe impl Send for NSBundle {}
|
||||
|
||||
impl UnwindSafe for NSBundle {}
|
||||
impl RefUnwindSafe for NSBundle {}
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSBundle {
|
||||
pub fn main() -> Id<Self, Shared> {
|
||||
unsafe { msg_send_id![Self::class(), mainBundle] }
|
||||
}
|
||||
|
||||
pub fn info(&self) -> Id<NSDictionary<NSString, NSObject>, Shared> {
|
||||
unsafe { msg_send_id![self, infoDictionary] }
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Option<Id<NSString, Shared>> {
|
||||
self.info().get(ns_string!("CFBundleName")).map(|name| {
|
||||
let ptr: *const NSObject = name;
|
||||
let ptr: *const NSString = ptr.cast();
|
||||
// SAFETY: TODO
|
||||
let name = unsafe { ptr.as_ref().unwrap_unchecked() };
|
||||
name.copy()
|
||||
})
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl fmt::Debug for NSBundle {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Delegate to NSObject
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use alloc::format;
|
||||
use std::println;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(target_os = "macos"), ignore = "varies between platforms")]
|
||||
fn try_running_functions() {
|
||||
// This is mostly empty since cargo doesn't bundle the application
|
||||
// before executing.
|
||||
let bundle = NSBundle::main();
|
||||
println!("{:?}", bundle);
|
||||
assert_eq!(format!("{:?}", bundle.info()), "{}");
|
||||
assert_eq!(bundle.name(), None);
|
||||
}
|
||||
}
|
||||
54
third-party/vendor/objc2/src/foundation/comparison_result.rs
vendored
Normal file
54
third-party/vendor/objc2/src/foundation/comparison_result.rs
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
use core::cmp::Ordering;
|
||||
|
||||
use crate::{Encode, Encoding, RefEncode};
|
||||
|
||||
/// Constants that indicate sort order.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nscomparisonresult?language=objc).
|
||||
#[repr(isize)] // NSInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum NSComparisonResult {
|
||||
/// The left operand is smaller than the right operand.
|
||||
Ascending = -1,
|
||||
/// The two operands are equal.
|
||||
Same = 0,
|
||||
/// The left operand is greater than the right operand.
|
||||
Descending = 1,
|
||||
}
|
||||
|
||||
impl Default for NSComparisonResult {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::Same
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSComparisonResult {
|
||||
const ENCODING: Encoding = isize::ENCODING;
|
||||
}
|
||||
|
||||
unsafe impl RefEncode for NSComparisonResult {
|
||||
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
|
||||
}
|
||||
|
||||
impl From<Ordering> for NSComparisonResult {
|
||||
#[inline]
|
||||
fn from(order: Ordering) -> Self {
|
||||
match order {
|
||||
Ordering::Less => Self::Ascending,
|
||||
Ordering::Equal => Self::Same,
|
||||
Ordering::Greater => Self::Descending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NSComparisonResult> for Ordering {
|
||||
#[inline]
|
||||
fn from(comparison_result: NSComparisonResult) -> Self {
|
||||
match comparison_result {
|
||||
NSComparisonResult::Ascending => Self::Less,
|
||||
NSComparisonResult::Same => Self::Equal,
|
||||
NSComparisonResult::Descending => Self::Greater,
|
||||
}
|
||||
}
|
||||
}
|
||||
48
third-party/vendor/objc2/src/foundation/copying.rs
vendored
Normal file
48
third-party/vendor/objc2/src/foundation/copying.rs
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
use crate::rc::{Id, Owned, Ownership};
|
||||
use crate::{msg_send_id, Message};
|
||||
|
||||
pub unsafe trait NSCopying: Message {
|
||||
/// Indicates whether the type is mutable or immutable.
|
||||
///
|
||||
/// This can be [`Owned`] if and only if `copy` creates a new instance,
|
||||
/// see the following example:
|
||||
///
|
||||
/// ```ignore
|
||||
/// let x: Id<MyObject, _> = MyObject::new();
|
||||
/// // This is valid only if `y` is a new instance. Otherwise `x` and `y`
|
||||
/// // would be able to create aliasing mutable references!
|
||||
/// let y: Id<MyObject, Owned> = x.copy();
|
||||
/// ```
|
||||
///
|
||||
/// Note that for the same reason, you should be careful when defining
|
||||
/// `new` methods on your object; e.g. immutable types like [`NSString`]
|
||||
/// don't return `Id<NSString, Owned>`, because that would allow this
|
||||
/// trait to create an aliasing `Id<NSString, Shared>` (since sending the
|
||||
/// `copy` message (and others) does not create a new instance, but
|
||||
/// instead just retains the instance).
|
||||
///
|
||||
/// [`NSString`]: crate::foundation::NSString
|
||||
type Ownership: Ownership;
|
||||
|
||||
/// The output type.
|
||||
///
|
||||
/// This is usually `Self`, but e.g. `NSMutableString` returns `NSString`.
|
||||
/// TODO: Verify???
|
||||
type Output: Message;
|
||||
|
||||
fn copy(&self) -> Id<Self::Output, Self::Ownership> {
|
||||
unsafe { msg_send_id![self, copy] }
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO
|
||||
///
|
||||
/// Note that the `mutableCopy` selector must return an owned object!
|
||||
pub unsafe trait NSMutableCopying: Message {
|
||||
/// TODO
|
||||
type Output: Message;
|
||||
|
||||
fn mutable_copy(&self) -> Id<Self::Output, Owned> {
|
||||
unsafe { msg_send_id![self, mutableCopy] }
|
||||
}
|
||||
}
|
||||
228
third-party/vendor/objc2/src/foundation/data.rs
vendored
Normal file
228
third-party/vendor/objc2/src/foundation/data.rs
vendored
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
#[cfg(feature = "block")]
|
||||
use alloc::vec::Vec;
|
||||
use core::ffi::c_void;
|
||||
use core::fmt;
|
||||
use core::ops::Index;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use core::slice::{self, SliceIndex};
|
||||
|
||||
use super::{NSCopying, NSMutableCopying, NSMutableData, NSObject};
|
||||
use crate::rc::{DefaultId, Id, Shared};
|
||||
use crate::runtime::{Class, Object};
|
||||
use crate::{extern_class, extern_methods, msg_send_id, ClassType};
|
||||
|
||||
extern_class!(
|
||||
/// A static byte buffer in memory.
|
||||
///
|
||||
/// This is similar to a [`slice`][`prim@slice`] of [`u8`].
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsdata?language=objc).
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct NSData;
|
||||
|
||||
unsafe impl ClassType for NSData {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
// SAFETY: `NSData` is immutable and `NSMutableData` can only be mutated from
|
||||
// `&mut` methods.
|
||||
unsafe impl Sync for NSData {}
|
||||
unsafe impl Send for NSData {}
|
||||
|
||||
impl UnwindSafe for NSData {}
|
||||
impl RefUnwindSafe for NSData {}
|
||||
|
||||
extern_methods!(
|
||||
/// Creation methods.
|
||||
unsafe impl NSData {
|
||||
pub fn new() -> Id<Self, Shared> {
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
pub fn with_bytes(bytes: &[u8]) -> Id<Self, Shared> {
|
||||
unsafe { Id::cast(with_slice(Self::class(), bytes)) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "block")]
|
||||
pub fn from_vec(bytes: Vec<u8>) -> Id<Self, Shared> {
|
||||
// GNUStep's NSData `initWithBytesNoCopy:length:deallocator:` has a
|
||||
// bug; it forgets to assign the input buffer and length to the
|
||||
// instance before it swizzles to NSDataWithDeallocatorBlock.
|
||||
// See https://github.com/gnustep/libs-base/pull/213
|
||||
// So we just use NSDataWithDeallocatorBlock directly.
|
||||
//
|
||||
// NSMutableData does not have this problem.
|
||||
#[cfg(feature = "gnustep-1-7")]
|
||||
let cls = crate::class!(NSDataWithDeallocatorBlock);
|
||||
#[cfg(not(feature = "gnustep-1-7"))]
|
||||
let cls = Self::class();
|
||||
|
||||
unsafe { Id::cast(with_vec(cls, bytes)) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Accessor methods.
|
||||
unsafe impl NSData {
|
||||
#[sel(length)]
|
||||
#[doc(alias = "length")]
|
||||
pub fn len(&self) -> usize;
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
#[sel(bytes)]
|
||||
fn bytes_raw(&self) -> *const c_void;
|
||||
|
||||
pub fn bytes(&self) -> &[u8] {
|
||||
let ptr = self.bytes_raw();
|
||||
let ptr: *const u8 = ptr.cast();
|
||||
// The bytes pointer may be null for length zero
|
||||
if ptr.is_null() {
|
||||
&[]
|
||||
} else {
|
||||
unsafe { slice::from_raw_parts(ptr, self.len()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
unsafe impl NSCopying for NSData {
|
||||
type Ownership = Shared;
|
||||
type Output = NSData;
|
||||
}
|
||||
|
||||
unsafe impl NSMutableCopying for NSData {
|
||||
type Output = NSMutableData;
|
||||
}
|
||||
|
||||
impl alloc::borrow::ToOwned for NSData {
|
||||
type Owned = Id<NSData, Shared>;
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
self.copy()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for NSData {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.bytes()
|
||||
}
|
||||
}
|
||||
|
||||
// Note: We don't implement `Borrow<[u8]>` since we can't guarantee that `Eq`,
|
||||
// `Ord` and `Hash` are equal for `NSData` vs. `[u8]`!
|
||||
|
||||
impl<I: SliceIndex<[u8]>> Index<I> for NSData {
|
||||
type Output = I::Output;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: I) -> &Self::Output {
|
||||
// Replaces the need for getBytes:range:
|
||||
Index::index(self.bytes(), index)
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultId for NSData {
|
||||
type Ownership = Shared;
|
||||
|
||||
#[inline]
|
||||
fn default_id() -> Id<Self, Self::Ownership> {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NSData {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// -[NSData description] is quite unreadable
|
||||
fmt::Debug::fmt(self.bytes(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a NSData {
|
||||
type Item = &'a u8;
|
||||
type IntoIter = core::slice::Iter<'a, u8>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.bytes().iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn with_slice(cls: &Class, bytes: &[u8]) -> Id<Object, Shared> {
|
||||
let bytes_ptr: *const c_void = bytes.as_ptr().cast();
|
||||
unsafe {
|
||||
msg_send_id![
|
||||
msg_send_id![cls, alloc],
|
||||
initWithBytes: bytes_ptr,
|
||||
length: bytes.len(),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "block")]
|
||||
pub(crate) unsafe fn with_vec(cls: &Class, bytes: Vec<u8>) -> Id<Object, Shared> {
|
||||
use core::mem::ManuallyDrop;
|
||||
|
||||
use block2::{Block, ConcreteBlock};
|
||||
|
||||
let capacity = bytes.capacity();
|
||||
|
||||
let dealloc = ConcreteBlock::new(move |bytes: *mut c_void, len: usize| unsafe {
|
||||
// Recreate the Vec and let it drop
|
||||
let _ = Vec::<u8>::from_raw_parts(bytes.cast(), len, capacity);
|
||||
});
|
||||
let dealloc = dealloc.copy();
|
||||
let dealloc: &Block<(*mut c_void, usize), ()> = &dealloc;
|
||||
|
||||
let mut bytes = ManuallyDrop::new(bytes);
|
||||
let bytes_ptr: *mut c_void = bytes.as_mut_ptr().cast();
|
||||
|
||||
unsafe {
|
||||
msg_send_id![
|
||||
msg_send_id![cls, alloc],
|
||||
initWithBytesNoCopy: bytes_ptr,
|
||||
length: bytes.len(),
|
||||
deallocator: dealloc,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use alloc::format;
|
||||
#[cfg(feature = "block")]
|
||||
use alloc::vec;
|
||||
|
||||
#[test]
|
||||
fn test_bytes() {
|
||||
let bytes = [3, 7, 16, 52, 112, 19];
|
||||
let data = NSData::with_bytes(&bytes);
|
||||
assert_eq!(data.len(), bytes.len());
|
||||
assert_eq!(data.bytes(), bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_bytes() {
|
||||
let data = NSData::new();
|
||||
assert!(Some(data.bytes()).is_some());
|
||||
}
|
||||
|
||||
#[cfg(feature = "block")]
|
||||
#[test]
|
||||
fn test_from_vec() {
|
||||
let bytes = vec![3, 7, 16];
|
||||
let bytes_ptr = bytes.as_ptr();
|
||||
|
||||
let data = NSData::from_vec(bytes);
|
||||
assert_eq!(data.bytes().as_ptr(), bytes_ptr);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let bytes = [3, 7, 16, 52, 112, 19];
|
||||
let data = NSData::with_bytes(&bytes);
|
||||
assert_eq!(format!("{:?}", data), "[3, 7, 16, 52, 112, 19]");
|
||||
}
|
||||
}
|
||||
264
third-party/vendor/objc2/src/foundation/dictionary.rs
vendored
Normal file
264
third-party/vendor/objc2/src/foundation/dictionary.rs
vendored
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
use alloc::vec::Vec;
|
||||
use core::cmp::min;
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::Index;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use core::ptr;
|
||||
|
||||
use super::{NSArray, NSCopying, NSEnumerator, NSFastEnumeration, NSObject};
|
||||
use crate::rc::{DefaultId, Id, Owned, Shared, SliceId};
|
||||
use crate::{ClassType, __inner_extern_class, extern_methods, msg_send, msg_send_id, Message};
|
||||
|
||||
__inner_extern_class!(
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct NSDictionary<K: Message, V: Message> {
|
||||
key: PhantomData<Id<K, Shared>>,
|
||||
obj: PhantomData<Id<V, Owned>>,
|
||||
}
|
||||
|
||||
unsafe impl<K: Message, V: Message> ClassType for NSDictionary<K, V> {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
// TODO: SAFETY
|
||||
// Approximately same as `NSArray<T, Shared>`
|
||||
unsafe impl<K: Message + Sync + Send, V: Message + Sync> Sync for NSDictionary<K, V> {}
|
||||
unsafe impl<K: Message + Sync + Send, V: Message + Send> Send for NSDictionary<K, V> {}
|
||||
|
||||
// Approximately same as `NSArray<T, Shared>`
|
||||
impl<K: Message + UnwindSafe, V: Message + UnwindSafe> UnwindSafe for NSDictionary<K, V> {}
|
||||
impl<K: Message + RefUnwindSafe, V: Message + RefUnwindSafe> RefUnwindSafe for NSDictionary<K, V> {}
|
||||
extern_methods!(
|
||||
unsafe impl<K: Message, V: Message> NSDictionary<K, V> {
|
||||
pub fn new() -> Id<Self, Shared> {
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
#[doc(alias = "count")]
|
||||
#[sel(count)]
|
||||
pub fn len(&self) -> usize;
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
#[doc(alias = "objectForKey:")]
|
||||
#[sel(objectForKey:)]
|
||||
pub fn get(&self, key: &K) -> Option<&V>;
|
||||
|
||||
#[sel(getObjects:andKeys:)]
|
||||
unsafe fn get_objects_and_keys(&self, objects: *mut &V, keys: *mut &K);
|
||||
|
||||
#[doc(alias = "getObjects:andKeys:")]
|
||||
pub fn keys(&self) -> Vec<&K> {
|
||||
let len = self.len();
|
||||
let mut keys = Vec::with_capacity(len);
|
||||
unsafe {
|
||||
self.get_objects_and_keys(ptr::null_mut(), keys.as_mut_ptr());
|
||||
keys.set_len(len);
|
||||
}
|
||||
keys
|
||||
}
|
||||
|
||||
#[doc(alias = "getObjects:andKeys:")]
|
||||
pub fn values(&self) -> Vec<&V> {
|
||||
let len = self.len();
|
||||
let mut vals = Vec::with_capacity(len);
|
||||
unsafe {
|
||||
self.get_objects_and_keys(vals.as_mut_ptr(), ptr::null_mut());
|
||||
vals.set_len(len);
|
||||
}
|
||||
vals
|
||||
}
|
||||
|
||||
#[doc(alias = "getObjects:andKeys:")]
|
||||
pub fn keys_and_objects(&self) -> (Vec<&K>, Vec<&V>) {
|
||||
let len = self.len();
|
||||
let mut keys = Vec::with_capacity(len);
|
||||
let mut objs = Vec::with_capacity(len);
|
||||
unsafe {
|
||||
self.get_objects_and_keys(objs.as_mut_ptr(), keys.as_mut_ptr());
|
||||
keys.set_len(len);
|
||||
objs.set_len(len);
|
||||
}
|
||||
(keys, objs)
|
||||
}
|
||||
|
||||
#[doc(alias = "keyEnumerator")]
|
||||
pub fn iter_keys(&self) -> NSEnumerator<'_, K> {
|
||||
unsafe {
|
||||
let result = msg_send![self, keyEnumerator];
|
||||
NSEnumerator::from_ptr(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "objectEnumerator")]
|
||||
pub fn iter_values(&self) -> NSEnumerator<'_, V> {
|
||||
unsafe {
|
||||
let result = msg_send![self, objectEnumerator];
|
||||
NSEnumerator::from_ptr(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keys_array(&self) -> Id<NSArray<K, Shared>, Shared> {
|
||||
unsafe { msg_send_id![self, allKeys] }
|
||||
}
|
||||
|
||||
pub fn from_keys_and_objects<T>(keys: &[&T], vals: Vec<Id<V, Owned>>) -> Id<Self, Shared>
|
||||
where
|
||||
T: NSCopying<Output = K>,
|
||||
{
|
||||
let vals = vals.as_slice_ref();
|
||||
|
||||
let cls = Self::class();
|
||||
let count = min(keys.len(), vals.len());
|
||||
let obj = unsafe { msg_send_id![cls, alloc] };
|
||||
unsafe {
|
||||
msg_send_id![
|
||||
obj,
|
||||
initWithObjects: vals.as_ptr(),
|
||||
forKeys: keys.as_ptr(),
|
||||
count: count,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_values_array(dict: Id<Self, Owned>) -> Id<NSArray<V, Owned>, Shared> {
|
||||
unsafe { msg_send_id![&dict, allValues] }
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl<K: Message, V: Message> DefaultId for NSDictionary<K, V> {
|
||||
type Ownership = Shared;
|
||||
|
||||
#[inline]
|
||||
fn default_id() -> Id<Self, Self::Ownership> {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<K: Message, V: Message> NSFastEnumeration for NSDictionary<K, V> {
|
||||
type Item = K;
|
||||
}
|
||||
|
||||
impl<'a, K: Message, V: Message> Index<&'a K> for NSDictionary<K, V> {
|
||||
type Output = V;
|
||||
|
||||
fn index<'s>(&'s self, index: &'a K) -> &'s V {
|
||||
self.get(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: fmt::Debug + Message, V: fmt::Debug + Message> fmt::Debug for NSDictionary<K, V> {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let iter = self.iter_keys().zip(self.iter_values());
|
||||
f.debug_map().entries(iter).finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::format;
|
||||
use alloc::vec;
|
||||
|
||||
use super::*;
|
||||
use crate::foundation::NSString;
|
||||
use crate::rc::autoreleasepool;
|
||||
|
||||
fn sample_dict(key: &str) -> Id<NSDictionary<NSString, NSObject>, Shared> {
|
||||
let string = NSString::from_str(key);
|
||||
let obj = NSObject::new();
|
||||
NSDictionary::from_keys_and_objects(&[&*string], vec![obj])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_len() {
|
||||
let dict = sample_dict("abcd");
|
||||
assert_eq!(dict.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get() {
|
||||
let dict = sample_dict("abcd");
|
||||
|
||||
let string = NSString::from_str("abcd");
|
||||
assert!(dict.get(&string).is_some());
|
||||
|
||||
let string = NSString::from_str("abcde");
|
||||
assert!(dict.get(&string).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keys() {
|
||||
let dict = sample_dict("abcd");
|
||||
let keys = dict.keys();
|
||||
|
||||
assert_eq!(keys.len(), 1);
|
||||
autoreleasepool(|pool| {
|
||||
assert_eq!(keys[0].as_str(pool), "abcd");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_values() {
|
||||
let dict = sample_dict("abcd");
|
||||
let vals = dict.values();
|
||||
|
||||
assert_eq!(vals.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keys_and_objects() {
|
||||
let dict = sample_dict("abcd");
|
||||
let (keys, objs) = dict.keys_and_objects();
|
||||
|
||||
assert_eq!(keys.len(), 1);
|
||||
assert_eq!(objs.len(), 1);
|
||||
autoreleasepool(|pool| {
|
||||
assert_eq!(keys[0].as_str(pool), "abcd");
|
||||
});
|
||||
assert_eq!(objs[0], dict.get(keys[0]).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iter_keys() {
|
||||
let dict = sample_dict("abcd");
|
||||
assert_eq!(dict.iter_keys().count(), 1);
|
||||
autoreleasepool(|pool| {
|
||||
assert_eq!(dict.iter_keys().next().unwrap().as_str(pool), "abcd");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iter_values() {
|
||||
let dict = sample_dict("abcd");
|
||||
assert_eq!(dict.iter_values().count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arrays() {
|
||||
let dict = sample_dict("abcd");
|
||||
|
||||
let keys = dict.keys_array();
|
||||
assert_eq!(keys.len(), 1);
|
||||
autoreleasepool(|pool| {
|
||||
assert_eq!(keys[0].as_str(pool), "abcd");
|
||||
});
|
||||
|
||||
// let objs = NSDictionary::into_values_array(dict);
|
||||
// assert_eq!(objs.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let key = NSString::from_str("a");
|
||||
// TODO: Fix this
|
||||
let val = unsafe { Id::from_shared(NSString::from_str("b")) };
|
||||
let dict = NSDictionary::from_keys_and_objects(&[&*key], vec![val]);
|
||||
assert_eq!(format!("{:?}", dict), r#"{"a": "b"}"#);
|
||||
}
|
||||
}
|
||||
196
third-party/vendor/objc2/src/foundation/enumerator.rs
vendored
Normal file
196
third-party/vendor/objc2/src/foundation/enumerator.rs
vendored
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
use core::marker::PhantomData;
|
||||
use core::mem;
|
||||
use core::ptr;
|
||||
use core::slice;
|
||||
use std::os::raw::c_ulong;
|
||||
|
||||
use crate::rc::{Id, Owned};
|
||||
use crate::runtime::Object;
|
||||
use crate::{msg_send, Encode, Encoding, Message, RefEncode};
|
||||
|
||||
// TODO: https://doc.rust-lang.org/stable/reference/trait-bounds.html#lifetime-bounds
|
||||
pub struct NSEnumerator<'a, T: Message> {
|
||||
id: Id<Object, Owned>,
|
||||
item: PhantomData<&'a T>,
|
||||
}
|
||||
|
||||
impl<'a, T: Message> NSEnumerator<'a, T> {
|
||||
/// TODO
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The object pointer must be a valid `NSEnumerator` with `Owned`
|
||||
/// ownership.
|
||||
pub unsafe fn from_ptr(ptr: *mut Object) -> Self {
|
||||
Self {
|
||||
id: unsafe { Id::retain_autoreleased(ptr) }.unwrap(),
|
||||
item: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Message> Iterator for NSEnumerator<'a, T> {
|
||||
type Item = &'a T;
|
||||
|
||||
fn next(&mut self) -> Option<&'a T> {
|
||||
unsafe { msg_send![&mut self.id, nextObject] }
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe trait NSFastEnumeration: Message {
|
||||
type Item: Message;
|
||||
|
||||
fn iter_fast(&self) -> NSFastEnumerator<'_, Self> {
|
||||
NSFastEnumerator::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct NSFastEnumerationState<T: Message> {
|
||||
state: c_ulong, // TODO: Verify this is actually always 64 bit
|
||||
items_ptr: *const *const T,
|
||||
mutations_ptr: *mut c_ulong,
|
||||
extra: [c_ulong; 5],
|
||||
}
|
||||
|
||||
unsafe impl<T: Message> Encode for NSFastEnumerationState<T> {
|
||||
const ENCODING: Encoding = Encoding::Struct(
|
||||
"?",
|
||||
&[
|
||||
Encoding::C_ULONG,
|
||||
Encoding::Pointer(&Encoding::Object), // <*const *const T>::ENCODING
|
||||
Encoding::Pointer(&Encoding::C_ULONG),
|
||||
Encoding::Array(5, &Encoding::C_ULONG),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
unsafe impl<T: Message> RefEncode for NSFastEnumerationState<T> {
|
||||
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
|
||||
}
|
||||
|
||||
fn enumerate<'a, 'b: 'a, C: NSFastEnumeration + ?Sized>(
|
||||
object: &'b C,
|
||||
state: &mut NSFastEnumerationState<C::Item>,
|
||||
buf: &'a mut [*const C::Item],
|
||||
) -> Option<&'a [*const C::Item]> {
|
||||
let count: usize = unsafe {
|
||||
// Reborrow state so that we don't move it
|
||||
let state = &mut *state;
|
||||
msg_send![
|
||||
object,
|
||||
countByEnumeratingWithState: state,
|
||||
objects: buf.as_mut_ptr(),
|
||||
count: buf.len(),
|
||||
]
|
||||
};
|
||||
|
||||
if count > 0 {
|
||||
unsafe { Some(slice::from_raw_parts(state.items_ptr, count)) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
const FAST_ENUM_BUF_SIZE: usize = 16;
|
||||
|
||||
pub struct NSFastEnumerator<'a, C: 'a + NSFastEnumeration + ?Sized> {
|
||||
object: &'a C,
|
||||
|
||||
ptr: *const *const C::Item,
|
||||
end: *const *const C::Item,
|
||||
|
||||
state: NSFastEnumerationState<C::Item>,
|
||||
buf: [*const C::Item; FAST_ENUM_BUF_SIZE],
|
||||
}
|
||||
|
||||
impl<'a, C: NSFastEnumeration + ?Sized> NSFastEnumerator<'a, C> {
|
||||
fn new(object: &'a C) -> Self {
|
||||
Self {
|
||||
object,
|
||||
|
||||
ptr: ptr::null(),
|
||||
end: ptr::null(),
|
||||
|
||||
state: unsafe { mem::zeroed() },
|
||||
buf: [ptr::null(); FAST_ENUM_BUF_SIZE],
|
||||
}
|
||||
}
|
||||
|
||||
fn update_buf(&mut self) -> bool {
|
||||
// If this isn't our first time enumerating, record the previous value
|
||||
// from the mutations pointer.
|
||||
let mutations = if !self.ptr.is_null() {
|
||||
Some(unsafe { *self.state.mutations_ptr })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let next_buf = enumerate(self.object, &mut self.state, &mut self.buf);
|
||||
if let Some(buf) = next_buf {
|
||||
// Check if the collection was mutated
|
||||
if let Some(mutations) = mutations {
|
||||
assert_eq!(
|
||||
mutations,
|
||||
unsafe { *self.state.mutations_ptr },
|
||||
"Mutation detected during enumeration of object {:p}",
|
||||
self.object
|
||||
);
|
||||
}
|
||||
|
||||
self.ptr = buf.as_ptr();
|
||||
self.end = unsafe { self.ptr.add(buf.len()) };
|
||||
true
|
||||
} else {
|
||||
self.ptr = ptr::null();
|
||||
self.end = ptr::null();
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: NSFastEnumeration + ?Sized> Iterator for NSFastEnumerator<'a, C> {
|
||||
type Item = &'a C::Item;
|
||||
|
||||
fn next(&mut self) -> Option<&'a C::Item> {
|
||||
if self.ptr == self.end && !self.update_buf() {
|
||||
None
|
||||
} else {
|
||||
unsafe {
|
||||
let obj = *self.ptr;
|
||||
self.ptr = self.ptr.offset(1);
|
||||
Some(obj.as_ref().unwrap_unchecked())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::NSFastEnumeration;
|
||||
use crate::foundation::{NSArray, NSNumber};
|
||||
|
||||
#[test]
|
||||
fn test_enumerator() {
|
||||
let vec = (0..4).map(NSNumber::new_usize).collect();
|
||||
let array = NSArray::from_vec(vec);
|
||||
|
||||
let enumerator = array.iter();
|
||||
assert_eq!(enumerator.count(), 4);
|
||||
|
||||
let enumerator = array.iter();
|
||||
assert!(enumerator.enumerate().all(|(i, obj)| obj.as_usize() == i));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fast_enumerator() {
|
||||
let vec = (0..4).map(NSNumber::new_usize).collect();
|
||||
let array = NSArray::from_vec(vec);
|
||||
|
||||
let enumerator = array.iter_fast();
|
||||
assert_eq!(enumerator.count(), 4);
|
||||
|
||||
let enumerator = array.iter_fast();
|
||||
assert!(enumerator.enumerate().all(|(i, obj)| obj.as_usize() == i));
|
||||
}
|
||||
}
|
||||
165
third-party/vendor/objc2/src/foundation/error.rs
vendored
Normal file
165
third-party/vendor/objc2/src/foundation/error.rs
vendored
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
use core::fmt;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
use super::{NSCopying, NSDictionary, NSObject, NSString};
|
||||
use crate::ffi::NSInteger;
|
||||
use crate::rc::{Id, Shared};
|
||||
use crate::{extern_class, extern_methods, msg_send_id, ClassType};
|
||||
|
||||
extern_class!(
|
||||
/// Information about an error condition including a domain, a
|
||||
/// domain-specific error code, and application-specific information.
|
||||
///
|
||||
/// See also Apple's [documentation on error handling][err], and their
|
||||
/// NSError [API reference][api].
|
||||
///
|
||||
/// [err]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ErrorHandlingCocoa/ErrorHandling/ErrorHandling.html#//apple_ref/doc/uid/TP40001806-CH201-SW1
|
||||
/// [api]: https://developer.apple.com/documentation/foundation/nserror?language=objc
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct NSError;
|
||||
|
||||
unsafe impl ClassType for NSError {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
// SAFETY: Error objects are immutable data containers.
|
||||
unsafe impl Sync for NSError {}
|
||||
unsafe impl Send for NSError {}
|
||||
|
||||
impl UnwindSafe for NSError {}
|
||||
impl RefUnwindSafe for NSError {}
|
||||
|
||||
pub type NSErrorUserInfoKey = NSString;
|
||||
pub type NSErrorDomain = NSString;
|
||||
|
||||
extern_methods!(
|
||||
/// Creation methods.
|
||||
unsafe impl NSError {
|
||||
/// Construct a new [`NSError`] with the given code in the given domain.
|
||||
pub fn new(code: NSInteger, domain: &NSString) -> Id<Self, Shared> {
|
||||
unsafe { Self::with_user_info(code, domain, None) }
|
||||
}
|
||||
|
||||
// TODO: Figure out safety of `user_info` dict!
|
||||
unsafe fn with_user_info(
|
||||
code: NSInteger,
|
||||
domain: &NSString,
|
||||
user_info: Option<&NSDictionary<NSErrorUserInfoKey, NSObject>>,
|
||||
) -> Id<Self, Shared> {
|
||||
// SAFETY: `domain` and `user_info` are copied to the error object, so
|
||||
// even if the `&NSString` came from a `&mut NSMutableString`, we're
|
||||
// still good!
|
||||
unsafe {
|
||||
msg_send_id![
|
||||
msg_send_id![Self::class(), alloc],
|
||||
initWithDomain: domain,
|
||||
code: code,
|
||||
userInfo: user_info,
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Accessor methods.
|
||||
unsafe impl NSError {
|
||||
pub fn domain(&self) -> Id<NSString, Shared> {
|
||||
unsafe { msg_send_id![self, domain] }
|
||||
}
|
||||
|
||||
#[sel(code)]
|
||||
pub fn code(&self) -> NSInteger;
|
||||
|
||||
pub fn user_info(&self) -> Option<Id<NSDictionary<NSErrorUserInfoKey, NSObject>, Shared>> {
|
||||
unsafe { msg_send_id![self, userInfo] }
|
||||
}
|
||||
|
||||
pub fn localized_description(&self) -> Id<NSString, Shared> {
|
||||
// TODO: For some reason this leaks a lot?
|
||||
let obj: Option<_> = unsafe { msg_send_id![self, localizedDescription] };
|
||||
obj.expect(
|
||||
"unexpected NULL localized description; a default should have been generated!",
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: localizedRecoveryOptions
|
||||
// TODO: localizedRecoverySuggestion
|
||||
// TODO: localizedFailureReason
|
||||
// TODO: helpAnchor
|
||||
// TODO: +setUserInfoValueProviderForDomain:provider:
|
||||
// TODO: +userInfoValueProviderForDomain:
|
||||
|
||||
// TODO: recoveryAttempter
|
||||
// TODO: attemptRecoveryFromError:...
|
||||
|
||||
// TODO: Figure out if this is a good design, or if we should do something
|
||||
// differently (like a Rusty name for the function, or putting a bunch of
|
||||
// statics in a module instead)?
|
||||
#[allow(non_snake_case)]
|
||||
pub fn NSLocalizedDescriptionKey() -> &'static NSErrorUserInfoKey {
|
||||
extern "C" {
|
||||
#[link_name = "NSLocalizedDescriptionKey"]
|
||||
static VALUE: &'static NSErrorUserInfoKey;
|
||||
}
|
||||
unsafe { VALUE }
|
||||
}
|
||||
|
||||
// TODO: Other NSErrorUserInfoKey values
|
||||
// TODO: NSErrorDomain values
|
||||
}
|
||||
);
|
||||
|
||||
impl fmt::Debug for NSError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("NSError")
|
||||
.field("domain", &self.domain())
|
||||
.field("code", &self.code())
|
||||
.field("user_info", &self.user_info())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NSError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.localized_description())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for NSError {}
|
||||
|
||||
unsafe impl NSCopying for NSError {
|
||||
type Ownership = Shared;
|
||||
type Output = Self;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use alloc::format;
|
||||
|
||||
use crate::ns_string;
|
||||
|
||||
#[test]
|
||||
fn custom_domain() {
|
||||
let error = NSError::new(42, ns_string!("MyDomain"));
|
||||
assert_eq!(error.code(), 42);
|
||||
assert_eq!(&*error.domain(), ns_string!("MyDomain"));
|
||||
let expected = if cfg!(feature = "apple") {
|
||||
"The operation couldn’t be completed. (MyDomain error 42.)"
|
||||
} else {
|
||||
"MyDomain 42"
|
||||
};
|
||||
assert_eq!(format!("{}", error), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
let error = NSError::new(-999, ns_string!("NSURLErrorDomain"));
|
||||
let expected = if cfg!(feature = "apple") {
|
||||
"The operation couldn’t be completed. (NSURLErrorDomain error -999.)"
|
||||
} else {
|
||||
"NSURLErrorDomain -999"
|
||||
};
|
||||
assert_eq!(format!("{}", error), expected);
|
||||
}
|
||||
}
|
||||
200
third-party/vendor/objc2/src/foundation/exception.rs
vendored
Normal file
200
third-party/vendor/objc2/src/foundation/exception.rs
vendored
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
use core::fmt;
|
||||
use core::hint::unreachable_unchecked;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
use super::{NSCopying, NSDictionary, NSObject, NSString};
|
||||
use crate::exception::Exception;
|
||||
use crate::rc::{Id, Shared};
|
||||
use crate::runtime::Object;
|
||||
use crate::{extern_class, extern_methods, msg_send_id, sel, ClassType};
|
||||
|
||||
extern_class!(
|
||||
/// A special condition that interrupts the normal flow of program
|
||||
/// execution.
|
||||
///
|
||||
/// Exceptions can be thrown and caught using the `objc2::exception`
|
||||
/// module.
|
||||
///
|
||||
/// See also [Apple's documentation][doc].
|
||||
///
|
||||
/// [doc]: https://developer.apple.com/documentation/foundation/nsexception?language=objc
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct NSException;
|
||||
|
||||
unsafe impl ClassType for NSException {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
// SAFETY: Exception objects are immutable data containers, and documented as
|
||||
// thread safe.
|
||||
unsafe impl Sync for NSException {}
|
||||
unsafe impl Send for NSException {}
|
||||
|
||||
impl UnwindSafe for NSException {}
|
||||
impl RefUnwindSafe for NSException {}
|
||||
|
||||
type NSExceptionName = NSString;
|
||||
extern_methods!(
|
||||
unsafe impl NSException {
|
||||
/// Create a new [`NSException`] object.
|
||||
///
|
||||
/// Returns `None` if the exception couldn't be created (example: If the
|
||||
/// process is out of memory).
|
||||
pub fn new(
|
||||
name: &NSExceptionName,
|
||||
reason: Option<&NSString>,
|
||||
user_info: Option<&NSDictionary<Object, Object>>,
|
||||
) -> Option<Id<Self, Shared>> {
|
||||
let obj = unsafe { msg_send_id![Self::class(), alloc] };
|
||||
unsafe { msg_send_id![obj, initWithName: name, reason: reason, userInfo: user_info] }
|
||||
}
|
||||
|
||||
#[sel(raise)]
|
||||
unsafe fn raise_raw(&self);
|
||||
|
||||
/// Raises the exception, causing program flow to jump to the local
|
||||
/// exception handler.
|
||||
///
|
||||
/// This is equivalent to using `objc2::exception::throw`.
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Same as `objc2::exception::throw`.
|
||||
pub unsafe fn raise(&self) -> ! {
|
||||
// SAFETY: We only create `Shared` NSExceptions, so it is safe to give
|
||||
// to the place where `@catch` receives it.
|
||||
unsafe { self.raise_raw() };
|
||||
// SAFETY: `raise` will throw an exception, or abort if something
|
||||
// unexpected happened.
|
||||
unsafe { unreachable_unchecked() }
|
||||
}
|
||||
|
||||
/// A that uniquely identifies the type of exception.
|
||||
///
|
||||
/// See [Apple's documentation][doc] for some of the different values this
|
||||
/// can take.
|
||||
///
|
||||
/// [doc]: https://developer.apple.com/documentation/foundation/nsexceptionname?language=objc
|
||||
pub fn name(&self) -> Id<NSExceptionName, Shared> {
|
||||
// Nullability not documented, but a name is expected in most places.
|
||||
unsafe { msg_send_id![self, name] }
|
||||
}
|
||||
|
||||
/// A human-readable message summarizing the reason for the exception.
|
||||
pub fn reason(&self) -> Option<Id<NSString, Shared>> {
|
||||
unsafe { msg_send_id![self, reason] }
|
||||
}
|
||||
|
||||
/// Application-specific data pertaining to the exception.
|
||||
pub fn user_info(&self) -> Option<Id<NSDictionary<Object, Object>, Shared>> {
|
||||
unsafe { msg_send_id![self, userInfo] }
|
||||
}
|
||||
|
||||
/// Convert this into an [`Exception`] object.
|
||||
pub fn into_exception(this: Id<Self, Shared>) -> Id<Exception, Shared> {
|
||||
// SAFETY: Downcasting to "subclass"
|
||||
unsafe { Id::cast(this) }
|
||||
}
|
||||
|
||||
pub(crate) fn is_nsexception(obj: &Exception) -> bool {
|
||||
if obj.class().responds_to(sel!(isKindOfClass:)) {
|
||||
// SAFETY: We only use `isKindOfClass:` on NSObject
|
||||
let obj: *const Exception = obj;
|
||||
let obj = unsafe { obj.cast::<NSObject>().as_ref().unwrap() };
|
||||
obj.is_kind_of::<Self>()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Create this from an [`Exception`] object.
|
||||
///
|
||||
/// This should be considered a hint; it may return `Err` in very, very
|
||||
/// few cases where the object is actually an instance of `NSException`.
|
||||
pub fn from_exception(
|
||||
obj: Id<Exception, Shared>,
|
||||
) -> Result<Id<Self, Shared>, Id<Exception, Shared>> {
|
||||
if Self::is_nsexception(&obj) {
|
||||
// SAFETY: Just checked the object is an NSException
|
||||
Ok(unsafe { Id::cast::<Self>(obj) })
|
||||
} else {
|
||||
Err(obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
unsafe impl NSCopying for NSException {
|
||||
type Ownership = Shared;
|
||||
type Output = NSException;
|
||||
}
|
||||
|
||||
impl alloc::borrow::ToOwned for NSException {
|
||||
type Owned = Id<NSException, Shared>;
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
self.copy()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NSException {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let obj: &Object = self.as_ref();
|
||||
write!(f, "{:?} '{}'", obj, self.name())?;
|
||||
if let Some(reason) = self.reason() {
|
||||
write!(f, " reason:{}", reason)?;
|
||||
} else {
|
||||
write!(f, " reason:(NULL)")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::format;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn create_and_query() {
|
||||
let exc = NSException::new(
|
||||
&NSString::from_str("abc"),
|
||||
Some(&NSString::from_str("def")),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(exc.name(), NSString::from_str("abc"));
|
||||
assert_eq!(exc.reason().unwrap(), NSString::from_str("def"));
|
||||
assert!(exc.user_info().is_none());
|
||||
|
||||
let debug = format!("<NSException: {:p}> 'abc' reason:def", exc);
|
||||
assert_eq!(format!("{:?}", exc), debug);
|
||||
|
||||
let description = if cfg!(feature = "gnustep-1-7") {
|
||||
format!("<NSException: {:p}> NAME:abc REASON:def", exc)
|
||||
} else {
|
||||
"def".into()
|
||||
};
|
||||
|
||||
let exc: &NSObject = &exc;
|
||||
assert_eq!(format!("{:?}", exc), description);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "'abc' reason:def"]
|
||||
fn unwrap() {
|
||||
let exc = NSException::new(
|
||||
&NSString::from_str("abc"),
|
||||
Some(&NSString::from_str("def")),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let _: () = Err(exc).unwrap();
|
||||
}
|
||||
|
||||
// Further tests in `tests::exception`
|
||||
}
|
||||
446
third-party/vendor/objc2/src/foundation/geometry.rs
vendored
Normal file
446
third-party/vendor/objc2/src/foundation/geometry.rs
vendored
Normal file
|
|
@ -0,0 +1,446 @@
|
|||
use crate::{Encode, Encoding, RefEncode};
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
type InnerFloat = f64;
|
||||
#[cfg(not(target_pointer_width = "64"))]
|
||||
type InnerFloat = f32;
|
||||
|
||||
/// The basic type for all floating-point values.
|
||||
///
|
||||
/// This is [`f32`] on 32-bit platforms and [`f64`] on 64-bit platforms.
|
||||
///
|
||||
/// This technically belongs to the `CoreGraphics` framework, but we define it
|
||||
/// here for convenience.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/coregraphics/cgfloat?language=objc).
|
||||
// Defined in CoreGraphics/CGBase.h
|
||||
// TODO: Use a newtype here?
|
||||
pub type CGFloat = InnerFloat;
|
||||
|
||||
// NSGeometry types are just aliases to CGGeometry types on iOS, tvOS, watchOS
|
||||
// and macOS 64bit (and hence their Objective-C encodings are different).
|
||||
//
|
||||
// TODO: Adjust `objc2-encode` so that this is handled there, and so that we
|
||||
// can effectively just forget about it and use `NS` and `CG` types equally.
|
||||
#[cfg(all(
|
||||
feature = "apple",
|
||||
not(all(target_os = "macos", target_pointer_width = "32"))
|
||||
))]
|
||||
mod names {
|
||||
pub(super) const POINT: &str = "CGPoint";
|
||||
pub(super) const SIZE: &str = "CGSize";
|
||||
pub(super) const RECT: &str = "CGRect";
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
feature = "gnustep-1-7",
|
||||
all(target_os = "macos", target_pointer_width = "32")
|
||||
))]
|
||||
mod names {
|
||||
pub(super) const POINT: &str = "_NSPoint";
|
||||
pub(super) const SIZE: &str = "_NSSize";
|
||||
pub(super) const RECT: &str = "_NSRect";
|
||||
}
|
||||
|
||||
/// A point in a two-dimensional coordinate system.
|
||||
///
|
||||
/// This technically belongs to the `CoreGraphics` framework, but we define it
|
||||
/// here for convenience.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/corefoundation/cgpoint?language=objc).
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Default)]
|
||||
pub struct CGPoint {
|
||||
/// The x-coordinate of the point.
|
||||
pub x: CGFloat,
|
||||
/// The y-coordinate of the point.
|
||||
pub y: CGFloat,
|
||||
}
|
||||
|
||||
unsafe impl Encode for CGPoint {
|
||||
const ENCODING: Encoding =
|
||||
Encoding::Struct(names::POINT, &[CGFloat::ENCODING, CGFloat::ENCODING]);
|
||||
}
|
||||
|
||||
unsafe impl RefEncode for CGPoint {
|
||||
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
|
||||
}
|
||||
|
||||
impl CGPoint {
|
||||
/// Create a new point with the given coordinates.
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::CGPoint;
|
||||
/// assert_eq!(CGPoint::new(10.0, -2.3), CGPoint { x: 10.0, y: -2.3 });
|
||||
/// ```
|
||||
#[inline]
|
||||
#[doc(alias = "NSMakePoint")]
|
||||
#[doc(alias = "CGPointMake")]
|
||||
pub const fn new(x: CGFloat, y: CGFloat) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
|
||||
/// A point with both coordinates set to `0.0`.
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::CGPoint;
|
||||
/// assert_eq!(CGPoint::ZERO, CGPoint { x: 0.0, y: 0.0 });
|
||||
/// ```
|
||||
#[doc(alias = "NSZeroPoint")]
|
||||
#[doc(alias = "CGPointZero")]
|
||||
#[doc(alias = "ORIGIN")]
|
||||
pub const ZERO: Self = Self::new(0.0, 0.0);
|
||||
}
|
||||
|
||||
/// A two-dimensional size.
|
||||
///
|
||||
/// As this is sometimes used to represent a distance vector, rather than a
|
||||
/// physical size, the width and height are _not_ guaranteed to be
|
||||
/// non-negative! Methods that expect that must use one of [`CGSize::abs`] or
|
||||
/// [`CGRect::standardize`].
|
||||
///
|
||||
/// This technically belongs to the `CoreGraphics` framework, but we define it
|
||||
/// here for convenience.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/corefoundation/cgsize?language=objc).
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Default)]
|
||||
pub struct CGSize {
|
||||
/// The dimensions along the x-axis.
|
||||
pub width: CGFloat,
|
||||
/// The dimensions along the y-axis.
|
||||
pub height: CGFloat,
|
||||
}
|
||||
|
||||
unsafe impl Encode for CGSize {
|
||||
const ENCODING: Encoding =
|
||||
Encoding::Struct(names::SIZE, &[CGFloat::ENCODING, CGFloat::ENCODING]);
|
||||
}
|
||||
|
||||
unsafe impl RefEncode for CGSize {
|
||||
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
|
||||
}
|
||||
|
||||
impl CGSize {
|
||||
/// Create a new size with the given dimensions.
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::CGSize;
|
||||
/// let size = CGSize::new(10.0, 2.3);
|
||||
/// assert_eq!(size.width, 10.0);
|
||||
/// assert_eq!(size.height, 2.3);
|
||||
/// ```
|
||||
///
|
||||
/// Negative values are allowed (though often undesired).
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::CGSize;
|
||||
/// let size = CGSize::new(-1.0, 0.0);
|
||||
/// assert_eq!(size.width, -1.0);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[doc(alias = "NSMakeSize")]
|
||||
#[doc(alias = "CGSizeMake")]
|
||||
pub const fn new(width: CGFloat, height: CGFloat) -> Self {
|
||||
// The documentation for NSSize explicitly says:
|
||||
// > If the value of width or height is negative, however, the
|
||||
// > behavior of some methods may be undefined.
|
||||
//
|
||||
// But since this type can come from FFI, we'll leave it up to the
|
||||
// user to ensure that it is used safely.
|
||||
Self { width, height }
|
||||
}
|
||||
|
||||
/// Convert the size to a non-negative size.
|
||||
///
|
||||
/// This can be used to convert the size to a safe value.
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::CGSize;
|
||||
/// assert_eq!(CGSize::new(-1.0, 1.0).abs(), CGSize::new(1.0, 1.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn abs(self) -> Self {
|
||||
Self::new(self.width.abs(), self.height.abs())
|
||||
}
|
||||
|
||||
/// A size that is 0.0 in both dimensions.
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::CGSize;
|
||||
/// assert_eq!(CGSize::ZERO, CGSize { width: 0.0, height: 0.0 });
|
||||
/// ```
|
||||
#[doc(alias = "NSZeroSize")]
|
||||
#[doc(alias = "CGSizeZero")]
|
||||
pub const ZERO: Self = Self::new(0.0, 0.0);
|
||||
}
|
||||
|
||||
/// The location and dimensions of a rectangle.
|
||||
///
|
||||
/// In the default Core Graphics coordinate space (macOS), the origin is
|
||||
/// located in the lower-left corner of the rectangle and the rectangle
|
||||
/// extends towards the upper-right corner.
|
||||
///
|
||||
/// If the context has a flipped coordinate space (iOS, tvOS, watchOS) the
|
||||
/// origin is in the upper-left corner and the rectangle extends towards the
|
||||
/// lower-right corner.
|
||||
///
|
||||
/// This technically belongs to the `CoreGraphics` framework, but we define it
|
||||
/// here for convenience.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/corefoundation/cgrect?language=objc).
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Default)]
|
||||
pub struct CGRect {
|
||||
/// The coordinates of the rectangle’s origin.
|
||||
pub origin: CGPoint,
|
||||
/// The dimensions of the rectangle.
|
||||
pub size: CGSize,
|
||||
}
|
||||
|
||||
unsafe impl Encode for CGRect {
|
||||
const ENCODING: Encoding =
|
||||
Encoding::Struct(names::RECT, &[CGPoint::ENCODING, CGSize::ENCODING]);
|
||||
}
|
||||
|
||||
unsafe impl RefEncode for CGRect {
|
||||
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
|
||||
}
|
||||
|
||||
impl CGRect {
|
||||
/// Create a new rectangle with the given origin and dimensions.
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{CGPoint, CGRect, CGSize};
|
||||
/// let origin = CGPoint::new(10.0, -2.3);
|
||||
/// let size = CGSize::new(5.0, 0.0);
|
||||
/// let rect = CGRect::new(origin, size);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[doc(alias = "NSMakeRect")]
|
||||
#[doc(alias = "CGRectMake")]
|
||||
pub const fn new(origin: CGPoint, size: CGSize) -> Self {
|
||||
Self { origin, size }
|
||||
}
|
||||
|
||||
/// A rectangle with origin (0.0, 0.0) and zero width and height.
|
||||
#[doc(alias = "NSZeroRect")]
|
||||
#[doc(alias = "CGRectZero")]
|
||||
pub const ZERO: Self = Self::new(CGPoint::ZERO, CGSize::ZERO);
|
||||
|
||||
/// Returns a rectangle with a positive width and height.
|
||||
///
|
||||
/// This is often useful
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{CGPoint, CGRect, CGSize};
|
||||
/// let origin = CGPoint::new(1.0, 1.0);
|
||||
/// let size = CGSize::new(-5.0, -2.0);
|
||||
/// let rect = CGRect::new(origin, size);
|
||||
/// assert_eq!(rect.standardize().size, CGSize::new(5.0, 2.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[doc(alias = "CGRectStandardize")]
|
||||
pub fn standardize(self) -> Self {
|
||||
Self::new(self.origin, self.size.abs())
|
||||
}
|
||||
|
||||
/// The smallest coordinate of the rectangle.
|
||||
#[inline]
|
||||
#[doc(alias = "CGRectGetMinX")]
|
||||
#[doc(alias = "CGRectGetMinY")]
|
||||
#[doc(alias = "NSMinX")]
|
||||
#[doc(alias = "NSMinY")]
|
||||
pub fn min(self) -> CGPoint {
|
||||
self.origin
|
||||
}
|
||||
|
||||
/// The center point of the rectangle.
|
||||
#[inline]
|
||||
#[doc(alias = "CGRectGetMidX")]
|
||||
#[doc(alias = "CGRectGetMidY")]
|
||||
#[doc(alias = "NSMidX")]
|
||||
#[doc(alias = "NSMidY")]
|
||||
pub fn mid(self) -> CGPoint {
|
||||
CGPoint::new(
|
||||
self.origin.x + (self.size.width * 0.5),
|
||||
self.origin.y + (self.size.height * 0.5),
|
||||
)
|
||||
}
|
||||
|
||||
/// The largest coordinate of the rectangle.
|
||||
#[inline]
|
||||
#[doc(alias = "CGRectGetMaxX")]
|
||||
#[doc(alias = "CGRectGetMaxY")]
|
||||
#[doc(alias = "NSMaxX")]
|
||||
#[doc(alias = "NSMaxY")]
|
||||
pub fn max(self) -> CGPoint {
|
||||
CGPoint::new(
|
||||
self.origin.x + self.size.width,
|
||||
self.origin.y + self.size.height,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns whether a rectangle has zero width or height.
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{CGPoint, CGRect, CGSize};
|
||||
/// assert!(CGRect::ZERO.is_empty());
|
||||
/// let point = CGPoint::new(1.0, 2.0);
|
||||
/// assert!(CGRect::new(point, CGSize::ZERO).is_empty());
|
||||
/// assert!(!CGRect::new(point, CGSize::new(1.0, 1.0)).is_empty());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[doc(alias = "CGRectIsEmpty")]
|
||||
pub fn is_empty(self) -> bool {
|
||||
!(self.size.width > 0.0 && self.size.height > 0.0)
|
||||
// TODO: NaN handling?
|
||||
// self.size.width <= 0.0 || self.size.height <= 0.0
|
||||
}
|
||||
|
||||
// TODO: NSContainsRect / CGRectContainsRect
|
||||
// TODO: NSDivideRect / CGRectDivide
|
||||
// TODO: NSInsetRect / CGRectInset
|
||||
// TODO: NSIntegralRect / CGRectIntegral
|
||||
// TODO: NSIntersectionRect / CGRectIntersection
|
||||
// TODO: NSUnionRect / CGRectUnion
|
||||
// TODO: NSIntersectsRect / CGRectIntersectsRect
|
||||
// TODO: NSMouseInRect
|
||||
// TODO: NSMouseInRect
|
||||
// TODO: NSPointInRect / CGRectContainsPoint
|
||||
// TODO: NSOffsetRect / CGRectOffset
|
||||
|
||||
// TODO: CGRectIsNull
|
||||
// TODO: CGRectIsInfinite
|
||||
// TODO: CGRectInfinite
|
||||
// TODO: CGRectNull
|
||||
|
||||
// TODO: NSHeight / CGRectGetHeight (standardized)
|
||||
// TODO: NSWidth / CGRectGetWidth (standardized)
|
||||
}
|
||||
|
||||
/// A point in a Cartesian coordinate system.
|
||||
///
|
||||
/// This is just a convenience alias for [`CGPoint`]. For ease of use, it is
|
||||
/// available on all platforms, though in practice it is only useful on macOS.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nspoint?language=objc).
|
||||
pub type NSPoint = CGPoint;
|
||||
|
||||
/// A two-dimensional size.
|
||||
///
|
||||
/// This is just a convenience alias for [`CGSize`]. For ease of use, it is
|
||||
/// available on all platforms, though in practice it is only useful on macOS.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nssize?language=objc).
|
||||
pub type NSSize = CGSize;
|
||||
|
||||
/// A rectangle.
|
||||
///
|
||||
/// This is just a convenience alias for [`CGRect`]. For ease of use, it is
|
||||
/// available on all platforms, though in practice it is only useful on macOS.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsrect?language=objc).
|
||||
pub type NSRect = CGRect;
|
||||
|
||||
// TODO: struct NSEdgeInsets
|
||||
// TODO: enum NSRectEdge
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_cgsize_new() {
|
||||
CGSize::new(1.0, 1.0);
|
||||
CGSize::new(0.0, -0.0);
|
||||
CGSize::new(-0.0, 0.0);
|
||||
CGSize::new(-0.0, -0.0);
|
||||
CGSize::new(-1.0, -1.0);
|
||||
CGSize::new(-1.0, 1.0);
|
||||
CGSize::new(1.0, -1.0);
|
||||
}
|
||||
|
||||
// We know the Rust implementation handles NaN, infinite, negative zero
|
||||
// and so on properly, so let's ensure that NSEqualXXX handles these as
|
||||
// well (so that we're confident that the implementations are equivalent).
|
||||
#[test]
|
||||
#[cfg(any(all(feature = "apple", target_os = "macos"), feature = "gnustep-1-7"))] // or macabi
|
||||
fn test_partial_eq() {
|
||||
use crate::runtime::Bool;
|
||||
|
||||
// Note: No need to use "C-unwind"
|
||||
extern "C" {
|
||||
fn NSEqualPoints(a: NSPoint, b: NSPoint) -> Bool;
|
||||
fn NSEqualSizes(a: NSSize, b: NSSize) -> Bool;
|
||||
fn NSEqualRects(a: NSRect, b: NSRect) -> Bool;
|
||||
}
|
||||
|
||||
// We assume that comparisons handle e.g. `x` and `y` in the same way,
|
||||
// therefore we just set the coordinates / dimensions to the same.
|
||||
let cases: &[(CGFloat, CGFloat)] = &[
|
||||
(0.0, 0.0),
|
||||
(-0.0, -0.0),
|
||||
(0.0, -0.0),
|
||||
(1.0, 1.0 + CGFloat::EPSILON),
|
||||
(0.0, CGFloat::MIN_POSITIVE),
|
||||
(0.0, CGFloat::EPSILON),
|
||||
(1.0, 1.0),
|
||||
(1.0, -1.0),
|
||||
// Infinity
|
||||
(CGFloat::INFINITY, CGFloat::INFINITY),
|
||||
(CGFloat::INFINITY, CGFloat::NEG_INFINITY),
|
||||
(CGFloat::NEG_INFINITY, CGFloat::NEG_INFINITY),
|
||||
// NaN
|
||||
(CGFloat::NAN, 0.0),
|
||||
(CGFloat::NAN, 1.0),
|
||||
(CGFloat::NAN, CGFloat::NAN),
|
||||
(CGFloat::NAN, -CGFloat::NAN),
|
||||
(-CGFloat::NAN, -CGFloat::NAN),
|
||||
(CGFloat::NAN, CGFloat::INFINITY),
|
||||
];
|
||||
|
||||
for case in cases {
|
||||
let point_a = NSPoint::new(case.0, case.1);
|
||||
let point_b = NSPoint::new(case.0, case.1);
|
||||
let actual = unsafe { NSEqualPoints(point_a, point_b).as_bool() };
|
||||
assert_eq!(point_a == point_b, actual);
|
||||
|
||||
if case.0 >= 0.0 && case.1 >= 0.0 {
|
||||
let size_a = NSSize::new(case.0, case.1);
|
||||
let size_b = NSSize::new(case.0, case.1);
|
||||
let actual = unsafe { NSEqualSizes(size_a, size_b).as_bool() };
|
||||
assert_eq!(size_a == size_b, actual);
|
||||
|
||||
let rect_a = NSRect::new(point_a, size_a);
|
||||
let rect_b = NSRect::new(point_b, size_b);
|
||||
let actual = unsafe { NSEqualRects(rect_a, rect_b).as_bool() };
|
||||
assert_eq!(rect_a == rect_b, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
215
third-party/vendor/objc2/src/foundation/mod.rs
vendored
Normal file
215
third-party/vendor/objc2/src/foundation/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
//! Bindings to the `Foundation` framework.
|
||||
//!
|
||||
//! This is the [`std`] equivalent for Objective-C, containing essential data
|
||||
//! types, collections, and operating-system services.
|
||||
//!
|
||||
//! See [Apple's documentation](https://developer.apple.com/documentation/foundation?language=objc).
|
||||
//!
|
||||
//!
|
||||
//! ## Philosophy
|
||||
//!
|
||||
//! The `Foundation` framework is _huge_! If we aspired to map every API it
|
||||
//! exposes (a lot of it is just helper methods to make Objective-C more
|
||||
//! ergonomic), this library would never be finished. Instead, our focus lies
|
||||
//! on conversion methods, to allow easily using them from Rust.
|
||||
//!
|
||||
//! If you find some API that an object doesn't expose (but should), we gladly
|
||||
//! accept [pull requests]. If it is something that is out of scope, these
|
||||
//! objects implement the [`Message`] trait, so you can always just manually
|
||||
//! call a method on them using the [`msg_send!`] family of macros.
|
||||
//!
|
||||
//! [pull requests]: https://github.com/madsmtm/objc2/pulls
|
||||
//! [`Message`]: crate::Message
|
||||
//! [`msg_send!`]: crate::msg_send
|
||||
//!
|
||||
//!
|
||||
//! # Use of `Deref`
|
||||
//!
|
||||
//! `objc2::foundation` uses the [`Deref`] trait in a bit special way: All
|
||||
//! objects deref to their superclasses. For example, `NSMutableArray` derefs
|
||||
//! to `NSArray`, which in turn derefs to `NSObject`.
|
||||
//!
|
||||
//! Note that this is explicitly recommended against in [the
|
||||
//! documentation][`Deref`] and [the Rust Design patterns
|
||||
//! book][anti-pattern-deref] (see those links for details).
|
||||
//!
|
||||
//! Due to Objective-C objects only ever being accessible behind pointers in
|
||||
//! the first place, the problems stated there are less severe, and having the
|
||||
//! implementation just means that everything is much nicer when you actually
|
||||
//! want to use the objects!
|
||||
//!
|
||||
//! All objects also implement [`AsRef`] and [`AsMut`] to their superclass,
|
||||
//! and can be used in [`Id::into_super`], so if you favour explicit
|
||||
//! conversion, that is a possibility too.
|
||||
//!
|
||||
//! [`Deref`]: std::ops::Deref
|
||||
//! [`ClassType`]: crate::ClassType
|
||||
//! [anti-pattern-deref]: https://rust-unofficial.github.io/patterns/anti_patterns/deref.html
|
||||
//! [`Id::into_super`]: crate::rc::Id::into_super
|
||||
|
||||
// TODO: Remove these
|
||||
#![allow(missing_docs)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
use std::os::raw::c_double;
|
||||
|
||||
pub use self::array::NSArray;
|
||||
pub use self::attributed_string::{NSAttributedString, NSAttributedStringKey};
|
||||
pub use self::bundle::NSBundle;
|
||||
pub use self::comparison_result::NSComparisonResult;
|
||||
pub use self::copying::{NSCopying, NSMutableCopying};
|
||||
pub use self::data::NSData;
|
||||
pub use self::dictionary::NSDictionary;
|
||||
pub use self::enumerator::{NSEnumerator, NSFastEnumeration, NSFastEnumerator};
|
||||
pub use self::error::{NSError, NSErrorDomain, NSErrorUserInfoKey};
|
||||
pub use self::exception::NSException;
|
||||
pub use self::geometry::{CGFloat, CGPoint, CGRect, CGSize, NSPoint, NSRect, NSSize};
|
||||
pub use self::mutable_array::NSMutableArray;
|
||||
pub use self::mutable_attributed_string::NSMutableAttributedString;
|
||||
pub use self::mutable_data::NSMutableData;
|
||||
pub use self::mutable_dictionary::NSMutableDictionary;
|
||||
pub use self::mutable_set::NSMutableSet;
|
||||
pub use self::mutable_string::NSMutableString;
|
||||
pub use self::number::NSNumber;
|
||||
pub use self::object::NSObject;
|
||||
pub use self::process_info::NSProcessInfo;
|
||||
pub use self::range::NSRange;
|
||||
pub use self::set::NSSet;
|
||||
pub use self::string::NSString;
|
||||
pub use self::thread::{is_main_thread, is_multi_threaded, MainThreadMarker, NSThread};
|
||||
#[cfg(not(macos_10_7))] // Temporary
|
||||
pub use self::uuid::NSUUID;
|
||||
pub use self::value::NSValue;
|
||||
pub use self::zone::NSZone;
|
||||
|
||||
// Available under Foundation, so makes sense here as well:
|
||||
// https://developer.apple.com/documentation/foundation/numbers_data_and_basic_values?language=objc
|
||||
#[doc(no_inline)]
|
||||
pub use crate::ffi::{NSInteger, NSUInteger};
|
||||
|
||||
/// A value indicating that a requested item couldn’t be found or doesn’t exist.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsnotfound?language=objc).
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const NSNotFound: NSInteger = crate::ffi::NSIntegerMax;
|
||||
|
||||
/// A number of seconds.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nstimeinterval?language=objc).
|
||||
pub type NSTimeInterval = c_double;
|
||||
|
||||
#[cfg(feature = "apple")]
|
||||
#[link(name = "Foundation", kind = "framework")]
|
||||
extern "C" {}
|
||||
|
||||
#[cfg(feature = "gnustep-1-7")]
|
||||
#[link(name = "gnustep-base", kind = "dylib")]
|
||||
extern "C" {}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod __ns_string;
|
||||
mod array;
|
||||
mod attributed_string;
|
||||
mod bundle;
|
||||
mod comparison_result;
|
||||
mod copying;
|
||||
mod data;
|
||||
mod dictionary;
|
||||
mod enumerator;
|
||||
mod error;
|
||||
mod exception;
|
||||
mod geometry;
|
||||
mod mutable_array;
|
||||
mod mutable_attributed_string;
|
||||
mod mutable_data;
|
||||
mod mutable_dictionary;
|
||||
mod mutable_set;
|
||||
mod mutable_string;
|
||||
mod number;
|
||||
mod object;
|
||||
mod process_info;
|
||||
mod range;
|
||||
mod set;
|
||||
mod string;
|
||||
mod thread;
|
||||
// Temporarily disable testing UUID on macOS 10.7 until
|
||||
#[cfg(not(macos_10_7))]
|
||||
mod uuid;
|
||||
mod value;
|
||||
mod zone;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
use super::*;
|
||||
use crate::rc::{Id, Owned, Shared};
|
||||
|
||||
// We expect most Foundation types to be UnwindSafe and RefUnwindSafe,
|
||||
// since they follow Rust's usual mutability rules (&T = immutable).
|
||||
//
|
||||
// A _lot_ of Objective-C code out there would be subtly broken if e.g.
|
||||
// `NSString` wasn't exception safe!
|
||||
// As an example: -[NSArray objectAtIndex:] can throw, but it is still
|
||||
// perfectly valid to access the array after that!
|
||||
//
|
||||
// Note that e.g. `&mut NSMutableString` is still not exception safe, but
|
||||
// that is the entire idea of `UnwindSafe` (that if the object could have
|
||||
// been mutated, it is not exception safe).
|
||||
//
|
||||
// Also note that this is still just a speed bump, not actually part of
|
||||
// any unsafe contract; we really protect against it if something is not
|
||||
// exception safe, since `UnwindSafe` is a safe trait.
|
||||
fn assert_unwindsafe<T: UnwindSafe + RefUnwindSafe>() {}
|
||||
|
||||
fn assert_auto_traits<T: Send + Sync + UnwindSafe + RefUnwindSafe>() {
|
||||
assert_unwindsafe::<T>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_sync_unwindsafe() {
|
||||
assert_auto_traits::<NSArray<NSString, Shared>>();
|
||||
assert_auto_traits::<NSArray<NSString, Owned>>();
|
||||
assert_auto_traits::<Id<NSArray<NSString, Shared>, Shared>>();
|
||||
assert_auto_traits::<Id<NSArray<NSString, Owned>, Shared>>();
|
||||
assert_auto_traits::<Id<NSArray<NSString, Shared>, Owned>>();
|
||||
assert_auto_traits::<Id<NSArray<NSString, Owned>, Owned>>();
|
||||
|
||||
assert_auto_traits::<NSAttributedString>();
|
||||
assert_auto_traits::<NSComparisonResult>();
|
||||
assert_auto_traits::<NSData>();
|
||||
assert_auto_traits::<NSDictionary<NSString, NSString>>();
|
||||
assert_auto_traits::<NSSet<NSString, Shared>>();
|
||||
assert_auto_traits::<NSSet<NSString, Owned>>();
|
||||
assert_auto_traits::<Id<NSSet<NSString, Shared>, Shared>>();
|
||||
assert_auto_traits::<Id<NSSet<NSString, Owned>, Shared>>();
|
||||
assert_auto_traits::<Id<NSSet<NSString, Shared>, Owned>>();
|
||||
assert_auto_traits::<Id<NSSet<NSString, Owned>, Owned>>();
|
||||
// TODO: Figure out if Send + Sync is safe?
|
||||
// assert_auto_traits::<NSEnumerator<NSString>>();
|
||||
// assert_auto_traits::<NSFastEnumerator<NSArray<NSString, Shared>>>();
|
||||
assert_auto_traits::<NSError>();
|
||||
assert_auto_traits::<NSException>();
|
||||
assert_auto_traits::<CGFloat>();
|
||||
assert_auto_traits::<NSPoint>();
|
||||
assert_auto_traits::<NSRect>();
|
||||
assert_auto_traits::<NSSize>();
|
||||
assert_auto_traits::<NSMutableArray<NSString, Shared>>();
|
||||
assert_auto_traits::<NSMutableAttributedString>();
|
||||
assert_auto_traits::<NSMutableData>();
|
||||
assert_auto_traits::<NSMutableDictionary<NSString, NSString>>();
|
||||
assert_auto_traits::<NSMutableSet<NSString, Shared>>();
|
||||
assert_auto_traits::<NSMutableString>();
|
||||
assert_auto_traits::<NSNumber>();
|
||||
// assert_auto_traits::<NSObject>(); // Intentional
|
||||
assert_auto_traits::<NSProcessInfo>();
|
||||
assert_auto_traits::<NSRange>();
|
||||
assert_auto_traits::<NSString>();
|
||||
assert_unwindsafe::<MainThreadMarker>(); // Intentional
|
||||
assert_auto_traits::<NSThread>();
|
||||
#[cfg(not(macos_10_7))]
|
||||
assert_auto_traits::<NSUUID>();
|
||||
// assert_auto_traits::<NSValue>(); // Intentional
|
||||
assert_unwindsafe::<NSZone>(); // Intentional
|
||||
}
|
||||
}
|
||||
326
third-party/vendor/objc2/src/foundation/mutable_array.rs
vendored
Normal file
326
third-party/vendor/objc2/src/foundation/mutable_array.rs
vendored
Normal file
|
|
@ -0,0 +1,326 @@
|
|||
use alloc::vec::Vec;
|
||||
use core::cmp::Ordering;
|
||||
use core::ffi::c_void;
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Index, IndexMut};
|
||||
|
||||
use super::array::with_objects;
|
||||
use super::{
|
||||
NSArray, NSComparisonResult, NSCopying, NSFastEnumeration, NSFastEnumerator, NSMutableCopying,
|
||||
NSObject,
|
||||
};
|
||||
use crate::rc::{DefaultId, Id, Owned, Ownership, Shared, SliceId};
|
||||
use crate::{ClassType, Message, __inner_extern_class, extern_methods, msg_send, msg_send_id};
|
||||
|
||||
__inner_extern_class!(
|
||||
/// A growable ordered collection of objects.
|
||||
///
|
||||
/// See the documentation for [`NSArray`] and/or [Apple's
|
||||
/// documentation][apple-doc] for more information.
|
||||
///
|
||||
/// [apple-doc]: https://developer.apple.com/documentation/foundation/nsmutablearray?language=objc
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct NSMutableArray<T: Message, O: Ownership = Owned> {
|
||||
p: PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
unsafe impl<T: Message, O: Ownership> ClassType for NSMutableArray<T, O> {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSArray<T, O>;
|
||||
}
|
||||
);
|
||||
|
||||
// SAFETY: Same as NSArray<T, O>
|
||||
//
|
||||
// Put here because rustdoc doesn't show these otherwise
|
||||
unsafe impl<T: Message + Sync + Send> Sync for NSMutableArray<T, Shared> {}
|
||||
unsafe impl<T: Message + Sync + Send> Send for NSMutableArray<T, Shared> {}
|
||||
unsafe impl<T: Message + Sync> Sync for NSMutableArray<T, Owned> {}
|
||||
unsafe impl<T: Message + Send> Send for NSMutableArray<T, Owned> {}
|
||||
|
||||
extern_methods!(
|
||||
/// Generic creation methods.
|
||||
unsafe impl<T: Message, O: Ownership> NSMutableArray<T, O> {
|
||||
pub fn new() -> Id<Self, Owned> {
|
||||
// SAFETY: Same as `NSArray::new`, except mutable arrays are always
|
||||
// unique.
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
pub fn from_vec(vec: Vec<Id<T, O>>) -> Id<Self, Owned> {
|
||||
// SAFETY: Same as `NSArray::from_vec`, except mutable arrays are
|
||||
// always unique.
|
||||
unsafe { with_objects(Self::class(), vec.as_slice_ref()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Creation methods that produce shared arrays.
|
||||
unsafe impl<T: Message> NSMutableArray<T, Shared> {
|
||||
pub fn from_slice(slice: &[Id<T, Shared>]) -> Id<Self, Owned> {
|
||||
// SAFETY: Same as `NSArray::from_slice`, except mutable arrays are
|
||||
// always unique.
|
||||
unsafe { with_objects(Self::class(), slice.as_slice_ref()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic accessor methods.
|
||||
unsafe impl<T: Message, O: Ownership> NSMutableArray<T, O> {
|
||||
#[doc(alias = "addObject:")]
|
||||
pub fn push(&mut self, obj: Id<T, O>) {
|
||||
// SAFETY: The object is not nil
|
||||
unsafe { msg_send![self, addObject: &*obj] }
|
||||
}
|
||||
|
||||
#[doc(alias = "insertObject:atIndex:")]
|
||||
pub fn insert(&mut self, index: usize, obj: Id<T, O>) {
|
||||
// TODO: Replace this check with catching the thrown NSRangeException
|
||||
let len = self.len();
|
||||
if index < len {
|
||||
// SAFETY: The object is not nil and the index is checked to be in
|
||||
// bounds.
|
||||
unsafe { msg_send![self, insertObject: &*obj, atIndex: index] }
|
||||
} else {
|
||||
panic!(
|
||||
"insertion index (is {}) should be <= len (is {})",
|
||||
index, len
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "replaceObjectAtIndex:withObject:")]
|
||||
pub fn replace(&mut self, index: usize, obj: Id<T, O>) -> Id<T, O> {
|
||||
let old_obj = unsafe {
|
||||
let obj = self.get(index).unwrap();
|
||||
Id::retain_autoreleased(obj as *const T as *mut T).unwrap_unchecked()
|
||||
};
|
||||
unsafe {
|
||||
let _: () = msg_send![
|
||||
self,
|
||||
replaceObjectAtIndex: index,
|
||||
withObject: &*obj,
|
||||
];
|
||||
}
|
||||
old_obj
|
||||
}
|
||||
|
||||
#[sel(removeObjectAtIndex:)]
|
||||
unsafe fn remove_at(&mut self, index: usize);
|
||||
|
||||
#[doc(alias = "removeObjectAtIndex:")]
|
||||
pub fn remove(&mut self, index: usize) -> Id<T, O> {
|
||||
let obj = if let Some(obj) = self.get(index) {
|
||||
unsafe { Id::retain_autoreleased(obj as *const T as *mut T).unwrap_unchecked() }
|
||||
} else {
|
||||
panic!("removal index should be < len");
|
||||
};
|
||||
unsafe { self.remove_at(index) };
|
||||
obj
|
||||
}
|
||||
|
||||
#[sel(removeLastObject)]
|
||||
unsafe fn remove_last(&mut self);
|
||||
|
||||
#[doc(alias = "removeLastObject")]
|
||||
pub fn pop(&mut self) -> Option<Id<T, O>> {
|
||||
self.last()
|
||||
.map(|obj| unsafe { Id::retain(obj as *const T as *mut T).unwrap_unchecked() })
|
||||
.map(|obj| {
|
||||
// SAFETY: `Self::last` just checked that there is an object
|
||||
unsafe { self.remove_last() };
|
||||
obj
|
||||
})
|
||||
}
|
||||
|
||||
#[doc(alias = "removeAllObjects")]
|
||||
#[sel(removeAllObjects)]
|
||||
pub fn clear(&mut self);
|
||||
|
||||
#[doc(alias = "sortUsingFunction:context:")]
|
||||
pub fn sort_by<F: FnMut(&T, &T) -> Ordering>(&mut self, compare: F) {
|
||||
// TODO: "C-unwind"
|
||||
extern "C" fn compare_with_closure<U, F: FnMut(&U, &U) -> Ordering>(
|
||||
obj1: &U,
|
||||
obj2: &U,
|
||||
context: *mut c_void,
|
||||
) -> NSComparisonResult {
|
||||
// Bring back a reference to the closure.
|
||||
// Guaranteed to be unique, we gave `sortUsingFunction` unique is
|
||||
// ownership, and that method only runs one function at a time.
|
||||
let closure: &mut F = unsafe { context.cast::<F>().as_mut().unwrap_unchecked() };
|
||||
|
||||
NSComparisonResult::from((*closure)(obj1, obj2))
|
||||
}
|
||||
|
||||
// We can't name the actual lifetimes in use here, so use `_`.
|
||||
// See also https://github.com/rust-lang/rust/issues/56105
|
||||
let f: extern "C" fn(_, _, *mut c_void) -> NSComparisonResult =
|
||||
compare_with_closure::<T, F>;
|
||||
|
||||
// Grab a type-erased pointer to the closure (a pointer to stack).
|
||||
let mut closure = compare;
|
||||
let context: *mut F = &mut closure;
|
||||
let context: *mut c_void = context.cast();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![self, sortUsingFunction: f, context: context];
|
||||
}
|
||||
// Keep the closure alive until the function has run.
|
||||
drop(closure);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Copying only possible when ItemOwnership = Shared
|
||||
|
||||
/// This is implemented as a shallow copy.
|
||||
unsafe impl<T: Message> NSCopying for NSMutableArray<T, Shared> {
|
||||
type Ownership = Shared;
|
||||
type Output = NSArray<T, Shared>;
|
||||
}
|
||||
|
||||
/// This is implemented as a shallow copy.
|
||||
unsafe impl<T: Message> NSMutableCopying for NSMutableArray<T, Shared> {
|
||||
type Output = NSMutableArray<T, Shared>;
|
||||
}
|
||||
|
||||
impl<T: Message> alloc::borrow::ToOwned for NSMutableArray<T, Shared> {
|
||||
type Owned = Id<NSMutableArray<T, Shared>, Owned>;
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
self.mutable_copy()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Message, O: Ownership> NSFastEnumeration for NSMutableArray<T, O> {
|
||||
type Item = T;
|
||||
}
|
||||
|
||||
impl<'a, T: Message, O: Ownership> IntoIterator for &'a NSMutableArray<T, O> {
|
||||
type Item = &'a T;
|
||||
type IntoIter = NSFastEnumerator<'a, NSMutableArray<T, O>>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter_fast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message, O: Ownership> Extend<Id<T, O>> for NSMutableArray<T, O> {
|
||||
fn extend<I: IntoIterator<Item = Id<T, O>>>(&mut self, iter: I) {
|
||||
let iterator = iter.into_iter();
|
||||
iterator.for_each(move |item| self.push(item));
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message, O: Ownership> Index<usize> for NSMutableArray<T, O> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, index: usize) -> &T {
|
||||
self.get(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message> IndexMut<usize> for NSMutableArray<T, Owned> {
|
||||
fn index_mut(&mut self, index: usize) -> &mut T {
|
||||
self.get_mut(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message, O: Ownership> DefaultId for NSMutableArray<T, O> {
|
||||
type Ownership = Owned;
|
||||
|
||||
#[inline]
|
||||
fn default_id() -> Id<Self, Self::Ownership> {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug + Message, O: Ownership> fmt::Debug for NSMutableArray<T, O> {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::vec;
|
||||
|
||||
use super::*;
|
||||
use crate::foundation::NSString;
|
||||
use crate::rc::{autoreleasepool, RcTestObject, ThreadTestData};
|
||||
|
||||
#[test]
|
||||
fn test_adding() {
|
||||
let mut array = NSMutableArray::new();
|
||||
let obj1 = RcTestObject::new();
|
||||
let obj2 = RcTestObject::new();
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
array.push(obj1);
|
||||
expected.retain += 1;
|
||||
expected.release += 1;
|
||||
expected.assert_current();
|
||||
assert_eq!(array.len(), 1);
|
||||
assert_eq!(array.get(0), array.get(0));
|
||||
|
||||
array.insert(0, obj2);
|
||||
expected.retain += 1;
|
||||
expected.release += 1;
|
||||
expected.assert_current();
|
||||
assert_eq!(array.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replace() {
|
||||
let mut array = NSMutableArray::new();
|
||||
let obj1 = RcTestObject::new();
|
||||
let obj2 = RcTestObject::new();
|
||||
array.push(obj1);
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
let old_obj = array.replace(0, obj2);
|
||||
expected.retain += 2;
|
||||
expected.release += 2;
|
||||
expected.assert_current();
|
||||
assert_ne!(&*old_obj, array.get(0).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove() {
|
||||
let mut array = NSMutableArray::new();
|
||||
for _ in 0..4 {
|
||||
array.push(RcTestObject::new());
|
||||
}
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
let _obj = array.remove(1);
|
||||
expected.retain += 1;
|
||||
expected.release += 1;
|
||||
expected.assert_current();
|
||||
assert_eq!(array.len(), 3);
|
||||
|
||||
let _obj = array.pop();
|
||||
expected.retain += 1;
|
||||
expected.release += 1;
|
||||
expected.assert_current();
|
||||
assert_eq!(array.len(), 2);
|
||||
|
||||
array.clear();
|
||||
expected.release += 2;
|
||||
expected.dealloc += 2;
|
||||
expected.assert_current();
|
||||
assert_eq!(array.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sort() {
|
||||
let strings = vec![NSString::from_str("hello"), NSString::from_str("hi")];
|
||||
let mut strings = NSMutableArray::from_vec(strings);
|
||||
|
||||
autoreleasepool(|pool| {
|
||||
strings.sort_by(|s1, s2| s1.as_str(pool).len().cmp(&s2.as_str(pool).len()));
|
||||
assert_eq!(strings[0].as_str(pool), "hi");
|
||||
assert_eq!(strings[1].as_str(pool), "hello");
|
||||
});
|
||||
}
|
||||
}
|
||||
115
third-party/vendor/objc2/src/foundation/mutable_attributed_string.rs
vendored
Normal file
115
third-party/vendor/objc2/src/foundation/mutable_attributed_string.rs
vendored
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
use core::fmt;
|
||||
|
||||
use super::{NSAttributedString, NSCopying, NSMutableCopying, NSObject, NSString};
|
||||
use crate::rc::{DefaultId, Id, Owned, Shared};
|
||||
use crate::{extern_class, extern_methods, msg_send_id, ClassType};
|
||||
|
||||
extern_class!(
|
||||
/// A mutable string that has associated attributes.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsmutableattributedstring?language=objc).
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct NSMutableAttributedString;
|
||||
|
||||
unsafe impl ClassType for NSMutableAttributedString {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSAttributedString;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
/// Creating mutable attributed strings.
|
||||
unsafe impl NSMutableAttributedString {
|
||||
/// Construct an empty mutable attributed string.
|
||||
pub fn new() -> Id<Self, Owned> {
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
// TODO: new_with_attributes
|
||||
|
||||
#[doc(alias = "initWithString:")]
|
||||
pub fn from_nsstring(string: &NSString) -> Id<Self, Owned> {
|
||||
unsafe {
|
||||
let obj = msg_send_id![Self::class(), alloc];
|
||||
msg_send_id![obj, initWithString: string]
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "initWithAttributedString:")]
|
||||
pub fn from_attributed_nsstring(attributed_string: &NSAttributedString) -> Id<Self, Owned> {
|
||||
unsafe {
|
||||
let obj = msg_send_id![Self::class(), alloc];
|
||||
msg_send_id![obj, initWithAttributedString: attributed_string]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Modifying the attributed string.
|
||||
unsafe impl NSMutableAttributedString {
|
||||
// TODO
|
||||
// - mutableString
|
||||
// - replaceCharactersInRange:withString:
|
||||
// - setAttributes:range:
|
||||
|
||||
/// Replaces the entire attributed string.
|
||||
#[doc(alias = "setAttributedString:")]
|
||||
#[sel(setAttributedString:)]
|
||||
pub fn replace(&mut self, attributed_string: &NSAttributedString);
|
||||
}
|
||||
);
|
||||
|
||||
impl DefaultId for NSMutableAttributedString {
|
||||
type Ownership = Owned;
|
||||
|
||||
#[inline]
|
||||
fn default_id() -> Id<Self, Self::Ownership> {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl NSCopying for NSMutableAttributedString {
|
||||
type Ownership = Shared;
|
||||
type Output = NSAttributedString;
|
||||
}
|
||||
|
||||
unsafe impl NSMutableCopying for NSMutableAttributedString {
|
||||
type Output = NSMutableAttributedString;
|
||||
}
|
||||
|
||||
impl alloc::borrow::ToOwned for NSMutableAttributedString {
|
||||
type Owned = Id<NSMutableAttributedString, Owned>;
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
self.mutable_copy()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NSMutableAttributedString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::string::ToString;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
let s = NSAttributedString::new();
|
||||
assert_eq!(&s.string().to_string(), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_copy() {
|
||||
let s1 = NSMutableAttributedString::from_nsstring(&NSString::from_str("abc"));
|
||||
let s2 = s1.copy();
|
||||
assert_ne!(Id::as_ptr(&s1).cast(), Id::as_ptr(&s2));
|
||||
assert!(s2.is_kind_of::<NSAttributedString>());
|
||||
|
||||
let s3 = s1.mutable_copy();
|
||||
assert_ne!(Id::as_ptr(&s1), Id::as_ptr(&s3));
|
||||
assert!(s3.is_kind_of::<NSMutableAttributedString>());
|
||||
}
|
||||
}
|
||||
361
third-party/vendor/objc2/src/foundation/mutable_data.rs
vendored
Normal file
361
third-party/vendor/objc2/src/foundation/mutable_data.rs
vendored
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
#[cfg(feature = "block")]
|
||||
use alloc::vec::Vec;
|
||||
use core::ffi::c_void;
|
||||
use core::fmt;
|
||||
use core::ops::{Index, IndexMut, Range};
|
||||
use core::slice::{self, SliceIndex};
|
||||
use std::io;
|
||||
|
||||
use super::data::with_slice;
|
||||
use super::{NSCopying, NSData, NSMutableCopying, NSObject, NSRange};
|
||||
use crate::rc::{DefaultId, Id, Owned, Shared};
|
||||
use crate::{extern_class, extern_methods, msg_send_id, ClassType};
|
||||
|
||||
extern_class!(
|
||||
/// A dynamic byte buffer in memory.
|
||||
///
|
||||
/// This is the Objective-C equivalent of a [`Vec`] containing [`u8`].
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsmutabledata?language=objc).
|
||||
///
|
||||
/// [`Vec`]: std::vec::Vec
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct NSMutableData;
|
||||
|
||||
unsafe impl ClassType for NSMutableData {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSData;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
/// Creation methods
|
||||
unsafe impl NSMutableData {
|
||||
pub fn new() -> Id<Self, Owned> {
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
pub fn with_bytes(bytes: &[u8]) -> Id<Self, Owned> {
|
||||
unsafe { Id::from_shared(Id::cast(with_slice(Self::class(), bytes))) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "block")]
|
||||
pub fn from_vec(bytes: Vec<u8>) -> Id<Self, Owned> {
|
||||
unsafe { Id::from_shared(Id::cast(super::data::with_vec(Self::class(), bytes))) }
|
||||
}
|
||||
|
||||
// TODO: Use malloc_buf/mbox and `initWithBytesNoCopy:...`?
|
||||
|
||||
#[doc(alias = "initWithData:")]
|
||||
pub fn from_data(data: &NSData) -> Id<Self, Owned> {
|
||||
// Not provided on NSData, one should just use NSData::copy or similar
|
||||
unsafe {
|
||||
let obj = msg_send_id![Self::class(), alloc];
|
||||
msg_send_id![obj, initWithData: data]
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "initWithCapacity:")]
|
||||
pub fn with_capacity(capacity: usize) -> Id<Self, Owned> {
|
||||
unsafe {
|
||||
let obj = msg_send_id![Self::class(), alloc];
|
||||
msg_send_id![obj, initWithCapacity: capacity]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutation methods
|
||||
unsafe impl NSMutableData {
|
||||
/// Expands with zeroes, or truncates the buffer.
|
||||
#[doc(alias = "setLength:")]
|
||||
#[sel(setLength:)]
|
||||
pub fn set_len(&mut self, len: usize);
|
||||
|
||||
#[sel(mutableBytes)]
|
||||
fn bytes_mut_raw(&mut self) -> *mut c_void;
|
||||
|
||||
#[doc(alias = "mutableBytes")]
|
||||
pub fn bytes_mut(&mut self) -> &mut [u8] {
|
||||
let ptr = self.bytes_mut_raw();
|
||||
let ptr: *mut u8 = ptr.cast();
|
||||
// The bytes pointer may be null for length zero
|
||||
if ptr.is_null() {
|
||||
&mut []
|
||||
} else {
|
||||
unsafe { slice::from_raw_parts_mut(ptr, self.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[sel(appendBytes:length:)]
|
||||
unsafe fn append_raw(&mut self, ptr: *const c_void, len: usize);
|
||||
|
||||
#[doc(alias = "appendBytes:length:")]
|
||||
pub fn extend_from_slice(&mut self, bytes: &[u8]) {
|
||||
let bytes_ptr: *const c_void = bytes.as_ptr().cast();
|
||||
unsafe { self.append_raw(bytes_ptr, bytes.len()) }
|
||||
}
|
||||
|
||||
pub fn push(&mut self, byte: u8) {
|
||||
self.extend_from_slice(&[byte])
|
||||
}
|
||||
|
||||
#[sel(replaceBytesInRange:withBytes:length:)]
|
||||
unsafe fn replace_raw(&mut self, range: NSRange, ptr: *const c_void, len: usize);
|
||||
|
||||
#[doc(alias = "replaceBytesInRange:withBytes:length:")]
|
||||
pub fn replace_range(&mut self, range: Range<usize>, bytes: &[u8]) {
|
||||
let range = NSRange::from(range);
|
||||
// No need to verify the length of the range here,
|
||||
// `replaceBytesInRange:` just zero-fills if out of bounds.
|
||||
let ptr: *const c_void = bytes.as_ptr().cast();
|
||||
unsafe { self.replace_raw(range, ptr, bytes.len()) }
|
||||
}
|
||||
|
||||
pub fn set_bytes(&mut self, bytes: &[u8]) {
|
||||
let len = self.len();
|
||||
self.replace_range(0..len, bytes);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
unsafe impl NSCopying for NSMutableData {
|
||||
type Ownership = Shared;
|
||||
type Output = NSData;
|
||||
}
|
||||
|
||||
unsafe impl NSMutableCopying for NSMutableData {
|
||||
type Output = NSMutableData;
|
||||
}
|
||||
|
||||
impl alloc::borrow::ToOwned for NSMutableData {
|
||||
type Owned = Id<NSMutableData, Owned>;
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
self.mutable_copy()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for NSMutableData {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<[u8]> for NSMutableData {
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
self.bytes_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: SliceIndex<[u8]>> Index<I> for NSMutableData {
|
||||
type Output = I::Output;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: I) -> &Self::Output {
|
||||
Index::index(self.bytes(), index)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: SliceIndex<[u8]>> IndexMut<I> for NSMutableData {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: I) -> &mut Self::Output {
|
||||
IndexMut::index_mut(self.bytes_mut(), index)
|
||||
}
|
||||
}
|
||||
|
||||
// impl FromIterator<u8> for Id<NSMutableData, Owned> {
|
||||
// fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self {
|
||||
// let iter = iter.into_iter();
|
||||
// let (lower, _) = iter.size_hint();
|
||||
// let data = Self::with_capacity(lower);
|
||||
// for item in iter {
|
||||
// data.push(item);
|
||||
// }
|
||||
// data
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Extend<u8> for NSMutableData {
|
||||
/// You should use [`extend_from_slice`] whenever possible, it is more
|
||||
/// performant.
|
||||
///
|
||||
/// [`extend_from_slice`]: Self::extend_from_slice
|
||||
fn extend<T: IntoIterator<Item = u8>>(&mut self, iter: T) {
|
||||
let iterator = iter.into_iter();
|
||||
iterator.for_each(move |item| self.push(item));
|
||||
}
|
||||
}
|
||||
|
||||
// Vec also has this impl
|
||||
impl<'a> Extend<&'a u8> for NSMutableData {
|
||||
fn extend<T: IntoIterator<Item = &'a u8>>(&mut self, iter: T) {
|
||||
let iterator = iter.into_iter();
|
||||
iterator.for_each(move |item| self.push(*item));
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Write for NSMutableData {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.extend_from_slice(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
self.extend_from_slice(buf);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultId for NSMutableData {
|
||||
type Ownership = Owned;
|
||||
|
||||
#[inline]
|
||||
fn default_id() -> Id<Self, Self::Ownership> {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NSMutableData {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a NSMutableData {
|
||||
type Item = &'a u8;
|
||||
type IntoIter = core::slice::Iter<'a, u8>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.bytes().iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a mut NSMutableData {
|
||||
type Item = &'a mut u8;
|
||||
type IntoIter = core::slice::IterMut<'a, u8>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.bytes_mut().iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::runtime::Object;
|
||||
|
||||
#[test]
|
||||
fn test_bytes_mut() {
|
||||
let mut data = NSMutableData::with_bytes(&[7, 16]);
|
||||
data.bytes_mut()[0] = 3;
|
||||
assert_eq!(data.bytes(), [3, 16]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_len() {
|
||||
let mut data = NSMutableData::with_bytes(&[7, 16]);
|
||||
data.set_len(4);
|
||||
assert_eq!(data.len(), 4);
|
||||
assert_eq!(data.bytes(), [7, 16, 0, 0]);
|
||||
|
||||
data.set_len(1);
|
||||
assert_eq!(data.len(), 1);
|
||||
assert_eq!(data.bytes(), [7]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_append() {
|
||||
let mut data = NSMutableData::with_bytes(&[7, 16]);
|
||||
data.extend_from_slice(&[3, 52]);
|
||||
assert_eq!(data.len(), 4);
|
||||
assert_eq!(data.bytes(), [7, 16, 3, 52]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replace() {
|
||||
let mut data = NSMutableData::with_bytes(&[7, 16]);
|
||||
data.replace_range(0..0, &[3]);
|
||||
assert_eq!(data.bytes(), [3, 7, 16]);
|
||||
|
||||
data.replace_range(1..2, &[52, 13]);
|
||||
assert_eq!(data.bytes(), [3, 52, 13, 16]);
|
||||
|
||||
data.replace_range(2..4, &[6]);
|
||||
assert_eq!(data.bytes(), [3, 52, 6]);
|
||||
|
||||
data.set_bytes(&[8, 17]);
|
||||
assert_eq!(data.bytes(), [8, 17]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_data() {
|
||||
let data = NSData::with_bytes(&[1, 2]);
|
||||
let mut_data = NSMutableData::from_data(&data);
|
||||
assert_eq!(&*data, &**mut_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_capacity() {
|
||||
let mut data = NSMutableData::with_capacity(5);
|
||||
assert_eq!(data.bytes(), &[]);
|
||||
data.extend_from_slice(&[1, 2, 3, 4, 5]);
|
||||
assert_eq!(data.bytes(), &[1, 2, 3, 4, 5]);
|
||||
data.extend_from_slice(&[6, 7]);
|
||||
assert_eq!(data.bytes(), &[1, 2, 3, 4, 5, 6, 7]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extend() {
|
||||
let mut data = NSMutableData::with_bytes(&[1, 2]);
|
||||
data.extend(3..=5);
|
||||
assert_eq!(data.bytes(), &[1, 2, 3, 4, 5]);
|
||||
data.extend(&*NSData::with_bytes(&[6, 7]));
|
||||
assert_eq!(data.bytes(), &[1, 2, 3, 4, 5, 6, 7]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_as_ref_borrow() {
|
||||
use core::borrow::{Borrow, BorrowMut};
|
||||
|
||||
fn impls_borrow<T: AsRef<U> + Borrow<U> + ?Sized, U: ?Sized>(_: &T) {}
|
||||
fn impls_borrow_mut<T: AsMut<U> + BorrowMut<U> + ?Sized, U: ?Sized>(_: &mut T) {}
|
||||
|
||||
let mut obj = NSMutableData::new();
|
||||
impls_borrow::<Id<NSMutableData, Owned>, NSMutableData>(&obj);
|
||||
impls_borrow_mut::<Id<NSMutableData, Owned>, NSMutableData>(&mut obj);
|
||||
|
||||
impls_borrow::<NSMutableData, NSMutableData>(&obj);
|
||||
impls_borrow_mut::<NSMutableData, NSMutableData>(&mut obj);
|
||||
impls_borrow::<NSMutableData, NSData>(&obj);
|
||||
impls_borrow_mut::<NSMutableData, NSData>(&mut obj);
|
||||
impls_borrow::<NSMutableData, NSObject>(&obj);
|
||||
impls_borrow_mut::<NSMutableData, NSObject>(&mut obj);
|
||||
impls_borrow::<NSMutableData, Object>(&obj);
|
||||
impls_borrow_mut::<NSMutableData, Object>(&mut obj);
|
||||
|
||||
impls_borrow::<NSData, NSData>(&obj);
|
||||
impls_borrow_mut::<NSData, NSData>(&mut obj);
|
||||
impls_borrow::<NSData, NSObject>(&obj);
|
||||
impls_borrow_mut::<NSData, NSObject>(&mut obj);
|
||||
impls_borrow::<NSData, Object>(&obj);
|
||||
impls_borrow_mut::<NSData, Object>(&mut obj);
|
||||
|
||||
fn impls_as_ref<T: AsRef<U> + ?Sized, U: ?Sized>(_: &T) {}
|
||||
fn impls_as_mut<T: AsMut<U> + ?Sized, U: ?Sized>(_: &mut T) {}
|
||||
|
||||
impls_as_ref::<NSMutableData, [u8]>(&obj);
|
||||
impls_as_mut::<NSMutableData, [u8]>(&mut obj);
|
||||
impls_as_ref::<NSData, [u8]>(&obj);
|
||||
|
||||
let obj: &mut NSMutableData = &mut obj;
|
||||
let _: &[u8] = obj.as_ref();
|
||||
let _: &mut [u8] = obj.as_mut();
|
||||
|
||||
let obj: &mut NSData = obj;
|
||||
let _: &[u8] = obj.as_ref();
|
||||
}
|
||||
}
|
||||
399
third-party/vendor/objc2/src/foundation/mutable_dictionary.rs
vendored
Normal file
399
third-party/vendor/objc2/src/foundation/mutable_dictionary.rs
vendored
Normal file
|
|
@ -0,0 +1,399 @@
|
|||
use alloc::vec::Vec;
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Index, IndexMut};
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use core::ptr;
|
||||
|
||||
use super::{NSArray, NSCopying, NSDictionary, NSFastEnumeration, NSObject};
|
||||
use crate::rc::{DefaultId, Id, Owned, Shared};
|
||||
use crate::{ClassType, __inner_extern_class, extern_methods, msg_send_id, Message};
|
||||
|
||||
__inner_extern_class!(
|
||||
/// A mutable collection of objects associated with unique keys.
|
||||
///
|
||||
/// See the documentation for [`NSDictionary`] and/or [Apple's
|
||||
/// documentation][apple-doc] for more information.
|
||||
///
|
||||
/// [apple-doc]: https://developer.apple.com/documentation/foundation/nsmutabledictionary?language=objc
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct NSMutableDictionary<K: Message, V: Message> {
|
||||
key: PhantomData<Id<K, Shared>>,
|
||||
obj: PhantomData<Id<V, Owned>>,
|
||||
}
|
||||
|
||||
unsafe impl<K: Message, V: Message> ClassType for NSMutableDictionary<K, V> {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSDictionary<K, V>;
|
||||
}
|
||||
);
|
||||
|
||||
// Same as `NSDictionary<K, V>`
|
||||
unsafe impl<K: Message + Sync + Send, V: Message + Sync> Sync for NSMutableDictionary<K, V> {}
|
||||
unsafe impl<K: Message + Sync + Send, V: Message + Send> Send for NSMutableDictionary<K, V> {}
|
||||
|
||||
// Same as `NSDictionary<K, V>`
|
||||
impl<K: Message + UnwindSafe, V: Message + UnwindSafe> UnwindSafe for NSMutableDictionary<K, V> {}
|
||||
impl<K: Message + RefUnwindSafe, V: Message + RefUnwindSafe> RefUnwindSafe
|
||||
for NSMutableDictionary<K, V>
|
||||
{
|
||||
}
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl<K: Message, V: Message> NSMutableDictionary<K, V> {
|
||||
/// Creates an empty [`NSMutableDictionary`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSMutableDictionary, NSObject, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let dict = NSMutableDictionary::<NSString, NSObject>::new();
|
||||
/// ```
|
||||
pub fn new() -> Id<Self, Owned> {
|
||||
// SAFETY:
|
||||
// Mutable dictionaries are always unique, so it's safe to return
|
||||
// `Id<Self, Owned>`
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
#[sel(setDictionary:)]
|
||||
fn set_dictionary(&mut self, dict: &NSDictionary<K, V>);
|
||||
|
||||
/// Creates an [`NSMutableDictionary`] from a slice of keys and a
|
||||
/// vector of values.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSMutableDictionary, NSNumber, NSObject};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
/// let dict = NSMutableDictionary::from_keys_and_objects(
|
||||
/// &[
|
||||
/// &*NSNumber::new_i32(1),
|
||||
/// &*NSNumber::new_i32(2),
|
||||
/// &*NSNumber::new_i32(3),
|
||||
/// ],
|
||||
/// vec![NSObject::new(), NSObject::new(), NSObject::new()],
|
||||
/// );
|
||||
/// ```
|
||||
pub fn from_keys_and_objects<T>(keys: &[&T], vals: Vec<Id<V, Owned>>) -> Id<Self, Owned>
|
||||
where
|
||||
T: NSCopying<Output = K>,
|
||||
{
|
||||
let mut dict = NSMutableDictionary::new();
|
||||
dict.set_dictionary(&*NSDictionary::from_keys_and_objects(keys, vals));
|
||||
dict
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the value corresponding to the key.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSMutableDictionary, NSObject, NSString};
|
||||
/// use objc2::ns_string;
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let mut dict = NSMutableDictionary::new();
|
||||
/// dict.insert(NSString::from_str("one"), NSObject::new());
|
||||
/// println!("{:?}", dict.get_mut(ns_string!("one")));
|
||||
/// ```
|
||||
#[doc(alias = "objectForKey:")]
|
||||
#[sel(objectForKey:)]
|
||||
pub fn get_mut(&mut self, key: &K) -> Option<&mut V>;
|
||||
|
||||
#[sel(getObjects:andKeys:)]
|
||||
unsafe fn get_objects_and_keys(&self, objects: *mut &mut V, keys: *mut &K);
|
||||
|
||||
/// Returns a vector of mutable references to the values in the dictionary.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSMutableDictionary, NSObject, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let mut dict = NSMutableDictionary::new();
|
||||
/// dict.insert(NSString::from_str("one"), NSObject::new());
|
||||
/// for val in dict.values_mut() {
|
||||
/// println!("{:?}", val);
|
||||
/// }
|
||||
/// ```
|
||||
#[doc(alias = "getObjects:andKeys:")]
|
||||
pub fn values_mut(&mut self) -> Vec<&mut V> {
|
||||
let len = self.len();
|
||||
let mut vals: Vec<&mut V> = Vec::with_capacity(len);
|
||||
// SAFETY: `vals` is not null
|
||||
unsafe {
|
||||
self.get_objects_and_keys(vals.as_mut_ptr(), ptr::null_mut());
|
||||
vals.set_len(len);
|
||||
}
|
||||
vals
|
||||
}
|
||||
|
||||
#[sel(setObject:forKey:)]
|
||||
fn set_object_for_key(&mut self, object: &V, key: &K);
|
||||
|
||||
/// Inserts a key-value pair into the dictionary.
|
||||
///
|
||||
/// If the dictionary did not have this key present, None is returned.
|
||||
/// If the dictionary did have this key present, the value is updated,
|
||||
/// and the old value is returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSMutableDictionary, NSObject, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let mut dict = NSMutableDictionary::new();
|
||||
/// dict.insert(NSString::from_str("one"), NSObject::new());
|
||||
/// ```
|
||||
#[doc(alias = "setObject:forKey:")]
|
||||
pub fn insert(&mut self, key: Id<K, Shared>, value: Id<V, Owned>) -> Option<Id<V, Owned>> {
|
||||
// SAFETY:
|
||||
// `obj` is a reference to a value in the dictionary so it's safe
|
||||
// to cast it to a pointer and pass it to `Id::retain_autoreleased`
|
||||
let obj = self.get(&*key).map(|obj| unsafe {
|
||||
Id::retain_autoreleased(obj as *const V as *mut V).unwrap_unchecked()
|
||||
});
|
||||
self.set_object_for_key(&*value, &*key);
|
||||
obj
|
||||
}
|
||||
|
||||
#[sel(removeObjectForKey:)]
|
||||
fn remove_object_for_key(&mut self, key: &K);
|
||||
|
||||
/// Removes a key from the dictionary, returning the value at the key
|
||||
/// if the key was previously in the dictionary.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSMutableDictionary, NSObject, NSString};
|
||||
/// use objc2::ns_string;
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let mut dict = NSMutableDictionary::new();
|
||||
/// dict.insert(NSString::from_str("one"), NSObject::new());
|
||||
/// dict.remove(ns_string!("one"));
|
||||
/// assert!(dict.is_empty());
|
||||
/// ```
|
||||
#[doc(alias = "removeObjectForKey:")]
|
||||
pub fn remove(&mut self, key: &K) -> Option<Id<V, Owned>> {
|
||||
// SAFETY:
|
||||
// `obj` is a reference to a value in the dictionary so it's safe
|
||||
// to cast it to a pointer and pass it to `Id::retain_autoreleased`
|
||||
let obj = self.get(key).map(|obj| unsafe {
|
||||
Id::retain_autoreleased(obj as *const V as *mut V).unwrap_unchecked()
|
||||
});
|
||||
self.remove_object_for_key(key);
|
||||
obj
|
||||
}
|
||||
|
||||
/// Clears the dictionary, removing all key-value pairs.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSMutableDictionary, NSObject, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let mut dict = NSMutableDictionary::new();
|
||||
/// dict.insert(NSString::from_str("one"), NSObject::new());
|
||||
/// dict.clear();
|
||||
/// assert!(dict.is_empty());
|
||||
/// ```
|
||||
#[doc(alias = "removeAllObjects")]
|
||||
#[sel(removeAllObjects)]
|
||||
pub fn clear(&mut self);
|
||||
|
||||
/// Returns an [`NSArray`] containing the dictionary's values,
|
||||
/// consuming the dictionary.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSMutableDictionary, NSObject, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let mut dict = NSMutableDictionary::new();
|
||||
/// dict.insert(NSString::from_str("one"), NSObject::new());
|
||||
/// let array = NSMutableDictionary::into_values_array(dict);
|
||||
/// println!("{:?}", array);
|
||||
/// ```
|
||||
pub fn into_values_array(dict: Id<Self, Owned>) -> Id<NSArray<V, Owned>, Shared> {
|
||||
unsafe { msg_send_id![&dict, allValues] }
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
unsafe impl<K: Message, V: Message> NSFastEnumeration for NSMutableDictionary<K, V> {
|
||||
type Item = K;
|
||||
}
|
||||
|
||||
impl<'a, K: Message, V: Message> Index<&'a K> for NSMutableDictionary<K, V> {
|
||||
type Output = V;
|
||||
|
||||
fn index<'s>(&'s self, index: &'a K) -> &'s V {
|
||||
self.get(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: Message, V: Message> IndexMut<&'a K> for NSMutableDictionary<K, V> {
|
||||
fn index_mut<'s>(&'s mut self, index: &'a K) -> &'s mut V {
|
||||
self.get_mut(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Message, V: Message> DefaultId for NSMutableDictionary<K, V> {
|
||||
type Ownership = Owned;
|
||||
|
||||
#[inline]
|
||||
fn default_id() -> Id<Self, Self::Ownership> {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: fmt::Debug + Message, V: fmt::Debug + Message> fmt::Debug for NSMutableDictionary<K, V> {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use alloc::vec;
|
||||
|
||||
use crate::{
|
||||
foundation::{NSNumber, NSString},
|
||||
rc::{RcTestObject, ThreadTestData},
|
||||
};
|
||||
|
||||
fn sample_dict() -> Id<NSMutableDictionary<NSNumber, NSObject>, Owned> {
|
||||
NSMutableDictionary::from_keys_and_objects(
|
||||
&[
|
||||
&*NSNumber::new_i32(1),
|
||||
&*NSNumber::new_i32(2),
|
||||
&*NSNumber::new_i32(3),
|
||||
],
|
||||
vec![NSObject::new(), NSObject::new(), NSObject::new()],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
let dict = NSMutableDictionary::<NSString, NSObject>::new();
|
||||
assert!(dict.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_mut() {
|
||||
let mut dict = sample_dict();
|
||||
assert!(dict.get_mut(&NSNumber::new_i32(1)).is_some());
|
||||
assert!(dict.get_mut(&NSNumber::new_i32(2)).is_some());
|
||||
assert!(dict.get_mut(&NSNumber::new_i32(4)).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_values_mut() {
|
||||
let mut dict = sample_dict();
|
||||
let vec = dict.values_mut();
|
||||
assert_eq!(vec.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert() {
|
||||
let mut dict = NSMutableDictionary::new();
|
||||
assert!(dict.insert(NSNumber::new_i32(1), NSObject::new()).is_none());
|
||||
assert!(dict.insert(NSNumber::new_i32(2), NSObject::new()).is_none());
|
||||
assert!(dict.insert(NSNumber::new_i32(3), NSObject::new()).is_none());
|
||||
assert!(dict.insert(NSNumber::new_i32(1), NSObject::new()).is_some());
|
||||
assert_eq!(dict.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert_retain_release() {
|
||||
let mut dict = NSMutableDictionary::new();
|
||||
dict.insert(NSNumber::new_i32(1), RcTestObject::new());
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
let old = dict.insert(NSNumber::new_i32(1), RcTestObject::new());
|
||||
expected.alloc += 1;
|
||||
expected.init += 1;
|
||||
expected.retain += 2;
|
||||
expected.release += 2;
|
||||
expected.assert_current();
|
||||
|
||||
drop(old);
|
||||
expected.release += 1;
|
||||
expected.dealloc += 1;
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove() {
|
||||
let mut dict = sample_dict();
|
||||
assert_eq!(dict.len(), 3);
|
||||
assert!(dict.remove(&NSNumber::new_i32(1)).is_some());
|
||||
assert!(dict.remove(&NSNumber::new_i32(2)).is_some());
|
||||
assert!(dict.remove(&NSNumber::new_i32(1)).is_none());
|
||||
assert!(dict.remove(&NSNumber::new_i32(4)).is_none());
|
||||
assert_eq!(dict.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clear() {
|
||||
let mut dict = sample_dict();
|
||||
assert_eq!(dict.len(), 3);
|
||||
|
||||
dict.clear();
|
||||
assert!(dict.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_clear_release_dealloc() {
|
||||
let mut dict = NSMutableDictionary::new();
|
||||
for i in 0..4 {
|
||||
dict.insert(NSNumber::new_i32(i), RcTestObject::new());
|
||||
}
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
let _obj = dict.remove(&NSNumber::new_i32(1));
|
||||
expected.retain += 1;
|
||||
expected.release += 1;
|
||||
expected.assert_current();
|
||||
assert_eq!(dict.len(), 3);
|
||||
|
||||
let _obj = dict.remove(&NSNumber::new_i32(2));
|
||||
expected.retain += 1;
|
||||
expected.release += 1;
|
||||
expected.assert_current();
|
||||
assert_eq!(dict.len(), 2);
|
||||
|
||||
dict.clear();
|
||||
expected.release += 2;
|
||||
expected.dealloc += 2;
|
||||
expected.assert_current();
|
||||
assert_eq!(dict.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_values_array() {
|
||||
let dict = sample_dict();
|
||||
let array = NSMutableDictionary::into_values_array(dict);
|
||||
assert_eq!(array.len(), 3);
|
||||
}
|
||||
}
|
||||
368
third-party/vendor/objc2/src/foundation/mutable_set.rs
vendored
Normal file
368
third-party/vendor/objc2/src/foundation/mutable_set.rs
vendored
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
use alloc::vec::Vec;
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use super::set::with_objects;
|
||||
use super::{NSCopying, NSFastEnumeration, NSFastEnumerator, NSMutableCopying, NSObject, NSSet};
|
||||
use crate::rc::{DefaultId, Id, Owned, Ownership, Shared, SliceId};
|
||||
use crate::{ClassType, Message, __inner_extern_class, extern_methods, msg_send_id};
|
||||
|
||||
__inner_extern_class!(
|
||||
/// A growable unordered collection of unique objects.
|
||||
///
|
||||
/// See the documentation for [`NSSet`] and/or [Apple's
|
||||
/// documentation][apple-doc] for more information.
|
||||
///
|
||||
/// [apple-doc]: https://developer.apple.com/documentation/foundation/nsmutableset?language=objc
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct NSMutableSet<T: Message, O: Ownership = Owned> {
|
||||
p: PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
unsafe impl<T: Message, O: Ownership> ClassType for NSMutableSet<T, O> {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSSet<T, O>;
|
||||
}
|
||||
);
|
||||
|
||||
// SAFETY: Same as NSSet<T, O>
|
||||
unsafe impl<T: Message + Sync + Send> Sync for NSMutableSet<T, Shared> {}
|
||||
unsafe impl<T: Message + Sync + Send> Send for NSMutableSet<T, Shared> {}
|
||||
unsafe impl<T: Message + Sync> Sync for NSMutableSet<T, Owned> {}
|
||||
unsafe impl<T: Message + Send> Send for NSMutableSet<T, Owned> {}
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl<T: Message, O: Ownership> NSMutableSet<T, O> {
|
||||
/// Creates an empty [`NSMutableSet`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSMutableSet, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let set = NSMutableSet::<NSString>::new();
|
||||
/// ```
|
||||
pub fn new() -> Id<Self, Owned> {
|
||||
// SAFETY:
|
||||
// Same as `NSSet::new`, except mutable sets are always unique.
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
/// Creates an [`NSMutableSet`] from a vector.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSMutableSet, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let strs = ["one", "two", "three"].map(NSString::from_str).to_vec();
|
||||
/// let set = NSMutableSet::from_vec(strs);
|
||||
/// ```
|
||||
pub fn from_vec(vec: Vec<Id<T, O>>) -> Id<Self, Owned> {
|
||||
// SAFETY:
|
||||
// We always return `Id<NSMutableSet<T, O>, Owned>` because mutable
|
||||
// sets are always unique.
|
||||
unsafe { with_objects(Self::class(), vec.as_slice_ref()) }
|
||||
}
|
||||
|
||||
/// Clears the set, removing all values.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSMutableSet, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let mut set = NSMutableSet::new();
|
||||
/// set.insert(NSString::from_str("one"));
|
||||
/// set.clear();
|
||||
/// assert!(set.is_empty());
|
||||
/// ```
|
||||
#[doc(alias = "removeAllObjects")]
|
||||
#[sel(removeAllObjects)]
|
||||
pub fn clear(&mut self);
|
||||
|
||||
/// Returns a [`Vec`] containing the set's elements, consuming the set.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSMutableSet, NSMutableString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let strs = vec![
|
||||
/// NSMutableString::from_str("one"),
|
||||
/// NSMutableString::from_str("two"),
|
||||
/// NSMutableString::from_str("three"),
|
||||
/// ];
|
||||
/// let set = NSMutableSet::from_vec(strs);
|
||||
/// let vec = NSMutableSet::into_vec(set);
|
||||
/// assert_eq!(vec.len(), 3);
|
||||
/// ```
|
||||
pub fn into_vec(set: Id<Self, Owned>) -> Vec<Id<T, O>> {
|
||||
set.into_iter()
|
||||
.map(|obj| unsafe { Id::retain(obj as *const T as *mut T).unwrap_unchecked() })
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Message> NSMutableSet<T, Shared> {
|
||||
/// Creates an [`NSMutableSet`] from a slice.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSMutableSet, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let strs = ["one", "two", "three"].map(NSString::from_str);
|
||||
/// let set = NSMutableSet::from_slice(&strs);
|
||||
/// ```
|
||||
pub fn from_slice(slice: &[Id<T, Shared>]) -> Id<Self, Owned> {
|
||||
// SAFETY:
|
||||
// Taking `&T` would not be sound, since the `&T` could come from
|
||||
// an `Id<T, Owned>` that would now no longer be owned!
|
||||
//
|
||||
// We always return `Id<NSMutableSet<T, Shared>, Owned>` because
|
||||
// the elements are shared and mutable sets are always unique.
|
||||
unsafe { with_objects(Self::class(), slice.as_slice_ref()) }
|
||||
}
|
||||
}
|
||||
|
||||
// We're explicit about `T` being `PartialEq` for these methods because the
|
||||
// set compares the input value with elements in the set
|
||||
// For comparison: Rust's HashSet requires similar methods to be `Hash` + `Eq`
|
||||
unsafe impl<T: Message + PartialEq, O: Ownership> NSMutableSet<T, O> {
|
||||
#[sel(addObject:)]
|
||||
fn add_object(&mut self, value: &T);
|
||||
|
||||
/// Adds a value to the set. Returns whether the value was
|
||||
/// newly inserted.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSMutableSet, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let mut set = NSMutableSet::new();
|
||||
///
|
||||
/// assert_eq!(set.insert(NSString::from_str("one")), true);
|
||||
/// assert_eq!(set.insert(NSString::from_str("one")), false);
|
||||
/// assert_eq!(set.len(), 1);
|
||||
/// ```
|
||||
#[doc(alias = "addObject:")]
|
||||
pub fn insert(&mut self, value: Id<T, O>) -> bool {
|
||||
// SAFETY:
|
||||
// We take `Id<T, O>` instead of `&T` because `&T` could be a
|
||||
// reference to an owned object which would cause us to have a copy
|
||||
// of an owned object in our set. By taking `Id<T, O>`, we force the
|
||||
// caller to transfer ownership of the value to us, making it safe
|
||||
// to insert the owned object into the set.
|
||||
let contains_value = self.contains(&value);
|
||||
self.add_object(&*value);
|
||||
!contains_value
|
||||
}
|
||||
|
||||
#[sel(removeObject:)]
|
||||
fn remove_object(&mut self, value: &T);
|
||||
|
||||
/// Removes a value from the set. Returns whether the value was present
|
||||
/// in the set.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSMutableSet, NSString};
|
||||
/// use objc2::ns_string;
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let mut set = NSMutableSet::new();
|
||||
///
|
||||
/// set.insert(NSString::from_str("one"));
|
||||
/// assert_eq!(set.remove(ns_string!("one")), true);
|
||||
/// assert_eq!(set.remove(ns_string!("one")), false);
|
||||
/// ```
|
||||
#[doc(alias = "removeObject:")]
|
||||
pub fn remove(&mut self, value: &T) -> bool {
|
||||
let contains_value = self.contains(value);
|
||||
self.remove_object(value);
|
||||
contains_value
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
unsafe impl<T: Message> NSCopying for NSMutableSet<T, Shared> {
|
||||
type Ownership = Shared;
|
||||
type Output = NSSet<T, Shared>;
|
||||
}
|
||||
|
||||
unsafe impl<T: Message> NSMutableCopying for NSMutableSet<T, Shared> {
|
||||
type Output = NSMutableSet<T, Shared>;
|
||||
}
|
||||
|
||||
impl<T: Message> alloc::borrow::ToOwned for NSMutableSet<T, Shared> {
|
||||
type Owned = Id<NSMutableSet<T, Shared>, Owned>;
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
self.mutable_copy()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Message, O: Ownership> NSFastEnumeration for NSMutableSet<T, O> {
|
||||
type Item = T;
|
||||
}
|
||||
|
||||
impl<'a, T: Message, O: Ownership> IntoIterator for &'a NSMutableSet<T, O> {
|
||||
type Item = &'a T;
|
||||
type IntoIter = NSFastEnumerator<'a, NSMutableSet<T, O>>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter_fast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message + PartialEq, O: Ownership> Extend<Id<T, O>> for NSMutableSet<T, O> {
|
||||
fn extend<I: IntoIterator<Item = Id<T, O>>>(&mut self, iter: I) {
|
||||
for item in iter {
|
||||
self.insert(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message, O: Ownership> DefaultId for NSMutableSet<T, O> {
|
||||
type Ownership = Owned;
|
||||
|
||||
#[inline]
|
||||
fn default_id() -> Id<Self, Self::Ownership> {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug + Message, O: Ownership> fmt::Debug for NSMutableSet<T, O> {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::vec;
|
||||
|
||||
use super::*;
|
||||
use crate::foundation::{NSMutableString, NSString};
|
||||
use crate::ns_string;
|
||||
use crate::rc::{RcTestObject, ThreadTestData};
|
||||
|
||||
#[test]
|
||||
fn test_insert() {
|
||||
let mut set = NSMutableSet::new();
|
||||
assert!(set.is_empty());
|
||||
|
||||
assert!(set.insert(NSString::from_str("one")));
|
||||
assert!(!set.insert(NSString::from_str("one")));
|
||||
assert!(set.insert(NSString::from_str("two")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove() {
|
||||
let strs = ["one", "two", "three"].map(NSString::from_str);
|
||||
let mut set = NSMutableSet::from_slice(&strs);
|
||||
|
||||
assert!(set.remove(ns_string!("one")));
|
||||
assert!(!set.remove(ns_string!("one")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clear() {
|
||||
let strs = ["one", "two", "three"].map(NSString::from_str);
|
||||
let mut set = NSMutableSet::from_slice(&strs);
|
||||
assert_eq!(set.len(), 3);
|
||||
|
||||
set.clear();
|
||||
assert!(set.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_vec() {
|
||||
let strs = vec![
|
||||
NSMutableString::from_str("one"),
|
||||
NSMutableString::from_str("two"),
|
||||
NSMutableString::from_str("three"),
|
||||
];
|
||||
let set = NSMutableSet::from_vec(strs);
|
||||
|
||||
let mut vec = NSMutableSet::into_vec(set);
|
||||
for str in vec.iter_mut() {
|
||||
str.push_nsstring(ns_string!(" times zero is zero"));
|
||||
}
|
||||
|
||||
assert_eq!(vec.len(), 3);
|
||||
let suffix = ns_string!("zero");
|
||||
assert!(vec.iter().all(|str| str.has_suffix(suffix)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extend() {
|
||||
let mut set = NSMutableSet::new();
|
||||
assert!(set.is_empty());
|
||||
|
||||
set.extend(["one", "two", "three"].map(NSString::from_str));
|
||||
assert_eq!(set.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mutable_copy() {
|
||||
let set1 = NSSet::from_slice(&["one", "two", "three"].map(NSString::from_str));
|
||||
let mut set2 = set1.mutable_copy();
|
||||
set2.insert(NSString::from_str("four"));
|
||||
|
||||
assert!(set1.is_subset(&set2));
|
||||
assert_ne!(set1.mutable_copy(), set2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert_retain_release() {
|
||||
let mut set = NSMutableSet::new();
|
||||
let obj1 = RcTestObject::new();
|
||||
let obj2 = RcTestObject::new();
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
set.insert(obj1);
|
||||
expected.retain += 1;
|
||||
expected.release += 1;
|
||||
expected.assert_current();
|
||||
assert_eq!(set.len(), 1);
|
||||
assert_eq!(set.get_any(), set.get_any());
|
||||
|
||||
set.insert(obj2);
|
||||
expected.retain += 1;
|
||||
expected.release += 1;
|
||||
expected.assert_current();
|
||||
assert_eq!(set.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clear_release_dealloc() {
|
||||
let mut set = NSMutableSet::new();
|
||||
for _ in 0..4 {
|
||||
set.insert(RcTestObject::new());
|
||||
}
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
set.clear();
|
||||
expected.release += 4;
|
||||
expected.dealloc += 4;
|
||||
expected.assert_current();
|
||||
assert_eq!(set.len(), 0);
|
||||
}
|
||||
}
|
||||
232
third-party/vendor/objc2/src/foundation/mutable_string.rs
vendored
Normal file
232
third-party/vendor/objc2/src/foundation/mutable_string.rs
vendored
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
use core::cmp;
|
||||
use core::fmt;
|
||||
use core::ops::AddAssign;
|
||||
use core::str;
|
||||
|
||||
use super::{NSCopying, NSMutableCopying, NSObject, NSString};
|
||||
use crate::rc::{DefaultId, Id, Owned, Shared};
|
||||
use crate::{extern_class, extern_methods, msg_send_id, ClassType};
|
||||
|
||||
extern_class!(
|
||||
/// A dynamic plain-text Unicode string object.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsmutablestring?language=objc).
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct NSMutableString;
|
||||
|
||||
unsafe impl ClassType for NSMutableString {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSString;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
/// Creating mutable strings.
|
||||
unsafe impl NSMutableString {
|
||||
/// Construct an empty [`NSMutableString`].
|
||||
pub fn new() -> Id<Self, Owned> {
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
/// Creates a new [`NSMutableString`] by copying the given string slice.
|
||||
#[doc(alias = "initWithBytes:length:encoding:")]
|
||||
#[allow(clippy::should_implement_trait)] // Not really sure of a better name
|
||||
pub fn from_str(string: &str) -> Id<Self, Owned> {
|
||||
unsafe {
|
||||
let obj = super::string::from_str(Self::class(), string);
|
||||
Id::new(obj.cast()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new [`NSMutableString`] from the given [`NSString`].
|
||||
#[doc(alias = "initWithString:")]
|
||||
pub fn from_nsstring(string: &NSString) -> Id<Self, Owned> {
|
||||
unsafe {
|
||||
let obj = msg_send_id![Self::class(), alloc];
|
||||
msg_send_id![obj, initWithString: string]
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "initWithCapacity:")]
|
||||
pub fn with_capacity(capacity: usize) -> Id<Self, Owned> {
|
||||
unsafe {
|
||||
let obj = msg_send_id![Self::class(), alloc];
|
||||
msg_send_id![obj, initWithCapacity: capacity]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutating strings.
|
||||
unsafe impl NSMutableString {
|
||||
/// Appends the given [`NSString`] onto the end of this.
|
||||
#[doc(alias = "appendString:")]
|
||||
// SAFETY: The string is not nil
|
||||
#[sel(appendString:)]
|
||||
pub fn push_nsstring(&mut self, nsstring: &NSString);
|
||||
|
||||
/// Replaces the entire string.
|
||||
#[doc(alias = "setString:")]
|
||||
// SAFETY: The string is not nil
|
||||
#[sel(setString:)]
|
||||
pub fn replace(&mut self, nsstring: &NSString);
|
||||
|
||||
// TODO:
|
||||
// - deleteCharactersInRange:
|
||||
// - replaceCharactersInRange:withString:
|
||||
// - insertString:atIndex:
|
||||
// Figure out how these work on character boundaries
|
||||
}
|
||||
);
|
||||
|
||||
impl DefaultId for NSMutableString {
|
||||
type Ownership = Owned;
|
||||
|
||||
#[inline]
|
||||
fn default_id() -> Id<Self, Self::Ownership> {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl NSCopying for NSMutableString {
|
||||
type Ownership = Shared;
|
||||
type Output = NSString;
|
||||
}
|
||||
|
||||
unsafe impl NSMutableCopying for NSMutableString {
|
||||
type Output = NSMutableString;
|
||||
}
|
||||
|
||||
impl alloc::borrow::ToOwned for NSMutableString {
|
||||
type Owned = Id<NSMutableString, Owned>;
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
self.mutable_copy()
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<&NSString> for NSMutableString {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: &NSString) {
|
||||
self.push_nsstring(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<NSString> for NSMutableString {
|
||||
#[inline]
|
||||
fn eq(&self, other: &NSString) -> bool {
|
||||
PartialEq::eq(&**self, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<NSMutableString> for NSString {
|
||||
#[inline]
|
||||
fn eq(&self, other: &NSMutableString) -> bool {
|
||||
PartialEq::eq(self, &**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for NSMutableString {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
PartialOrd::partial_cmp(&**self, &**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<NSString> for NSMutableString {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &NSString) -> Option<cmp::Ordering> {
|
||||
PartialOrd::partial_cmp(&**self, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<NSMutableString> for NSString {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &NSMutableString) -> Option<cmp::Ordering> {
|
||||
PartialOrd::partial_cmp(self, &**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for NSMutableString {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
Ord::cmp(&**self, &**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for NSMutableString {
|
||||
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
|
||||
let nsstring = NSString::from_str(s);
|
||||
self.push_nsstring(&nsstring);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NSMutableString {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NSMutableString {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::format;
|
||||
use alloc::string::ToString;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn display_debug() {
|
||||
let s = NSMutableString::from_str("test\"123");
|
||||
assert_eq!(format!("{}", s), "test\"123");
|
||||
assert_eq!(format!("{:?}", s), r#""test\"123""#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_nsstring() {
|
||||
let s = NSString::from_str("abc");
|
||||
let s = NSMutableString::from_nsstring(&s);
|
||||
assert_eq!(&s.to_string(), "abc");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_push_nsstring() {
|
||||
let mut s = NSMutableString::from_str("abc");
|
||||
s.push_nsstring(&NSString::from_str("def"));
|
||||
*s += &NSString::from_str("ghi");
|
||||
assert_eq!(&s.to_string(), "abcdefghi");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replace() {
|
||||
let mut s = NSMutableString::from_str("abc");
|
||||
s.replace(&NSString::from_str("def"));
|
||||
assert_eq!(&s.to_string(), "def");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_capacity() {
|
||||
let mut s = NSMutableString::with_capacity(3);
|
||||
*s += &NSString::from_str("abc");
|
||||
*s += &NSString::from_str("def");
|
||||
assert_eq!(&s.to_string(), "abcdef");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_copy() {
|
||||
let s1 = NSMutableString::from_str("abc");
|
||||
let s2 = s1.copy();
|
||||
assert_ne!(Id::as_ptr(&s1), Id::as_ptr(&s2).cast());
|
||||
assert!(s2.is_kind_of::<NSString>());
|
||||
|
||||
let s3 = s1.mutable_copy();
|
||||
assert_ne!(Id::as_ptr(&s1), Id::as_ptr(&s3));
|
||||
assert!(s3.is_kind_of::<NSMutableString>());
|
||||
}
|
||||
}
|
||||
414
third-party/vendor/objc2/src/foundation/number.rs
vendored
Normal file
414
third-party/vendor/objc2/src/foundation/number.rs
vendored
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
use core::cmp::Ordering;
|
||||
use core::fmt;
|
||||
use core::hash;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use std::os::raw::{
|
||||
c_char, c_double, c_float, c_int, c_longlong, c_short, c_uchar, c_uint, c_ulonglong, c_ushort,
|
||||
};
|
||||
|
||||
use super::{
|
||||
CGFloat, NSComparisonResult, NSCopying, NSInteger, NSObject, NSString, NSUInteger, NSValue,
|
||||
};
|
||||
use crate::rc::{Id, Shared};
|
||||
use crate::{extern_class, extern_methods, msg_send, msg_send_id, ClassType, Encoding};
|
||||
|
||||
extern_class!(
|
||||
/// An object wrapper for primitive scalars.
|
||||
///
|
||||
/// This is the Objective-C equivalant of a Rust enum containing the
|
||||
/// common scalar types `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`,
|
||||
/// `u64`, `f32`, `f64` and the two C types `c_long` and `c_ulong`.
|
||||
///
|
||||
/// All accessor methods are safe, though they may return unexpected
|
||||
/// results if the number was not created from said type. Consult [Apple's
|
||||
/// documentation][apple-doc] for details.
|
||||
///
|
||||
/// Note that due to limitations in Objective-C type encodings, it is not
|
||||
/// possible to distinguish between an `NSNumber` created from [`bool`],
|
||||
/// and one created from an [`i8`]/[`u8`]. You should use the getter
|
||||
/// methods that fit your use-case instead!
|
||||
///
|
||||
/// This does not implement [`Eq`] nor [`Ord`], since it may contain a
|
||||
/// floating point value. Beware that the implementation of [`PartialEq`]
|
||||
/// and [`PartialOrd`] does not properly handle NaNs either. Compare
|
||||
/// [`NSNumber::encoding`] with [`Encoding::Float`] or
|
||||
/// [`Encoding::Double`], and use [`NSNumber::as_f32`] or
|
||||
/// [`NSNumber::as_f64`] to get the desired floating point value directly.
|
||||
///
|
||||
/// See [Apple's documentation][apple-doc] for more information.
|
||||
///
|
||||
/// [apple-doc]: https://developer.apple.com/documentation/foundation/nsnumber?language=objc
|
||||
pub struct NSNumber;
|
||||
|
||||
unsafe impl ClassType for NSNumber {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSValue;
|
||||
}
|
||||
);
|
||||
|
||||
// SAFETY: `NSNumber` is just a wrapper around an integer/float/bool, and it
|
||||
// is immutable.
|
||||
unsafe impl Sync for NSNumber {}
|
||||
unsafe impl Send for NSNumber {}
|
||||
|
||||
impl UnwindSafe for NSNumber {}
|
||||
impl RefUnwindSafe for NSNumber {}
|
||||
|
||||
macro_rules! def_new_fn {
|
||||
{$(
|
||||
$(#[$($m:meta)*])*
|
||||
($fn_name:ident($fn_inp:ty); $method_name:ident: $method_inp:ty),
|
||||
)*} => {$(
|
||||
$(#[$($m)*])*
|
||||
pub fn $fn_name(val: $fn_inp) -> Id<Self, Shared> {
|
||||
let val = val as $method_inp;
|
||||
unsafe {
|
||||
msg_send_id![Self::class(), $method_name: val]
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
/// Creation methods.
|
||||
impl NSNumber {
|
||||
def_new_fn! {
|
||||
(new_bool(bool); numberWithBool: bool),
|
||||
(new_i8(i8); numberWithChar: c_char),
|
||||
(new_u8(u8); numberWithUnsignedChar: c_uchar),
|
||||
(new_i16(i16); numberWithShort: c_short),
|
||||
(new_u16(u16); numberWithUnsignedShort: c_ushort),
|
||||
(new_i32(i32); numberWithInt: c_int),
|
||||
(new_u32(u32); numberWithUnsignedInt: c_uint),
|
||||
(new_i64(i64); numberWithLongLong: c_longlong),
|
||||
(new_u64(u64); numberWithUnsignedLongLong: c_ulonglong),
|
||||
(new_isize(isize); numberWithInteger: NSInteger),
|
||||
(new_usize(usize); numberWithUnsignedInteger: NSUInteger),
|
||||
(new_f32(f32); numberWithFloat: c_float),
|
||||
(new_f64(f64); numberWithDouble: c_double),
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new_cgfloat(val: CGFloat) -> Id<Self, Shared> {
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
{
|
||||
Self::new_f64(val)
|
||||
}
|
||||
#[cfg(not(target_pointer_width = "64"))]
|
||||
{
|
||||
Self::new_f32(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! def_get_fn {
|
||||
{$(
|
||||
$(#[$($m:meta)*])*
|
||||
($fn_name:ident -> $fn_ret:ty; $method_name:ident -> $method_ret:ty),
|
||||
)*} => {$(
|
||||
$(#[$($m)*])*
|
||||
pub fn $fn_name(&self) -> $fn_ret {
|
||||
let ret: $method_ret = unsafe { msg_send![self, $method_name] };
|
||||
ret as $fn_ret
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
/// Getter methods.
|
||||
impl NSNumber {
|
||||
def_get_fn! {
|
||||
(as_bool -> bool; boolValue -> bool),
|
||||
(as_i8 -> i8; charValue -> c_char),
|
||||
(as_u8 -> u8; unsignedCharValue -> c_uchar),
|
||||
(as_i16 -> i16; shortValue -> c_short),
|
||||
(as_u16 -> u16; unsignedShortValue -> c_ushort),
|
||||
(as_i32 -> i32; intValue -> c_int),
|
||||
(as_u32 -> u32; unsignedIntValue -> c_uint),
|
||||
// TODO: Getter methods for `long` and `unsigned long`
|
||||
(as_i64 -> i64; longLongValue -> c_longlong),
|
||||
(as_u64 -> u64; unsignedLongLongValue -> c_ulonglong),
|
||||
(as_isize -> isize; integerValue -> NSInteger),
|
||||
(as_usize -> usize; unsignedIntegerValue -> NSUInteger),
|
||||
(as_f32 -> f32; floatValue -> c_float),
|
||||
(as_f64 -> f64; doubleValue -> c_double),
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_cgfloat(&self) -> CGFloat {
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
{
|
||||
self.as_f64()
|
||||
}
|
||||
#[cfg(not(target_pointer_width = "64"))]
|
||||
{
|
||||
self.as_f32()
|
||||
}
|
||||
}
|
||||
|
||||
/// The Objective-C encoding of this `NSNumber`.
|
||||
///
|
||||
/// This is guaranteed to return one of:
|
||||
/// - [`Encoding::Char`]
|
||||
/// - [`Encoding::UChar`]
|
||||
/// - [`Encoding::Short`]
|
||||
/// - [`Encoding::UShort`]
|
||||
/// - [`Encoding::Int`]
|
||||
/// - [`Encoding::UInt`]
|
||||
/// - [`Encoding::Long`]
|
||||
/// - [`Encoding::ULong`]
|
||||
/// - [`Encoding::LongLong`]
|
||||
/// - [`Encoding::ULongLong`]
|
||||
/// - [`Encoding::Float`]
|
||||
/// - [`Encoding::Double`]
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Convert an `NSNumber` to/from an enumeration describing the different
|
||||
/// number properties.
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::Encoding;
|
||||
/// use objc2::foundation::NSNumber;
|
||||
/// use objc2::rc::{Id, Shared};
|
||||
///
|
||||
/// // Note: `bool` would convert to either `Signed` or `Unsigned`,
|
||||
/// // depending on platform
|
||||
/// #[derive(Copy, Clone)]
|
||||
/// pub enum Number {
|
||||
/// Signed(i64),
|
||||
/// Unsigned(u64),
|
||||
/// Floating(f64),
|
||||
/// }
|
||||
///
|
||||
/// impl Number {
|
||||
/// fn into_nsnumber(self) -> Id<NSNumber, Shared> {
|
||||
/// match self {
|
||||
/// Self::Signed(val) => NSNumber::new_i64(val),
|
||||
/// Self::Unsigned(val) => NSNumber::new_u64(val),
|
||||
/// Self::Floating(val) => NSNumber::new_f64(val),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl From<&NSNumber> for Number {
|
||||
/// fn from(n: &NSNumber) -> Self {
|
||||
/// match n.encoding() {
|
||||
/// Encoding::Char
|
||||
/// | Encoding::Short
|
||||
/// | Encoding::Int
|
||||
/// | Encoding::Long
|
||||
/// | Encoding::LongLong => Self::Signed(n.as_i64()),
|
||||
/// Encoding::UChar
|
||||
/// | Encoding::UShort
|
||||
/// | Encoding::UInt
|
||||
/// | Encoding::ULong
|
||||
/// | Encoding::ULongLong => Self::Unsigned(n.as_u64()),
|
||||
/// Encoding::Float
|
||||
/// | Encoding::Double => Self::Floating(n.as_f64()),
|
||||
/// _ => unreachable!(),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn encoding(&self) -> Encoding {
|
||||
// Use NSValue::encoding
|
||||
let enc = (**self)
|
||||
.encoding()
|
||||
.expect("NSNumber must have an encoding!");
|
||||
|
||||
// Guaranteed under "Subclassing Notes"
|
||||
// <https://developer.apple.com/documentation/foundation/nsnumber?language=objc#1776615>
|
||||
match enc {
|
||||
"c" => Encoding::Char,
|
||||
"C" => Encoding::UChar,
|
||||
"s" => Encoding::Short,
|
||||
"S" => Encoding::UShort,
|
||||
"i" => Encoding::Int,
|
||||
"I" => Encoding::UInt,
|
||||
"l" => Encoding::Long,
|
||||
"L" => Encoding::ULong,
|
||||
"q" => Encoding::LongLong,
|
||||
"Q" => Encoding::ULongLong,
|
||||
"f" => Encoding::Float,
|
||||
"d" => Encoding::Double,
|
||||
_ => unreachable!("invalid encoding for NSNumber"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSNumber {
|
||||
#[sel(compare:)]
|
||||
fn compare(&self, other: &Self) -> NSComparisonResult;
|
||||
|
||||
#[sel(isEqualToNumber:)]
|
||||
fn is_equal_to_number(&self, other: &Self) -> bool;
|
||||
|
||||
fn string(&self) -> Id<NSString, Shared> {
|
||||
unsafe { msg_send_id![self, stringValue] }
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
unsafe impl NSCopying for NSNumber {
|
||||
type Ownership = Shared;
|
||||
type Output = NSNumber;
|
||||
}
|
||||
|
||||
impl alloc::borrow::ToOwned for NSNumber {
|
||||
type Owned = Id<NSNumber, Shared>;
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
self.copy()
|
||||
}
|
||||
}
|
||||
|
||||
/// Beware: This uses the Objective-C method "isEqualToNumber:", which has
|
||||
/// different floating point NaN semantics than Rust!
|
||||
impl PartialEq for NSNumber {
|
||||
#[doc(alias = "isEqualToNumber:")]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
// Use isEqualToNumber: instaed of isEqual: since it is faster
|
||||
self.is_equal_to_number(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl hash::Hash for NSNumber {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
// Delegate to NSObject
|
||||
(***self).hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
/// Beware: This uses the Objective-C method "compare:", which has different
|
||||
/// floating point NaN semantics than Rust!
|
||||
impl PartialOrd for NSNumber {
|
||||
#[doc(alias = "compare:")]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
// Use Objective-C semantics for comparison
|
||||
Some(self.compare(other).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NSNumber {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.string(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NSNumber {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Delegate to -[NSObject description]
|
||||
// (happens to return the same as -[NSNumber stringValue])
|
||||
fmt::Debug::fmt(&***self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::format;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
let val = NSNumber::new_u32(13);
|
||||
assert_eq!(val.as_u32(), 13);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip() {
|
||||
assert!(NSNumber::new_bool(true).as_bool());
|
||||
assert!(!NSNumber::new_bool(false).as_bool());
|
||||
|
||||
fn assert_roundtrip_signed(val: i64) {
|
||||
assert_eq!(NSNumber::new_i8(val as i8).as_i8(), val as i8);
|
||||
assert_eq!(NSNumber::new_i16(val as i16).as_i16(), val as i16);
|
||||
assert_eq!(NSNumber::new_i32(val as i32).as_i32(), val as i32);
|
||||
assert_eq!(NSNumber::new_i64(val).as_i64(), val);
|
||||
assert_eq!(NSNumber::new_isize(val as isize).as_isize(), val as isize);
|
||||
}
|
||||
|
||||
assert_roundtrip_signed(i64::MIN);
|
||||
assert_roundtrip_signed(i32::MIN as i64);
|
||||
assert_roundtrip_signed(i16::MIN as i64);
|
||||
assert_roundtrip_signed(i8::MIN as i64);
|
||||
assert_roundtrip_signed(-1);
|
||||
assert_roundtrip_signed(0);
|
||||
assert_roundtrip_signed(1);
|
||||
assert_roundtrip_signed(i8::MAX as i64);
|
||||
assert_roundtrip_signed(i16::MAX as i64);
|
||||
assert_roundtrip_signed(i32::MAX as i64);
|
||||
assert_roundtrip_signed(i64::MAX);
|
||||
|
||||
fn assert_roundtrip_unsigned(val: u64) {
|
||||
assert_eq!(NSNumber::new_u8(val as u8).as_u8(), val as u8);
|
||||
assert_eq!(NSNumber::new_u16(val as u16).as_u16(), val as u16);
|
||||
assert_eq!(NSNumber::new_u32(val as u32).as_u32(), val as u32);
|
||||
assert_eq!(NSNumber::new_u64(val).as_u64(), val);
|
||||
assert_eq!(NSNumber::new_usize(val as usize).as_usize(), val as usize);
|
||||
}
|
||||
|
||||
assert_roundtrip_unsigned(0);
|
||||
assert_roundtrip_unsigned(1);
|
||||
assert_roundtrip_unsigned(u8::MAX as u64);
|
||||
assert_roundtrip_unsigned(u16::MAX as u64);
|
||||
assert_roundtrip_unsigned(u32::MAX as u64);
|
||||
assert_roundtrip_unsigned(u64::MAX);
|
||||
|
||||
fn assert_roundtrip_float(val: f64) {
|
||||
assert_eq!(NSNumber::new_f32(val as f32).as_f32(), val as f32);
|
||||
assert_eq!(NSNumber::new_f64(val).as_f64(), val);
|
||||
}
|
||||
|
||||
assert_roundtrip_float(0.0);
|
||||
assert_roundtrip_float(-1.0);
|
||||
assert_roundtrip_float(1.0);
|
||||
assert_roundtrip_float(f64::INFINITY);
|
||||
assert_roundtrip_float(-f64::INFINITY);
|
||||
assert_roundtrip_float(f64::MAX);
|
||||
assert_roundtrip_float(f64::MIN);
|
||||
assert_roundtrip_float(f64::MIN_POSITIVE);
|
||||
|
||||
assert!(NSNumber::new_f32(f32::NAN).as_f32().is_nan());
|
||||
assert!(NSNumber::new_f64(f64::NAN).as_f64().is_nan());
|
||||
assert!(NSNumber::new_f32(-f32::NAN).as_f32().is_nan());
|
||||
assert!(NSNumber::new_f64(-f64::NAN).as_f64().is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cast_between_types() {
|
||||
assert_eq!(NSNumber::new_bool(true).as_i8(), 1);
|
||||
assert_eq!(NSNumber::new_i32(i32::MAX).as_u32(), i32::MAX as u32);
|
||||
assert_eq!(NSNumber::new_f32(1.0).as_u32(), 1);
|
||||
assert_eq!(NSNumber::new_f32(1.0).as_u32(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equality() {
|
||||
let val1 = NSNumber::new_u32(123);
|
||||
let val2 = NSNumber::new_u32(123);
|
||||
let val3 = NSNumber::new_u8(123);
|
||||
assert_eq!(val1, val1);
|
||||
assert_eq!(val1, val2);
|
||||
assert_eq!(val1, val3);
|
||||
|
||||
let val4 = NSNumber::new_u32(456);
|
||||
assert_ne!(val1, val4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_debug() {
|
||||
fn assert_display_debug<T: fmt::Debug + fmt::Display>(val: T, expected: &str) {
|
||||
// The two impls for these happen to be the same
|
||||
assert_eq!(format!("{}", val), expected);
|
||||
assert_eq!(format!("{:?}", val), expected);
|
||||
}
|
||||
assert_display_debug(NSNumber::new_u8(171), "171");
|
||||
assert_display_debug(NSNumber::new_i8(-12), "-12");
|
||||
assert_display_debug(NSNumber::new_u32(0xdeadbeef), "3735928559");
|
||||
assert_display_debug(NSNumber::new_f32(1.1), "1.1");
|
||||
assert_display_debug(NSNumber::new_f32(1.0), "1");
|
||||
assert_display_debug(NSNumber::new_bool(true), "1");
|
||||
assert_display_debug(NSNumber::new_bool(false), "0");
|
||||
}
|
||||
}
|
||||
205
third-party/vendor/objc2/src/foundation/object.rs
vendored
Normal file
205
third-party/vendor/objc2/src/foundation/object.rs
vendored
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
use core::fmt;
|
||||
use core::hash;
|
||||
|
||||
use super::NSString;
|
||||
use crate::rc::{DefaultId, Id, Owned, Shared};
|
||||
use crate::runtime::{Class, Object};
|
||||
use crate::{ClassType, __inner_extern_class, class, extern_methods, msg_send_id};
|
||||
|
||||
__inner_extern_class! {
|
||||
@__inner
|
||||
pub struct NSObject () {}
|
||||
|
||||
unsafe impl () for NSObject {
|
||||
INHERITS = [Object];
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl ClassType for NSObject {
|
||||
type Super = Object;
|
||||
const NAME: &'static str = "NSObject";
|
||||
|
||||
#[inline]
|
||||
fn class() -> &'static Class {
|
||||
class!(NSObject)
|
||||
}
|
||||
|
||||
fn as_super(&self) -> &Self::Super {
|
||||
&self.__inner
|
||||
}
|
||||
|
||||
fn as_super_mut(&mut self) -> &mut Self::Super {
|
||||
&mut self.__inner
|
||||
}
|
||||
}
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSObject {
|
||||
pub fn new() -> Id<Self, Owned> {
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
#[sel(isKindOfClass:)]
|
||||
fn is_kind_of_inner(&self, cls: &Class) -> bool;
|
||||
|
||||
#[sel(isEqual:)]
|
||||
fn is_equal(&self, other: &Self) -> bool;
|
||||
|
||||
#[sel(hash)]
|
||||
fn hash_code(&self) -> usize;
|
||||
|
||||
/// Check if the object is an instance of the class, or one of it's
|
||||
/// subclasses.
|
||||
///
|
||||
/// See [Apple's documentation][apple-doc] for more details on what you
|
||||
/// may (and what you may not) do with this information.
|
||||
///
|
||||
/// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418511-iskindofclass
|
||||
#[doc(alias = "isKindOfClass:")]
|
||||
pub fn is_kind_of<T: ClassType>(&self) -> bool {
|
||||
self.is_kind_of_inner(T::class())
|
||||
}
|
||||
|
||||
// Note: We don't provide a method to convert `NSObject` to `T` based on
|
||||
// `is_kind_of`, since that is not possible to do in general!
|
||||
//
|
||||
// For example, something may have a return type of `NSString`, while
|
||||
// behind the scenes they really return `NSMutableString` and expect it to
|
||||
// not be modified.
|
||||
}
|
||||
);
|
||||
|
||||
/// Objective-C equality has approximately the same semantics as Rust
|
||||
/// equality (although less aptly specified).
|
||||
///
|
||||
/// At the very least, equality is _expected_ to be symmetric and
|
||||
/// transitive, and that's about the best we can do.
|
||||
///
|
||||
/// See also <https://nshipster.com/equality/>
|
||||
impl PartialEq for NSObject {
|
||||
#[inline]
|
||||
#[doc(alias = "isEqual:")]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.is_equal(other)
|
||||
}
|
||||
}
|
||||
|
||||
/// Most types' equality is reflexive.
|
||||
impl Eq for NSObject {}
|
||||
|
||||
/// Hashing in Objective-C has the exact same requirement as in Rust:
|
||||
///
|
||||
/// > If two objects are equal (as determined by the isEqual: method),
|
||||
/// > they must have the same hash value.
|
||||
///
|
||||
/// See <https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418859-hash>
|
||||
impl hash::Hash for NSObject {
|
||||
#[inline]
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
self.hash_code().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NSObject {
|
||||
#[doc(alias = "description")]
|
||||
#[doc(alias = "debugDescription")]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Get description
|
||||
let description: Option<Id<NSString, Shared>> = unsafe { msg_send_id![self, description] };
|
||||
|
||||
match description {
|
||||
// Attempt to format with description
|
||||
Some(description) => fmt::Display::fmt(&description, f),
|
||||
// If description was `NULL`, use `Object`'s `Debug` impl instead
|
||||
None => {
|
||||
let obj: &Object = self;
|
||||
fmt::Debug::fmt(obj, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultId for NSObject {
|
||||
type Ownership = Owned;
|
||||
|
||||
#[inline]
|
||||
fn default_id() -> Id<Self, Self::Ownership> {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use alloc::format;
|
||||
|
||||
#[test]
|
||||
fn test_deref() {
|
||||
let mut obj: Id<NSObject, Owned> = NSObject::new();
|
||||
let _: &NSObject = &obj;
|
||||
let _: &mut NSObject = &mut obj;
|
||||
let _: &Object = &obj;
|
||||
let _: &mut Object = &mut obj;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_as_ref_borrow() {
|
||||
use core::borrow::{Borrow, BorrowMut};
|
||||
|
||||
fn impls_as_ref<T: AsRef<U> + Borrow<U> + ?Sized, U: ?Sized>(_: &T) {}
|
||||
fn impls_as_mut<T: AsMut<U> + BorrowMut<U> + ?Sized, U: ?Sized>(_: &mut T) {}
|
||||
|
||||
let mut obj = NSObject::new();
|
||||
impls_as_ref::<Id<NSObject, Owned>, NSObject>(&obj);
|
||||
impls_as_mut::<Id<NSObject, Owned>, NSObject>(&mut obj);
|
||||
impls_as_ref::<NSObject, NSObject>(&obj);
|
||||
impls_as_mut::<NSObject, NSObject>(&mut obj);
|
||||
impls_as_ref::<NSObject, Object>(&obj);
|
||||
impls_as_mut::<NSObject, Object>(&mut obj);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equality() {
|
||||
let obj1 = NSObject::new();
|
||||
assert_eq!(obj1, obj1);
|
||||
|
||||
let obj2 = NSObject::new();
|
||||
assert_ne!(obj1, obj2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash() {
|
||||
use core::hash::Hasher;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::Hash;
|
||||
|
||||
let obj1 = NSObject::new();
|
||||
|
||||
let mut hashstate1 = DefaultHasher::new();
|
||||
let mut hashstate2 = DefaultHasher::new();
|
||||
|
||||
obj1.hash(&mut hashstate1);
|
||||
obj1.hash(&mut hashstate2);
|
||||
|
||||
assert_eq!(hashstate1.finish(), hashstate2.finish());
|
||||
|
||||
let obj2 = NSObject::new();
|
||||
let mut hashstate2 = DefaultHasher::new();
|
||||
obj2.hash(&mut hashstate2);
|
||||
assert_ne!(hashstate1.finish(), hashstate2.finish());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let obj = NSObject::new();
|
||||
let expected = format!("<NSObject: {:p}>", &*obj);
|
||||
assert_eq!(format!("{:?}", obj), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_kind_of() {
|
||||
let obj = NSObject::new();
|
||||
assert!(obj.is_kind_of::<NSObject>());
|
||||
assert!(!obj.is_kind_of::<NSString>());
|
||||
}
|
||||
}
|
||||
65
third-party/vendor/objc2/src/foundation/process_info.rs
vendored
Normal file
65
third-party/vendor/objc2/src/foundation/process_info.rs
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
use core::fmt;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
use super::{NSObject, NSString};
|
||||
use crate::rc::{Id, Shared};
|
||||
use crate::{extern_class, extern_methods, msg_send_id, ClassType};
|
||||
|
||||
extern_class!(
|
||||
/// A collection of information about the current process.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsprocessinfo?language=objc).
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct NSProcessInfo;
|
||||
|
||||
unsafe impl ClassType for NSProcessInfo {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
// SAFETY: The documentation explicitly states:
|
||||
// > NSProcessInfo is thread-safe in macOS 10.7 and later.
|
||||
unsafe impl Send for NSProcessInfo {}
|
||||
unsafe impl Sync for NSProcessInfo {}
|
||||
|
||||
impl UnwindSafe for NSProcessInfo {}
|
||||
impl RefUnwindSafe for NSProcessInfo {}
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSProcessInfo {
|
||||
pub fn process_info() -> Id<NSProcessInfo, Shared> {
|
||||
unsafe { msg_send_id![Self::class(), processInfo] }
|
||||
}
|
||||
|
||||
pub fn process_name(&self) -> Id<NSString, Shared> {
|
||||
unsafe { msg_send_id![self, processName] }
|
||||
}
|
||||
|
||||
// TODO: This contains a lot more important functionality!
|
||||
}
|
||||
);
|
||||
|
||||
impl fmt::Debug for NSProcessInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("NSProcessInfo")
|
||||
.field("process_name", &self.process_name())
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::format;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let info = NSProcessInfo::process_info();
|
||||
let expected = format!(
|
||||
"NSProcessInfo {{ process_name: {:?}, .. }}",
|
||||
info.process_name()
|
||||
);
|
||||
assert_eq!(format!("{:?}", info), expected);
|
||||
}
|
||||
}
|
||||
183
third-party/vendor/objc2/src/foundation/range.rs
vendored
Normal file
183
third-party/vendor/objc2/src/foundation/range.rs
vendored
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
use core::ops::Range;
|
||||
|
||||
use super::NSUInteger;
|
||||
use crate::{Encode, Encoding, RefEncode};
|
||||
|
||||
/// TODO.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsrange?language=objc).
|
||||
#[repr(C)]
|
||||
// PartialEq is same as NSEqualRanges
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct NSRange {
|
||||
/// The lower bound of the range (inclusive).
|
||||
pub location: NSUInteger,
|
||||
/// The number of items in the range, starting from `location`.
|
||||
pub length: NSUInteger,
|
||||
}
|
||||
|
||||
impl NSRange {
|
||||
/// Create a new range with the given values.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::NSRange;
|
||||
/// assert_eq!(NSRange::new(3, 2), NSRange::from(3..5));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[doc(alias = "NSMakeRange")]
|
||||
pub const fn new(location: usize, length: usize) -> Self {
|
||||
// Equivalent to NSMakeRange
|
||||
Self { location, length }
|
||||
}
|
||||
|
||||
/// Returns `true` if the range contains no items.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::NSRange;
|
||||
///
|
||||
/// assert!(!NSRange::from(3..5).is_empty());
|
||||
/// assert!( NSRange::from(3..3).is_empty());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.length == 0
|
||||
}
|
||||
|
||||
/// Returns `true` if the index is within the range.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::NSRange;
|
||||
///
|
||||
/// assert!(!NSRange::from(3..5).contains(2));
|
||||
/// assert!( NSRange::from(3..5).contains(3));
|
||||
/// assert!( NSRange::from(3..5).contains(4));
|
||||
/// assert!(!NSRange::from(3..5).contains(5));
|
||||
///
|
||||
/// assert!(!NSRange::from(3..3).contains(3));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[doc(alias = "NSLocationInRange")]
|
||||
pub fn contains(&self, index: usize) -> bool {
|
||||
// Same as NSLocationInRange
|
||||
if let Some(len) = index.checked_sub(self.location) {
|
||||
len < self.length
|
||||
} else {
|
||||
// index < self.location
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the upper bound of the range (exclusive).
|
||||
#[inline]
|
||||
#[doc(alias = "NSMaxRange")]
|
||||
pub fn end(&self) -> usize {
|
||||
self.location
|
||||
.checked_add(self.length)
|
||||
.expect("NSRange too large")
|
||||
}
|
||||
|
||||
// TODO: https://developer.apple.com/documentation/foundation/1408420-nsrangefromstring
|
||||
// TODO: NSUnionRange
|
||||
// TODO: NSIntersectionRange
|
||||
}
|
||||
|
||||
// Sadly, we can't do this:
|
||||
// impl RangeBounds<usize> for NSRange {
|
||||
// fn start_bound(&self) -> Bound<&usize> {
|
||||
// Bound::Included(&self.location)
|
||||
// }
|
||||
// fn end_bound(&self) -> Bound<&usize> {
|
||||
// Bound::Excluded(&(self.location + self.length))
|
||||
// }
|
||||
// }
|
||||
|
||||
impl From<Range<usize>> for NSRange {
|
||||
fn from(range: Range<usize>) -> Self {
|
||||
let length = range
|
||||
.end
|
||||
.checked_sub(range.start)
|
||||
.expect("Range end < start");
|
||||
Self {
|
||||
location: range.start,
|
||||
length,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NSRange> for Range<usize> {
|
||||
#[inline]
|
||||
fn from(nsrange: NSRange) -> Self {
|
||||
Self {
|
||||
start: nsrange.location,
|
||||
end: nsrange.end(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSRange {
|
||||
const ENCODING: Encoding = Encoding::Struct("_NSRange", &[usize::ENCODING, usize::ENCODING]);
|
||||
}
|
||||
|
||||
unsafe impl RefEncode for NSRange {
|
||||
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_from_range() {
|
||||
let cases: &[(Range<usize>, NSRange)] = &[
|
||||
(0..0, NSRange::new(0, 0)),
|
||||
(0..10, NSRange::new(0, 10)),
|
||||
(10..10, NSRange::new(10, 0)),
|
||||
(10..20, NSRange::new(10, 10)),
|
||||
];
|
||||
|
||||
for (range, expected) in cases {
|
||||
assert_eq!(NSRange::from(range.clone()), *expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "Range end < start"]
|
||||
#[allow(clippy::reversed_empty_ranges)]
|
||||
fn test_from_range_inverted() {
|
||||
let _ = NSRange::from(10..0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_contains() {
|
||||
let range = NSRange::from(10..20);
|
||||
assert!(!range.contains(0));
|
||||
assert!(!range.contains(9));
|
||||
assert!(range.contains(10));
|
||||
assert!(range.contains(11));
|
||||
assert!(!range.contains(20));
|
||||
assert!(!range.contains(21));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_end() {
|
||||
let range = NSRange::from(10..20);
|
||||
assert!(!range.contains(0));
|
||||
assert!(!range.contains(9));
|
||||
assert!(range.contains(10));
|
||||
assert!(range.contains(11));
|
||||
assert!(!range.contains(20));
|
||||
assert!(!range.contains(21));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "NSRange too large"]
|
||||
fn test_end_large() {
|
||||
let _ = NSRange::new(usize::MAX, usize::MAX).end();
|
||||
}
|
||||
}
|
||||
680
third-party/vendor/objc2/src/foundation/set.rs
vendored
Normal file
680
third-party/vendor/objc2/src/foundation/set.rs
vendored
Normal file
|
|
@ -0,0 +1,680 @@
|
|||
use alloc::vec::Vec;
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
use super::{
|
||||
NSArray, NSCopying, NSEnumerator, NSFastEnumeration, NSFastEnumerator, NSMutableCopying,
|
||||
NSMutableSet, NSObject,
|
||||
};
|
||||
use crate::rc::{DefaultId, Id, Owned, Ownership, Shared, SliceId};
|
||||
use crate::runtime::Class;
|
||||
use crate::{ClassType, Message, __inner_extern_class, extern_methods, msg_send, msg_send_id};
|
||||
|
||||
__inner_extern_class!(
|
||||
/// An immutable unordered collection of unique objects.
|
||||
///
|
||||
/// See [Apple's documentation][apple-doc].
|
||||
///
|
||||
/// [apple-doc]: https://developer.apple.com/documentation/foundation/nsset?language=objc
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct NSSet<T: Message, O: Ownership = Shared> {
|
||||
item: PhantomData<Id<T, O>>,
|
||||
notunwindsafe: PhantomData<&'static mut ()>,
|
||||
}
|
||||
|
||||
unsafe impl<T: Message, O: Ownership> ClassType for NSSet<T, O> {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
// SAFETY: Same as NSArray<T, O>
|
||||
unsafe impl<T: Message + Sync + Send> Sync for NSSet<T, Shared> {}
|
||||
unsafe impl<T: Message + Sync + Send> Send for NSSet<T, Shared> {}
|
||||
unsafe impl<T: Message + Sync> Sync for NSSet<T, Owned> {}
|
||||
unsafe impl<T: Message + Send> Send for NSSet<T, Owned> {}
|
||||
|
||||
// SAFETY: Same as NSArray<T, O>
|
||||
impl<T: Message + RefUnwindSafe, O: Ownership> RefUnwindSafe for NSSet<T, O> {}
|
||||
impl<T: Message + RefUnwindSafe> UnwindSafe for NSSet<T, Shared> {}
|
||||
impl<T: Message + UnwindSafe> UnwindSafe for NSSet<T, Owned> {}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) unsafe fn with_objects<T: Message + ?Sized, R: Message, O: Ownership>(
|
||||
cls: &Class,
|
||||
objects: &[&T],
|
||||
) -> Id<R, O> {
|
||||
unsafe {
|
||||
msg_send_id![
|
||||
msg_send_id![cls, alloc],
|
||||
initWithObjects: objects.as_ptr(),
|
||||
count: objects.len()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl<T: Message, O: Ownership> NSSet<T, O> {
|
||||
/// Creates an empty [`NSSet`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSSet, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let set = NSSet::<NSString>::new();
|
||||
/// ```
|
||||
pub fn new() -> Id<Self, Shared> {
|
||||
// SAFETY:
|
||||
// - `new` may not create a new object, but instead return a shared
|
||||
// instance. We remedy this by returning `Id<Self, Shared>`.
|
||||
// - `O` don't actually matter here! E.g. `NSSet<T, Owned>` is
|
||||
// perfectly legal, since the set doesn't have any elements, and
|
||||
// hence the notion of ownership over the elements is void.
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
/// Creates an [`NSSet`] from a vector.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSSet, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let strs = ["one", "two", "three"].map(NSString::from_str).to_vec();
|
||||
/// let set = NSSet::from_vec(strs);
|
||||
/// ```
|
||||
pub fn from_vec(vec: Vec<Id<T, O>>) -> Id<Self, O> {
|
||||
// SAFETY:
|
||||
// When we know that we have ownership over the variables, we also
|
||||
// know that there cannot be another set in existence with the same
|
||||
// objects, so `Id<NSSet<T, Owned>, Owned>` is safe to return when
|
||||
// we receive `Vec<Id<T, Owned>>`.
|
||||
unsafe { with_objects(Self::class(), vec.as_slice_ref()) }
|
||||
}
|
||||
|
||||
/// Returns the number of elements in the set.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSSet, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let strs = ["one", "two", "three"].map(NSString::from_str);
|
||||
/// let set = NSSet::from_slice(&strs);
|
||||
/// assert_eq!(set.len(), 3);
|
||||
/// ```
|
||||
#[doc(alias = "count")]
|
||||
#[sel(count)]
|
||||
pub fn len(&self) -> usize;
|
||||
|
||||
/// Returns `true` if the set contains no elements.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSSet, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let set = NSSet::<NSString>::new();
|
||||
/// assert!(set.is_empty());
|
||||
/// ```
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Returns a reference to one of the objects in the set, or `None` if
|
||||
/// the set is empty.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSSet, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let strs = ["one", "two", "three"].map(NSString::from_str);
|
||||
/// let set = NSSet::from_slice(&strs);
|
||||
/// let any = set.get_any().unwrap();
|
||||
/// assert!(any == &*strs[0] || any == &*strs[1] || any == &*strs[2]);
|
||||
/// ```
|
||||
#[doc(alias = "anyObject")]
|
||||
#[sel(anyObject)]
|
||||
pub fn get_any(&self) -> Option<&T>;
|
||||
|
||||
/// An iterator visiting all elements in arbitrary order.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSSet, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let strs = ["one", "two", "three"].map(NSString::from_str);
|
||||
/// let set = NSSet::from_slice(&strs);
|
||||
/// for s in set.iter() {
|
||||
/// println!("{s}");
|
||||
/// }
|
||||
/// ```
|
||||
#[doc(alias = "objectEnumerator")]
|
||||
pub fn iter(&self) -> NSEnumerator<'_, T> {
|
||||
unsafe {
|
||||
let result = msg_send![self, objectEnumerator];
|
||||
NSEnumerator::from_ptr(result)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a [`Vec`] containing the set's elements, consuming the set.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSMutableString, NSSet};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let strs = vec![
|
||||
/// NSMutableString::from_str("one"),
|
||||
/// NSMutableString::from_str("two"),
|
||||
/// NSMutableString::from_str("three"),
|
||||
/// ];
|
||||
/// let set = NSSet::from_vec(strs);
|
||||
/// let vec = NSSet::into_vec(set);
|
||||
/// assert_eq!(vec.len(), 3);
|
||||
/// ```
|
||||
pub fn into_vec(set: Id<Self, O>) -> Vec<Id<T, O>> {
|
||||
set.into_iter()
|
||||
.map(|obj| unsafe { Id::retain(obj as *const T as *mut T).unwrap_unchecked() })
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Message> NSSet<T, Shared> {
|
||||
/// Creates an [`NSSet`] from a slice.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSSet, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let strs = ["one", "two", "three"].map(NSString::from_str);
|
||||
/// let set = NSSet::from_slice(&strs);
|
||||
/// ```
|
||||
pub fn from_slice(slice: &[Id<T, Shared>]) -> Id<Self, Shared> {
|
||||
// SAFETY:
|
||||
// Taking `&T` would not be sound, since the `&T` could come from
|
||||
// an `Id<T, Owned>` that would now no longer be owned!
|
||||
//
|
||||
// We always return `Id<NSSet<T, Shared>, Shared>` because the
|
||||
// elements are shared.
|
||||
unsafe { with_objects(Self::class(), slice.as_slice_ref()) }
|
||||
}
|
||||
|
||||
/// Returns an [`NSArray`] containing the set's elements, or an empty
|
||||
/// array if the set is empty.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSNumber, NSSet, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let nums = [1, 2, 3];
|
||||
/// let set = NSSet::from_slice(&nums.map(NSNumber::new_i32));
|
||||
///
|
||||
/// assert_eq!(set.to_array().len(), 3);
|
||||
/// assert!(set.to_array().iter().all(|i| nums.contains(&i.as_i32())));
|
||||
/// ```
|
||||
#[doc(alias = "allObjects")]
|
||||
pub fn to_array(&self) -> Id<NSArray<T, Shared>, Shared> {
|
||||
// SAFETY:
|
||||
// We only define this method for sets with shared elements
|
||||
// because we can't return copies of owned elements.
|
||||
unsafe { msg_send_id![self, allObjects] }
|
||||
}
|
||||
}
|
||||
|
||||
// We're explicit about `T` being `PartialEq` for these methods because the
|
||||
// set compares the input value(s) with elements in the set
|
||||
// For comparison: Rust's HashSet requires similar methods to be `Hash` + `Eq`
|
||||
unsafe impl<T: Message + PartialEq, O: Ownership> NSSet<T, O> {
|
||||
/// Returns `true` if the set contains a value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSSet, NSString};
|
||||
/// use objc2::ns_string;
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let strs = ["one", "two", "three"].map(NSString::from_str);
|
||||
/// let set = NSSet::from_slice(&strs);
|
||||
/// assert!(set.contains(ns_string!("one")));
|
||||
/// ```
|
||||
#[doc(alias = "containsObject:")]
|
||||
#[sel(containsObject:)]
|
||||
pub fn contains(&self, value: &T) -> bool;
|
||||
|
||||
/// Returns a reference to the value in the set, if any, that is equal
|
||||
/// to the given value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSSet, NSString};
|
||||
/// use objc2::ns_string;
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let strs = ["one", "two", "three"].map(NSString::from_str);
|
||||
/// let set = NSSet::from_slice(&strs);
|
||||
/// assert_eq!(set.get(ns_string!("one")), Some(&*strs[0]));
|
||||
/// assert_eq!(set.get(ns_string!("four")), None);
|
||||
/// ```
|
||||
#[doc(alias = "member:")]
|
||||
#[sel(member:)]
|
||||
pub fn get(&self, value: &T) -> Option<&T>;
|
||||
|
||||
/// Returns `true` if the set is a subset of another, i.e., `other`
|
||||
/// contains at least all the values in `self`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSSet, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let set1 = NSSet::from_slice(&["one", "two"].map(NSString::from_str));
|
||||
/// let set2 = NSSet::from_slice(&["one", "two", "three"].map(NSString::from_str));
|
||||
///
|
||||
/// assert!(set1.is_subset(&set2));
|
||||
/// assert!(!set2.is_subset(&set1));
|
||||
/// ```
|
||||
#[doc(alias = "isSubsetOfSet:")]
|
||||
#[sel(isSubsetOfSet:)]
|
||||
pub fn is_subset(&self, other: &NSSet<T, O>) -> bool;
|
||||
|
||||
/// Returns `true` if the set is a superset of another, i.e., `self`
|
||||
/// contains at least all the values in `other`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSSet, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let set1 = NSSet::from_slice(&["one", "two"].map(NSString::from_str));
|
||||
/// let set2 = NSSet::from_slice(&["one", "two", "three"].map(NSString::from_str));
|
||||
///
|
||||
/// assert!(!set1.is_superset(&set2));
|
||||
/// assert!(set2.is_superset(&set1));
|
||||
/// ```
|
||||
pub fn is_superset(&self, other: &NSSet<T, O>) -> bool {
|
||||
other.is_subset(self)
|
||||
}
|
||||
|
||||
#[sel(intersectsSet:)]
|
||||
fn intersects_set(&self, other: &NSSet<T, O>) -> bool;
|
||||
|
||||
/// Returns `true` if `self` has no elements in common with `other`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSSet, NSString};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let set1 = NSSet::from_slice(&["one", "two"].map(NSString::from_str));
|
||||
/// let set2 = NSSet::from_slice(&["one", "two", "three"].map(NSString::from_str));
|
||||
/// let set3 = NSSet::from_slice(&["four", "five", "six"].map(NSString::from_str));
|
||||
///
|
||||
/// assert!(!set1.is_disjoint(&set2));
|
||||
/// assert!(set1.is_disjoint(&set3));
|
||||
/// assert!(set2.is_disjoint(&set3));
|
||||
/// ```
|
||||
pub fn is_disjoint(&self, other: &NSSet<T, O>) -> bool {
|
||||
!self.intersects_set(other)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
unsafe impl<T: Message> NSCopying for NSSet<T, Shared> {
|
||||
type Ownership = Shared;
|
||||
type Output = NSSet<T, Shared>;
|
||||
}
|
||||
|
||||
unsafe impl<T: Message> NSMutableCopying for NSSet<T, Shared> {
|
||||
type Output = NSMutableSet<T, Shared>;
|
||||
}
|
||||
|
||||
impl<T: Message> alloc::borrow::ToOwned for NSSet<T, Shared> {
|
||||
type Owned = Id<NSSet<T, Shared>, Shared>;
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
self.copy()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Message, O: Ownership> NSFastEnumeration for NSSet<T, O> {
|
||||
type Item = T;
|
||||
}
|
||||
|
||||
impl<'a, T: Message, O: Ownership> IntoIterator for &'a NSSet<T, O> {
|
||||
type Item = &'a T;
|
||||
type IntoIter = NSFastEnumerator<'a, NSSet<T, O>>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter_fast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message, O: Ownership> DefaultId for NSSet<T, O> {
|
||||
type Ownership = Shared;
|
||||
|
||||
#[inline]
|
||||
fn default_id() -> Id<Self, Self::Ownership> {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug + Message, O: Ownership> fmt::Debug for NSSet<T, O> {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_set().entries(self.iter_fast()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::format;
|
||||
use alloc::vec;
|
||||
|
||||
use super::*;
|
||||
use crate::foundation::{NSMutableString, NSNumber, NSString};
|
||||
use crate::ns_string;
|
||||
use crate::rc::{RcTestObject, ThreadTestData};
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
let set = NSSet::<NSString>::new();
|
||||
assert!(set.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_vec() {
|
||||
let set = NSSet::<NSString>::from_vec(Vec::new());
|
||||
assert!(set.is_empty());
|
||||
|
||||
let strs = ["one", "two", "three"].map(NSString::from_str);
|
||||
let set = NSSet::from_vec(strs.to_vec());
|
||||
assert!(strs.into_iter().all(|s| set.contains(&s)));
|
||||
|
||||
let nums = [1, 2, 3].map(NSNumber::new_i32);
|
||||
let set = NSSet::from_vec(nums.to_vec());
|
||||
assert!(nums.into_iter().all(|n| set.contains(&n)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_slice() {
|
||||
let set = NSSet::<NSString>::from_slice(&[]);
|
||||
assert!(set.is_empty());
|
||||
|
||||
let strs = ["one", "two", "three"].map(NSString::from_str);
|
||||
let set = NSSet::from_slice(&strs);
|
||||
assert!(strs.into_iter().all(|s| set.contains(&s)));
|
||||
|
||||
let nums = [1, 2, 3].map(NSNumber::new_i32);
|
||||
let set = NSSet::from_slice(&nums);
|
||||
assert!(nums.into_iter().all(|n| set.contains(&n)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_len() {
|
||||
let set = NSSet::<NSString>::new();
|
||||
assert!(set.is_empty());
|
||||
|
||||
let set = NSSet::from_slice(&["one", "two", "two"].map(NSString::from_str));
|
||||
assert_eq!(set.len(), 2);
|
||||
|
||||
let set = NSSet::from_vec(vec![NSObject::new(), NSObject::new(), NSObject::new()]);
|
||||
assert_eq!(set.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get() {
|
||||
let set = NSSet::<NSString>::new();
|
||||
assert!(set.get(ns_string!("one")).is_none());
|
||||
|
||||
let set = NSSet::from_slice(&["one", "two", "two"].map(NSString::from_str));
|
||||
assert!(set.get(ns_string!("two")).is_some());
|
||||
assert!(set.get(ns_string!("three")).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_return_lifetime() {
|
||||
let set = NSSet::from_slice(&["one", "two", "two"].map(NSString::from_str));
|
||||
|
||||
let res = {
|
||||
let value = NSString::from_str("one");
|
||||
set.get(&value)
|
||||
};
|
||||
|
||||
assert_eq!(res, Some(ns_string!("one")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_any() {
|
||||
let set = NSSet::<NSString>::new();
|
||||
assert!(set.get_any().is_none());
|
||||
|
||||
let strs = ["one", "two", "three"].map(NSString::from_str);
|
||||
let set = NSSet::from_slice(&strs);
|
||||
let any = set.get_any().unwrap();
|
||||
assert!(any == &*strs[0] || any == &*strs[1] || any == &*strs[2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_contains() {
|
||||
let set = NSSet::<NSString>::new();
|
||||
assert!(!set.contains(ns_string!("one")));
|
||||
|
||||
let set = NSSet::from_slice(&["one", "two", "two"].map(NSString::from_str));
|
||||
assert!(set.contains(ns_string!("one")));
|
||||
assert!(!set.contains(ns_string!("three")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_subset() {
|
||||
let set1 = NSSet::from_slice(&["one", "two"].map(NSString::from_str));
|
||||
let set2 = NSSet::from_slice(&["one", "two", "three"].map(NSString::from_str));
|
||||
|
||||
assert!(set1.is_subset(&set2));
|
||||
assert!(!set2.is_subset(&set1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_superset() {
|
||||
let set1 = NSSet::from_slice(&["one", "two"].map(NSString::from_str));
|
||||
let set2 = NSSet::from_slice(&["one", "two", "three"].map(NSString::from_str));
|
||||
|
||||
assert!(!set1.is_superset(&set2));
|
||||
assert!(set2.is_superset(&set1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_disjoint() {
|
||||
let set1 = NSSet::from_slice(&["one", "two"].map(NSString::from_str));
|
||||
let set2 = NSSet::from_slice(&["one", "two", "three"].map(NSString::from_str));
|
||||
let set3 = NSSet::from_slice(&["four", "five", "six"].map(NSString::from_str));
|
||||
|
||||
assert!(!set1.is_disjoint(&set2));
|
||||
assert!(set1.is_disjoint(&set3));
|
||||
assert!(set2.is_disjoint(&set3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_array() {
|
||||
let nums = [1, 2, 3];
|
||||
let set = NSSet::from_slice(&nums.map(NSNumber::new_i32));
|
||||
|
||||
assert_eq!(set.to_array().len(), 3);
|
||||
assert!(set.to_array().iter().all(|i| nums.contains(&i.as_i32())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iter() {
|
||||
let nums = [1, 2, 3];
|
||||
let set = NSSet::from_slice(&nums.map(NSNumber::new_i32));
|
||||
|
||||
assert_eq!(set.iter().count(), 3);
|
||||
assert!(set.iter().all(|i| nums.contains(&i.as_i32())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iter_fast() {
|
||||
let nums = [1, 2, 3];
|
||||
let set = NSSet::from_slice(&nums.map(NSNumber::new_i32));
|
||||
|
||||
assert_eq!(set.iter_fast().count(), 3);
|
||||
assert!(set.iter_fast().all(|i| nums.contains(&i.as_i32())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_iter() {
|
||||
let nums = [1, 2, 3];
|
||||
let set = NSSet::from_slice(&nums.map(NSNumber::new_i32));
|
||||
|
||||
assert!(set.into_iter().all(|i| nums.contains(&i.as_i32())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_vec() {
|
||||
let strs = vec![
|
||||
NSMutableString::from_str("one"),
|
||||
NSMutableString::from_str("two"),
|
||||
NSMutableString::from_str("three"),
|
||||
];
|
||||
let set = NSSet::from_vec(strs);
|
||||
|
||||
let mut vec = NSSet::into_vec(set);
|
||||
for str in vec.iter_mut() {
|
||||
str.push_nsstring(ns_string!(" times zero is zero"));
|
||||
}
|
||||
|
||||
assert_eq!(vec.len(), 3);
|
||||
let suffix = ns_string!("zero");
|
||||
assert!(vec.iter().all(|str| str.has_suffix(suffix)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equality() {
|
||||
let set1 = NSSet::<NSString>::new();
|
||||
let set2 = NSSet::<NSString>::new();
|
||||
assert_eq!(set1, set2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_copy() {
|
||||
let set1 = NSSet::from_slice(&["one", "two", "three"].map(NSString::from_str));
|
||||
let set2 = set1.copy();
|
||||
assert_eq!(set1, set2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let set = NSSet::<NSString>::new();
|
||||
assert_eq!(format!("{:?}", set), "{}");
|
||||
|
||||
let set = NSSet::from_slice(&["one", "two"].map(NSString::from_str));
|
||||
assert!(matches!(
|
||||
format!("{:?}", set).as_str(),
|
||||
"{\"one\", \"two\"}" | "{\"two\", \"one\"}"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_retains_stored() {
|
||||
let obj = Id::into_shared(RcTestObject::new());
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
let input = [obj.clone(), obj.clone()];
|
||||
expected.retain += 2;
|
||||
expected.assert_current();
|
||||
|
||||
let set = NSSet::from_slice(&input);
|
||||
expected.retain += 1;
|
||||
expected.assert_current();
|
||||
|
||||
let _obj = set.get_any().unwrap();
|
||||
expected.assert_current();
|
||||
|
||||
drop(set);
|
||||
expected.release += 1;
|
||||
expected.assert_current();
|
||||
|
||||
let set = NSSet::from_vec(Vec::from(input));
|
||||
expected.retain += 1;
|
||||
expected.release += 2;
|
||||
expected.assert_current();
|
||||
|
||||
drop(set);
|
||||
expected.release += 1;
|
||||
expected.assert_current();
|
||||
|
||||
drop(obj);
|
||||
expected.release += 1;
|
||||
expected.dealloc += 1;
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nscopying_uses_retain() {
|
||||
let obj = Id::into_shared(RcTestObject::new());
|
||||
let set = NSSet::from_slice(&[obj]);
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
let _copy = set.copy();
|
||||
expected.assert_current();
|
||||
|
||||
let _copy = set.mutable_copy();
|
||||
expected.retain += 1;
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
feature = "apple",
|
||||
ignore = "this works differently on different framework versions"
|
||||
)]
|
||||
fn test_iter_no_retain() {
|
||||
let obj = Id::into_shared(RcTestObject::new());
|
||||
let set = NSSet::from_slice(&[obj]);
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
let iter = set.iter();
|
||||
expected.retain += 0;
|
||||
expected.assert_current();
|
||||
|
||||
assert_eq!(iter.count(), 1);
|
||||
expected.autorelease += 0;
|
||||
expected.assert_current();
|
||||
|
||||
let iter = set.iter_fast();
|
||||
assert_eq!(iter.count(), 1);
|
||||
expected.assert_current();
|
||||
}
|
||||
}
|
||||
527
third-party/vendor/objc2/src/foundation/string.rs
vendored
Normal file
527
third-party/vendor/objc2/src/foundation/string.rs
vendored
Normal file
|
|
@ -0,0 +1,527 @@
|
|||
use alloc::borrow::ToOwned;
|
||||
use core::cmp;
|
||||
use core::ffi::c_void;
|
||||
use core::fmt;
|
||||
use core::panic::RefUnwindSafe;
|
||||
use core::panic::UnwindSafe;
|
||||
use core::ptr::NonNull;
|
||||
use core::slice;
|
||||
use core::str;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
use super::{NSComparisonResult, NSCopying, NSMutableCopying, NSMutableString, NSObject};
|
||||
use crate::rc::{autoreleasepool, AutoreleasePool, DefaultId, Id, Shared};
|
||||
use crate::runtime::{Class, Object};
|
||||
use crate::{extern_class, extern_methods, msg_send, msg_send_id, ClassType};
|
||||
|
||||
#[cfg(feature = "apple")]
|
||||
const UTF8_ENCODING: usize = 4;
|
||||
#[cfg(feature = "gnustep-1-7")]
|
||||
const UTF8_ENCODING: i32 = 4;
|
||||
|
||||
extern_class!(
|
||||
/// An immutable, plain-text Unicode string object.
|
||||
///
|
||||
/// Can be created statically using the [`ns_string!`] macro.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsstring?language=objc).
|
||||
///
|
||||
/// [`ns_string!`]: crate::ns_string
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct NSString;
|
||||
// TODO: Use isEqualToString: for comparison (instead of just isEqual:)
|
||||
// The former is more performant
|
||||
|
||||
// TODO: Check if performance of NSSelectorFromString is worthwhile
|
||||
|
||||
unsafe impl ClassType for NSString {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
// SAFETY: `NSString` is immutable and `NSMutableString` can only be mutated
|
||||
// from `&mut` methods.
|
||||
unsafe impl Sync for NSString {}
|
||||
unsafe impl Send for NSString {}
|
||||
|
||||
// Even if an exception occurs inside a string method, the state of the string
|
||||
// (should) still be perfectly safe to access.
|
||||
impl UnwindSafe for NSString {}
|
||||
impl RefUnwindSafe for NSString {}
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSString {
|
||||
/// Construct an empty NSString.
|
||||
pub fn new() -> Id<Self, Shared> {
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
/// Create a new string by appending the given string to self.
|
||||
///
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
/// use objc2::ns_string;
|
||||
/// let error_tag = ns_string!("Error: ");
|
||||
/// let error_string = ns_string!("premature end of file.");
|
||||
/// let error_message = error_tag.concat(error_string);
|
||||
/// assert_eq!(&*error_message, ns_string!("Error: premature end of file."));
|
||||
/// ```
|
||||
#[doc(alias = "stringByAppendingString")]
|
||||
#[doc(alias = "stringByAppendingString:")]
|
||||
pub fn concat(&self, other: &Self) -> Id<Self, Shared> {
|
||||
// SAFETY: The other string is non-null, and won't be retained
|
||||
// by the function.
|
||||
unsafe { msg_send_id![self, stringByAppendingString: other] }
|
||||
}
|
||||
|
||||
/// Create a new string by appending the given string, separated by
|
||||
/// a path separator.
|
||||
///
|
||||
/// This is similar to [`Path::join`][std::path::Path::join].
|
||||
///
|
||||
/// Note that this method only works with file paths (not, for
|
||||
/// example, string representations of URLs).
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
/// use objc2::ns_string;
|
||||
///
|
||||
/// let extension = ns_string!("scratch.tiff");
|
||||
/// assert_eq!(&*ns_string!("/tmp").join_path(extension), ns_string!("/tmp/scratch.tiff"));
|
||||
/// assert_eq!(&*ns_string!("/tmp/").join_path(extension), ns_string!("/tmp/scratch.tiff"));
|
||||
/// assert_eq!(&*ns_string!("/").join_path(extension), ns_string!("/scratch.tiff"));
|
||||
/// assert_eq!(&*ns_string!("").join_path(extension), ns_string!("scratch.tiff"));
|
||||
/// ```
|
||||
#[doc(alias = "stringByAppendingPathComponent")]
|
||||
#[doc(alias = "stringByAppendingPathComponent:")]
|
||||
pub fn join_path(&self, other: &Self) -> Id<Self, Shared> {
|
||||
// SAFETY: Same as `Self::concat`.
|
||||
unsafe { msg_send_id![self, stringByAppendingPathComponent: other] }
|
||||
}
|
||||
|
||||
/// The number of UTF-8 code units in `self`.
|
||||
#[doc(alias = "lengthOfBytesUsingEncoding")]
|
||||
#[doc(alias = "lengthOfBytesUsingEncoding:")]
|
||||
pub fn len(&self) -> usize {
|
||||
unsafe { msg_send![self, lengthOfBytesUsingEncoding: UTF8_ENCODING] }
|
||||
}
|
||||
|
||||
/// The number of UTF-16 code units in the string.
|
||||
///
|
||||
/// See also [`NSString::len`].
|
||||
#[doc(alias = "length")]
|
||||
#[sel(length)]
|
||||
pub fn len_utf16(&self) -> usize;
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
// TODO: lengthOfBytesUsingEncoding: might sometimes return 0 for
|
||||
// other reasons, so this is not really correct!
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Get the [`str`](`prim@str`) representation of this string if it can be
|
||||
/// done efficiently.
|
||||
///
|
||||
/// Returns [`None`] if the internal storage does not allow this to be
|
||||
/// done efficiently. Use [`NSString::as_str`] or `NSString::to_string`
|
||||
/// if performance is not an issue.
|
||||
#[doc(alias = "CFStringGetCStringPtr")]
|
||||
#[allow(unused)]
|
||||
// TODO: Finish this
|
||||
fn as_str_wip(&self) -> Option<&str> {
|
||||
type CFStringEncoding = u32;
|
||||
#[allow(non_upper_case_globals)]
|
||||
// https://developer.apple.com/documentation/corefoundation/cfstringbuiltinencodings/kcfstringencodingutf8?language=objc
|
||||
const kCFStringEncodingUTF8: CFStringEncoding = 0x08000100;
|
||||
extern "C" {
|
||||
// https://developer.apple.com/documentation/corefoundation/1542133-cfstringgetcstringptr?language=objc
|
||||
fn CFStringGetCStringPtr(s: &NSString, encoding: CFStringEncoding)
|
||||
-> *const c_char;
|
||||
}
|
||||
let bytes = unsafe { CFStringGetCStringPtr(self, kCFStringEncodingUTF8) };
|
||||
NonNull::new(bytes as *mut u8).map(|bytes| {
|
||||
let len = self.len();
|
||||
let bytes: &[u8] = unsafe { slice::from_raw_parts(bytes.as_ptr(), len) };
|
||||
str::from_utf8(bytes).unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
/// Get an [UTF-16] string slice if it can be done efficiently.
|
||||
///
|
||||
/// Returns [`None`] if the internal storage of `self` does not allow this
|
||||
/// to be returned efficiently.
|
||||
///
|
||||
/// See [`as_str`](Self::as_str) for the UTF-8 equivalent.
|
||||
///
|
||||
/// [UTF-16]: https://en.wikipedia.org/wiki/UTF-16
|
||||
#[allow(unused)]
|
||||
// TODO: Finish this
|
||||
fn as_utf16(&self) -> Option<&[u16]> {
|
||||
extern "C" {
|
||||
// https://developer.apple.com/documentation/corefoundation/1542939-cfstringgetcharactersptr?language=objc
|
||||
fn CFStringGetCharactersPtr(s: &NSString) -> *const u16;
|
||||
}
|
||||
let ptr = unsafe { CFStringGetCharactersPtr(self) };
|
||||
NonNull::new(ptr as *mut u16)
|
||||
.map(|ptr| unsafe { slice::from_raw_parts(ptr.as_ptr(), self.len_utf16()) })
|
||||
}
|
||||
|
||||
/// Get the [`str`](`prim@str`) representation of this.
|
||||
///
|
||||
/// TODO: Further explain this.
|
||||
#[doc(alias = "UTF8String")]
|
||||
pub fn as_str<'r, 's: 'r, 'p: 'r>(&'s self, pool: &'p AutoreleasePool) -> &'r str {
|
||||
// NOTE: Please keep up to date with `objc2::exception`!
|
||||
|
||||
// This is necessary until `auto` types stabilizes.
|
||||
pool.__verify_is_inner();
|
||||
|
||||
// The documentation on `UTF8String` is a bit sparse, but with
|
||||
// educated guesses and testing I've determined that NSString stores
|
||||
// a pointer to the string data, sometimes with an UTF-8 encoding,
|
||||
// (usual for ascii data), sometimes in other encodings (UTF-16?).
|
||||
//
|
||||
// `UTF8String` then checks the internal encoding:
|
||||
// - If the data is UTF-8 encoded, it returns the internal pointer.
|
||||
// - If the data is in another encoding, it creates a new allocation,
|
||||
// writes the UTF-8 representation of the string into it,
|
||||
// autoreleases the allocation and returns a pointer to it.
|
||||
//
|
||||
// So the lifetime of the returned pointer is either the same as the
|
||||
// NSString OR the lifetime of the innermost @autoreleasepool.
|
||||
//
|
||||
// https://developer.apple.com/documentation/foundation/nsstring/1411189-utf8string?language=objc
|
||||
let bytes: *const c_char = unsafe { msg_send![self, UTF8String] };
|
||||
let bytes: *const u8 = bytes.cast();
|
||||
let len = self.len();
|
||||
|
||||
// SAFETY:
|
||||
// The held AutoreleasePool is the innermost, and the reference is
|
||||
// constrained both by the pool and the NSString.
|
||||
//
|
||||
// `len` is the length of the string in the UTF-8 encoding.
|
||||
//
|
||||
// `bytes` is a null-terminated C string (with length = len + 1), so
|
||||
// it is never a NULL pointer.
|
||||
let bytes: &'r [u8] = unsafe { slice::from_raw_parts(bytes, len) };
|
||||
|
||||
// TODO: Always UTF-8, so should we use `from_utf8_unchecked`?
|
||||
str::from_utf8(bytes).unwrap()
|
||||
|
||||
// NOTE: Please keep up to date with `objc2::exception`!
|
||||
}
|
||||
|
||||
// TODO: Allow usecases where the NUL byte from `UTF8String` is kept?
|
||||
|
||||
/// Creates an immutable `NSString` by copying the given string slice.
|
||||
///
|
||||
/// Prefer using the [`ns_string!`] macro when possible.
|
||||
///
|
||||
/// [`ns_string!`]: crate::ns_string
|
||||
#[doc(alias = "initWithBytes")]
|
||||
#[doc(alias = "initWithBytes:length:encoding:")]
|
||||
#[allow(clippy::should_implement_trait)] // Not really sure of a better name
|
||||
pub fn from_str(string: &str) -> Id<Self, Shared> {
|
||||
unsafe {
|
||||
let obj = from_str(Self::class(), string);
|
||||
Id::new(obj.cast()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: initWithBytesNoCopy:, maybe add lifetime parameter to NSString?
|
||||
// See https://github.com/nvzqz/fruity/blob/320efcf715c2c5fbd2f3084f671f2be2e03a6f2b/src/foundation/ns_string/mod.rs#L350-L381
|
||||
// Might be quite difficult, as Objective-C code might assume the NSString
|
||||
// is always alive?
|
||||
// See https://github.com/drewcrawford/foundationr/blob/b27683417a35510e8e5d78a821f081905b803de6/src/nsstring.rs
|
||||
|
||||
/// Whether the given string matches the beginning characters of this
|
||||
/// string.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsstring/1410309-hasprefix?language=objc).
|
||||
#[doc(alias = "hasPrefix")]
|
||||
#[doc(alias = "hasPrefix:")]
|
||||
#[sel(hasPrefix:)]
|
||||
pub fn has_prefix(&self, prefix: &NSString) -> bool;
|
||||
|
||||
/// Whether the given string matches the ending characters of this string.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsstring/1416529-hassuffix?language=objc).
|
||||
#[doc(alias = "hasSuffix")]
|
||||
#[doc(alias = "hasSuffix:")]
|
||||
#[sel(hasSuffix:)]
|
||||
pub fn has_suffix(&self, suffix: &NSString) -> bool;
|
||||
|
||||
// TODO: Other comparison methods:
|
||||
// - compare:options:
|
||||
// - compare:options:range:
|
||||
// - compare:options:range:locale:
|
||||
// - localizedCompare:
|
||||
// - caseInsensitiveCompare:
|
||||
// - localizedCaseInsensitiveCompare:
|
||||
// - localizedStandardCompare:
|
||||
#[sel(compare:)]
|
||||
fn compare(&self, other: &Self) -> NSComparisonResult;
|
||||
|
||||
// pub fn from_nsrange(range: NSRange) -> Id<Self, Shared>
|
||||
// https://developer.apple.com/documentation/foundation/1415155-nsstringfromrange?language=objc
|
||||
}
|
||||
);
|
||||
|
||||
pub(crate) fn from_str(cls: &Class, string: &str) -> *mut Object {
|
||||
let bytes: *const c_void = string.as_ptr().cast();
|
||||
unsafe {
|
||||
let obj: *mut Object = msg_send![cls, alloc];
|
||||
msg_send![
|
||||
obj,
|
||||
initWithBytes: bytes,
|
||||
length: string.len(),
|
||||
encoding: UTF8_ENCODING,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for NSString {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for NSString {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.compare(other).into()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: PartialEq and PartialOrd against &str
|
||||
// See `fruity`'s implementation:
|
||||
// https://github.com/nvzqz/fruity/blob/320efcf715c2c5fbd2f3084f671f2be2e03a6f2b/src/foundation/ns_string/mod.rs#L69-L163
|
||||
|
||||
impl DefaultId for NSString {
|
||||
type Ownership = Shared;
|
||||
|
||||
#[inline]
|
||||
fn default_id() -> Id<Self, Self::Ownership> {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl NSCopying for NSString {
|
||||
type Ownership = Shared;
|
||||
type Output = NSString;
|
||||
}
|
||||
|
||||
unsafe impl NSMutableCopying for NSString {
|
||||
type Output = NSMutableString;
|
||||
}
|
||||
|
||||
impl ToOwned for NSString {
|
||||
type Owned = Id<NSString, Shared>;
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
self.copy()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NSString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// The call to `to_owned` is unfortunate, but is required to work
|
||||
// around `f` not being AutoreleaseSafe.
|
||||
// TODO: Fix this!
|
||||
let s = autoreleasepool(|pool| self.as_str(pool).to_owned());
|
||||
fmt::Display::fmt(&s, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NSString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// The call to `to_owned` is unfortunate, but is required to work
|
||||
// around `f` not being AutoreleaseSafe.
|
||||
// TODO: Fix this!
|
||||
let s = autoreleasepool(|pool| self.as_str(pool).to_owned());
|
||||
fmt::Debug::fmt(&s, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use alloc::format;
|
||||
use core::ptr;
|
||||
|
||||
#[test]
|
||||
fn test_equality() {
|
||||
let s1 = NSString::from_str("abc");
|
||||
let s2 = NSString::from_str("abc");
|
||||
assert_eq!(s1, s1);
|
||||
assert_eq!(s1, s2);
|
||||
|
||||
let s3 = NSString::from_str("def");
|
||||
assert_ne!(s1, s3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_debug() {
|
||||
let s = NSString::from_str("xyz\"123");
|
||||
assert_eq!(format!("{}", s), "xyz\"123");
|
||||
assert_eq!(format!("{:?}", s), r#""xyz\"123""#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty() {
|
||||
let s1 = NSString::from_str("");
|
||||
let s2 = NSString::new();
|
||||
|
||||
assert_eq!(s1.len(), 0);
|
||||
assert_eq!(s2.len(), 0);
|
||||
|
||||
assert_eq!(s1, s2);
|
||||
|
||||
autoreleasepool(|pool| {
|
||||
assert_eq!(s1.as_str(pool), "");
|
||||
assert_eq!(s2.as_str(pool), "");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_utf8() {
|
||||
let expected = "ประเทศไทย中华Việt Nam";
|
||||
let s = NSString::from_str(expected);
|
||||
assert_eq!(s.len(), expected.len());
|
||||
autoreleasepool(|pool| {
|
||||
assert_eq!(s.as_str(pool), expected);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nul() {
|
||||
let expected = "\0";
|
||||
let s = NSString::from_str(expected);
|
||||
assert_eq!(s.len(), expected.len());
|
||||
autoreleasepool(|pool| {
|
||||
assert_eq!(s.as_str(pool), expected);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interior_nul() {
|
||||
let expected = "Hello\0World";
|
||||
let s = NSString::from_str(expected);
|
||||
assert_eq!(s.len(), expected.len());
|
||||
autoreleasepool(|pool| {
|
||||
assert_eq!(s.as_str(pool), expected);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_copy() {
|
||||
let s1 = NSString::from_str("abc");
|
||||
let s2 = s1.copy();
|
||||
// An optimization that NSString makes, since it is immutable
|
||||
assert_eq!(Id::as_ptr(&s1), Id::as_ptr(&s2));
|
||||
assert!(s2.is_kind_of::<NSString>());
|
||||
|
||||
let s3 = s1.mutable_copy();
|
||||
assert_ne!(Id::as_ptr(&s1), Id::as_ptr(&s3).cast());
|
||||
assert!(s3.is_kind_of::<NSMutableString>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_copy_nsstring_is_same() {
|
||||
let string1 = NSString::from_str("Hello, world!");
|
||||
let string2 = string1.copy();
|
||||
assert!(
|
||||
ptr::eq(&*string1, &*string2),
|
||||
"Cloned NSString didn't have the same address"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Apparently NSString does this for some reason?
|
||||
fn test_strips_first_leading_zero_width_no_break_space() {
|
||||
let ns_string = NSString::from_str("\u{feff}");
|
||||
let expected = "";
|
||||
autoreleasepool(|pool| {
|
||||
assert_eq!(ns_string.as_str(pool), expected);
|
||||
});
|
||||
assert_eq!(ns_string.len(), 0);
|
||||
|
||||
let s = "\u{feff}\u{feff}a\u{feff}";
|
||||
|
||||
// Huh, this difference might be a GNUStep bug?
|
||||
#[cfg(feature = "apple")]
|
||||
let expected = "\u{feff}a\u{feff}";
|
||||
#[cfg(feature = "gnustep-1-7")]
|
||||
let expected = "a\u{feff}";
|
||||
|
||||
let ns_string = NSString::from_str(s);
|
||||
autoreleasepool(|pool| {
|
||||
assert_eq!(ns_string.as_str(pool), expected);
|
||||
});
|
||||
assert_eq!(ns_string.len(), expected.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash() {
|
||||
use core::hash::Hasher;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::Hash;
|
||||
|
||||
let s1 = NSString::from_str("example string goes here");
|
||||
let s2 = NSString::from_str("example string goes here");
|
||||
|
||||
let mut hashstate = DefaultHasher::new();
|
||||
let mut hashstate2 = DefaultHasher::new();
|
||||
|
||||
s1.hash(&mut hashstate);
|
||||
s2.hash(&mut hashstate2);
|
||||
|
||||
assert_eq!(hashstate.finish(), hashstate2.finish());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prefix_suffix() {
|
||||
let s = NSString::from_str("abcdef");
|
||||
let prefix = NSString::from_str("abc");
|
||||
let suffix = NSString::from_str("def");
|
||||
assert!(s.has_prefix(&prefix));
|
||||
assert!(s.has_suffix(&suffix));
|
||||
assert!(!s.has_prefix(&suffix));
|
||||
assert!(!s.has_suffix(&prefix));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::nonminimal_bool)]
|
||||
fn test_cmp() {
|
||||
let s1 = NSString::from_str("aa");
|
||||
assert!(s1 <= s1);
|
||||
assert!(s1 >= s1);
|
||||
let s2 = NSString::from_str("ab");
|
||||
assert!(s1 < s2);
|
||||
assert!(!(s1 > s2));
|
||||
assert!(s1 <= s2);
|
||||
assert!(!(s1 >= s2));
|
||||
let s3 = NSString::from_str("ba");
|
||||
assert!(s1 < s3);
|
||||
assert!(!(s1 > s3));
|
||||
assert!(s1 <= s3);
|
||||
assert!(!(s1 >= s3));
|
||||
assert!(s2 < s3);
|
||||
assert!(!(s2 > s3));
|
||||
assert!(s2 <= s3);
|
||||
assert!(!(s2 >= s3));
|
||||
|
||||
let s = NSString::from_str("abc");
|
||||
let shorter = NSString::from_str("a");
|
||||
let longer = NSString::from_str("abcdef");
|
||||
assert!(s > shorter);
|
||||
assert!(s < longer);
|
||||
}
|
||||
}
|
||||
237
third-party/vendor/objc2/src/foundation/thread.rs
vendored
Normal file
237
third-party/vendor/objc2/src/foundation/thread.rs
vendored
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
use super::{NSObject, NSString};
|
||||
use crate::rc::{Id, Shared};
|
||||
use crate::{extern_class, extern_methods, msg_send_id, ClassType};
|
||||
|
||||
extern_class!(
|
||||
/// A thread of execution.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsthread?language=objc).
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct NSThread;
|
||||
|
||||
unsafe impl ClassType for NSThread {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
unsafe impl Send for NSThread {}
|
||||
unsafe impl Sync for NSThread {}
|
||||
|
||||
impl UnwindSafe for NSThread {}
|
||||
impl RefUnwindSafe for NSThread {}
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSThread {
|
||||
/// Returns the [`NSThread`] object representing the current thread.
|
||||
pub fn current() -> Id<Self, Shared> {
|
||||
unsafe { msg_send_id![Self::class(), currentThread] }
|
||||
}
|
||||
|
||||
/// Returns the [`NSThread`] object representing the main thread.
|
||||
pub fn main() -> Id<NSThread, Shared> {
|
||||
// The main thread static may not have been initialized
|
||||
// This can at least fail in GNUStep!
|
||||
let obj: Option<_> = unsafe { msg_send_id![Self::class(), mainThread] };
|
||||
obj.expect("Could not retrieve main thread.")
|
||||
}
|
||||
|
||||
/// Returns `true` if the thread is the main thread.
|
||||
#[sel(isMainThread)]
|
||||
pub fn is_main(&self) -> bool;
|
||||
|
||||
/// The name of the thread.
|
||||
pub fn name(&self) -> Option<Id<NSString, Shared>> {
|
||||
unsafe { msg_send_id![self, name] }
|
||||
}
|
||||
|
||||
unsafe fn new() -> Id<Self, Shared> {
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
#[sel(start)]
|
||||
unsafe fn start(&self);
|
||||
|
||||
#[sel(isMainThread)]
|
||||
fn is_current_main() -> bool;
|
||||
|
||||
#[sel(isMultiThreaded)]
|
||||
fn is_global_multi() -> bool;
|
||||
}
|
||||
);
|
||||
|
||||
impl fmt::Debug for NSThread {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Use -[NSThread description] since that includes the thread number
|
||||
let obj: &NSObject = self;
|
||||
fmt::Debug::fmt(obj, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the application is multithreaded according to Cocoa.
|
||||
pub fn is_multi_threaded() -> bool {
|
||||
NSThread::is_global_multi()
|
||||
}
|
||||
|
||||
/// Whether the current thread is the main thread.
|
||||
pub fn is_main_thread() -> bool {
|
||||
NSThread::is_current_main()
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn make_multithreaded() {
|
||||
let thread = unsafe { NSThread::new() };
|
||||
unsafe { thread.start() };
|
||||
// Don't bother waiting for it to complete!
|
||||
}
|
||||
|
||||
/// A marker type taken by functions that can only be executed on the main
|
||||
/// thread.
|
||||
///
|
||||
/// By design, this is neither [`Send`] nor [`Sync`], and can only be created
|
||||
/// on the main thread, meaning that if you're holding this, you know you're
|
||||
/// running on the main thread.
|
||||
///
|
||||
/// See the following links for more information on main-thread-only APIs:
|
||||
/// - [Main Thread Only APIs on OS X](https://www.dribin.org/dave/blog/archives/2009/02/01/main_thread_apis/)
|
||||
/// - [Thread Safety Summary](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-SW1)
|
||||
/// - [Are the Cocoa Frameworks Thread Safe?](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW47)
|
||||
/// - [Technical Note TN2028 - Threading Architectures](https://developer.apple.com/library/archive/technotes/tn/tn2028.html#//apple_ref/doc/uid/DTS10003065)
|
||||
/// - [Thread Management](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html)
|
||||
/// - [Mike Ash' article on thread safety](https://www.mikeash.com/pyblog/friday-qa-2009-01-09.html)
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Use when designing APIs that are only safe to use on the main thread:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use objc2::foundation::MainThreadMarker;
|
||||
/// use objc2::runtime::Object;
|
||||
/// use objc2::msg_send;
|
||||
/// # let obj = 0 as *const Object;
|
||||
///
|
||||
/// // This action requires the main thread, so we take a marker as parameter.
|
||||
/// // It signals clearly to users "this requires the main thread".
|
||||
/// unsafe fn do_thing(obj: *const Object, _mtm: MainThreadMarker) {
|
||||
/// msg_send![obj, someActionThatRequiresTheMainThread]
|
||||
/// }
|
||||
///
|
||||
/// // Usage
|
||||
/// let mtm = MainThreadMarker::new().unwrap();
|
||||
/// unsafe { do_thing(obj, mtm) }
|
||||
/// ```
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
// This is valid to Copy because it's still `!Send` and `!Sync`.
|
||||
pub struct MainThreadMarker {
|
||||
// No lifetime information needed; the main thread is static and available
|
||||
// throughout the entire program!
|
||||
_priv: PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
impl MainThreadMarker {
|
||||
/// Construct a new [`MainThreadMarker`].
|
||||
///
|
||||
/// Returns [`None`] if the current thread was not the main thread.
|
||||
pub fn new() -> Option<Self> {
|
||||
if is_main_thread() {
|
||||
// SAFETY: We just checked that we are running on the main thread.
|
||||
Some(unsafe { Self::new_unchecked() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a new [`MainThreadMarker`] without first checking whether
|
||||
/// the current thread is the main one.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The current thread must be the main thread.
|
||||
pub unsafe fn new_unchecked() -> Self {
|
||||
// SAFETY: Upheld by caller
|
||||
Self { _priv: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for MainThreadMarker {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("MainThreadMarker").finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::format;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
feature = "gnustep-1-7",
|
||||
ignore = "Retrieving main thread is weirdly broken, only works with --test-threads=1"
|
||||
)]
|
||||
fn test_main_thread() {
|
||||
let current = NSThread::current();
|
||||
let main = NSThread::main();
|
||||
|
||||
assert!(main.is_main());
|
||||
|
||||
if main == current {
|
||||
assert!(current.is_main());
|
||||
assert!(is_main_thread());
|
||||
} else {
|
||||
assert!(!current.is_main());
|
||||
assert!(!is_main_thread());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_main_thread() {
|
||||
let res = std::thread::spawn(|| (is_main_thread(), NSThread::current().is_main()))
|
||||
.join()
|
||||
.unwrap();
|
||||
assert_eq!(res, (false, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_main_thread_auto_traits() {
|
||||
fn assert_traits<T: Unpin + UnwindSafe + RefUnwindSafe + Sized>() {}
|
||||
|
||||
assert_traits::<MainThreadMarker>()
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
feature = "gnustep-1-7",
|
||||
ignore = "Retrieving main thread is weirdly broken, only works with --test-threads=1"
|
||||
)]
|
||||
fn test_debug() {
|
||||
let thread = NSThread::main();
|
||||
|
||||
let actual = format!("{:?}", thread);
|
||||
let expected = [
|
||||
// macOS 11
|
||||
format!("<NSThread: {:p}>{{number = 1, name = (null)}}", thread),
|
||||
format!("<NSThread: {:p}>{{number = 1, name = main}}", thread),
|
||||
// macOS 12
|
||||
format!("<_NSMainThread: {:p}>{{number = 1, name = (null)}}", thread),
|
||||
format!("<_NSMainThread: {:p}>{{number = 1, name = main}}", thread),
|
||||
];
|
||||
assert!(
|
||||
expected.contains(&actual),
|
||||
"Expected one of {:?}, got {:?}",
|
||||
expected,
|
||||
actual,
|
||||
);
|
||||
|
||||
// SAFETY: We don't use the marker for anything other than its Debug
|
||||
// impl, so this test doesn't actually need to run on the main thread!
|
||||
let marker = unsafe { MainThreadMarker::new_unchecked() };
|
||||
assert_eq!(format!("{:?}", marker), "MainThreadMarker");
|
||||
}
|
||||
}
|
||||
189
third-party/vendor/objc2/src/foundation/uuid.rs
vendored
Normal file
189
third-party/vendor/objc2/src/foundation/uuid.rs
vendored
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
use core::fmt;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
use super::{NSCopying, NSObject, NSString};
|
||||
use crate::rc::{DefaultId, Id, Shared};
|
||||
use crate::{extern_class, extern_methods, msg_send_id, ClassType, Encode, Encoding, RefEncode};
|
||||
|
||||
extern_class!(
|
||||
/// A universally unique value.
|
||||
///
|
||||
/// Can be used to identify types, interfaces, and other items.
|
||||
///
|
||||
/// Conversion methods to/from UUIDs from the `uuid` crate can be
|
||||
/// enabled with the `uuid` crate feature.
|
||||
///
|
||||
/// macOS: This is only available on 10.8 and above.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsuuid?language=objc).
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct NSUUID;
|
||||
|
||||
unsafe impl ClassType for NSUUID {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
/// The headers describe `initWithUUIDBytes:` and `getUUIDBytes:` as
|
||||
/// taking `uuid_t`, but something fishy is going on, in reality they
|
||||
/// expect a reference to these!
|
||||
///
|
||||
/// Hence we create this newtype to change the encoding.
|
||||
#[repr(transparent)]
|
||||
struct UuidBytes([u8; 16]);
|
||||
|
||||
unsafe impl RefEncode for UuidBytes {
|
||||
const ENCODING_REF: Encoding = Encoding::Array(16, &u8::ENCODING);
|
||||
}
|
||||
|
||||
// SAFETY: `NSUUID` is immutable.
|
||||
unsafe impl Sync for NSUUID {}
|
||||
unsafe impl Send for NSUUID {}
|
||||
|
||||
impl UnwindSafe for NSUUID {}
|
||||
impl RefUnwindSafe for NSUUID {}
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSUUID {
|
||||
pub fn new_v4() -> Id<Self, Shared> {
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
/// The 'nil UUID'.
|
||||
pub fn nil() -> Id<Self, Shared> {
|
||||
Self::from_bytes([0; 16])
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: [u8; 16]) -> Id<Self, Shared> {
|
||||
let bytes = UuidBytes(bytes);
|
||||
unsafe {
|
||||
let obj = msg_send_id![Self::class(), alloc];
|
||||
msg_send_id![obj, initWithUUIDBytes: &bytes]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_string(string: &NSString) -> Option<Id<Self, Shared>> {
|
||||
unsafe {
|
||||
let obj = msg_send_id![Self::class(), alloc];
|
||||
msg_send_id![obj, initWithUUIDString: string]
|
||||
}
|
||||
}
|
||||
|
||||
#[sel(getUUIDBytes:)]
|
||||
fn get_bytes_raw(&self, bytes: &mut UuidBytes);
|
||||
|
||||
pub fn as_bytes(&self) -> [u8; 16] {
|
||||
let mut bytes = UuidBytes([0; 16]);
|
||||
self.get_bytes_raw(&mut bytes);
|
||||
bytes.0
|
||||
}
|
||||
|
||||
pub fn string(&self) -> Id<NSString, Shared> {
|
||||
unsafe { msg_send_id![self, UUIDString] }
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl fmt::Display for NSUUID {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.string(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NSUUID {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// The `uuid` crate does `Debug` and `Display` equally, and so do we
|
||||
fmt::Display::fmt(&self.string(), f)
|
||||
}
|
||||
}
|
||||
|
||||
// UUID `compare:` is broken for some reason?
|
||||
|
||||
// impl PartialOrd for NSUUID {
|
||||
// #[inline]
|
||||
// fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
// Some(self.cmp(other))
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl Ord for NSUUID {
|
||||
// fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
// let res: NSComparisonResult = unsafe { msg_send![self, compare: other] };
|
||||
// res.into()
|
||||
// }
|
||||
// }
|
||||
|
||||
/// Conversion methods to/from `uuid` crate.
|
||||
#[cfg(feature = "uuid")]
|
||||
impl NSUUID {
|
||||
pub fn from_uuid(uuid: uuid::Uuid) -> Id<Self, Shared> {
|
||||
Self::from_bytes(uuid.into_bytes())
|
||||
}
|
||||
|
||||
pub fn as_uuid(&self) -> uuid::Uuid {
|
||||
uuid::Uuid::from_bytes(self.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultId for NSUUID {
|
||||
type Ownership = Shared;
|
||||
fn default_id() -> Id<Self, Self::Ownership> {
|
||||
Self::nil()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl NSCopying for NSUUID {
|
||||
type Ownership = Shared;
|
||||
type Output = NSUUID;
|
||||
}
|
||||
|
||||
impl alloc::borrow::ToOwned for NSUUID {
|
||||
type Owned = Id<NSUUID, Shared>;
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
self.copy()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::format;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
let uuid1 = NSUUID::new_v4();
|
||||
let uuid2 = NSUUID::new_v4();
|
||||
assert_ne!(uuid1, uuid2, "Statistically very unlikely");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bytes() {
|
||||
let uuid = NSUUID::from_bytes([10; 16]);
|
||||
assert_eq!(uuid.as_bytes(), [10; 16]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_debug() {
|
||||
let uuid = NSUUID::from_bytes([10; 16]);
|
||||
let expected = "0A0A0A0A-0A0A-0A0A-0A0A-0A0A0A0A0A0A";
|
||||
assert_eq!(format!("{}", uuid), expected);
|
||||
assert_eq!(format!("{:?}", uuid), expected);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn test_compare() {
|
||||
// let uuid1 = NSUUID::from_bytes([10; 16]);
|
||||
// let uuid2 = NSUUID::from_bytes([9; 16]);
|
||||
// assert!(uuid1 > uuid2);
|
||||
// }
|
||||
|
||||
#[cfg(feature = "uuid")]
|
||||
#[test]
|
||||
fn test_convert_roundtrip() {
|
||||
let nsuuid1 = NSUUID::new_v4();
|
||||
let uuid = nsuuid1.as_uuid();
|
||||
let nsuuid2 = NSUUID::from_uuid(uuid);
|
||||
assert_eq!(nsuuid1, nsuuid2);
|
||||
}
|
||||
}
|
||||
363
third-party/vendor/objc2/src/foundation/value.rs
vendored
Normal file
363
third-party/vendor/objc2/src/foundation/value.rs
vendored
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
use alloc::string::ToString;
|
||||
use core::ffi::c_void;
|
||||
use core::fmt;
|
||||
use core::hash;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ptr::NonNull;
|
||||
use core::str;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::c_char;
|
||||
|
||||
use super::{NSCopying, NSObject, NSPoint, NSRange, NSRect, NSSize};
|
||||
use crate::rc::{Id, Shared};
|
||||
use crate::{extern_class, extern_methods, msg_send, msg_send_id, ClassType, Encode};
|
||||
|
||||
extern_class!(
|
||||
/// A container wrapping any encodable type as an Obective-C object.
|
||||
///
|
||||
/// Since Objective-C collections like [`NSArray`] can only contain
|
||||
/// objects, it is common to wrap pointers or structures like [`NSRange`].
|
||||
///
|
||||
/// Note that creating `NSValue`s is not `unsafe`, but almost all usage of
|
||||
/// it is, since we cannot guarantee that the type that was used to
|
||||
/// construct it is the same as the expected output type.
|
||||
///
|
||||
/// See also the [`NSNumber`] subclass for when you want to wrap numbers.
|
||||
///
|
||||
/// See [Apple's documentation][apple-doc] for more information.
|
||||
///
|
||||
/// [`NSArray`]: super::NSArray
|
||||
/// [`NSRange`]: super::NSRange
|
||||
/// [`NSNumber`]: super::NSNumber
|
||||
/// [apple-doc]: https://developer.apple.com/documentation/foundation/nsnumber?language=objc
|
||||
pub struct NSValue;
|
||||
|
||||
unsafe impl ClassType for NSValue {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
// We can't implement any auto traits for NSValue, since it can contain an
|
||||
// arbitary object!
|
||||
|
||||
extern_methods!(
|
||||
/// Creation methods.
|
||||
unsafe impl NSValue {
|
||||
// Default / empty new is not provided because `-init` returns `nil` on
|
||||
// Apple and GNUStep throws an exception on all other messages to this
|
||||
// invalid instance.
|
||||
|
||||
/// Create a new `NSValue` containing the given type.
|
||||
///
|
||||
/// Be careful when using this since you may accidentally pass a reference
|
||||
/// when you wanted to pass a concrete type instead.
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create an `NSValue` containing an [`NSPoint`][super::NSPoint].
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSPoint, NSValue};
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
/// let val = NSValue::new::<NSPoint>(NSPoint::new(1.0, 1.0));
|
||||
/// ```
|
||||
pub fn new<T: 'static + Copy + Encode>(value: T) -> Id<Self, Shared> {
|
||||
let bytes: *const T = &value;
|
||||
let bytes: *const c_void = bytes.cast();
|
||||
let encoding = CString::new(T::ENCODING.to_string()).unwrap();
|
||||
unsafe {
|
||||
msg_send_id![
|
||||
msg_send_id![Self::class(), alloc],
|
||||
initWithBytes: bytes,
|
||||
objCType: encoding.as_ptr(),
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter methods.
|
||||
unsafe impl NSValue {
|
||||
/// Retrieve the data contained in the `NSValue`.
|
||||
///
|
||||
/// Note that this is broken on GNUStep for some types, see
|
||||
/// [gnustep/libs-base#216].
|
||||
///
|
||||
/// [gnustep/libs-base#216]: https://github.com/gnustep/libs-base/pull/216
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The type of `T` must be what the NSValue actually stores, and any
|
||||
/// safety invariants that the value has must be upheld.
|
||||
///
|
||||
/// Note that it may be, but is not always, enough to simply check whether
|
||||
/// [`contains_encoding`] returns `true`. For example, `NonNull<T>` have
|
||||
/// the same encoding as `*const T`, but `NonNull<T>` is clearly not
|
||||
/// safe to return from this function even if you've checked the encoding
|
||||
/// beforehand.
|
||||
///
|
||||
/// [`contains_encoding`]: Self::contains_encoding
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Store a pointer in `NSValue`, and retrieve it again afterwards.
|
||||
///
|
||||
/// ```
|
||||
/// use std::ffi::c_void;
|
||||
/// use std::ptr;
|
||||
/// use objc2::foundation::NSValue;
|
||||
///
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
/// let val = NSValue::new::<*const c_void>(ptr::null());
|
||||
/// // SAFETY: The value was just created with a pointer
|
||||
/// let res = unsafe { val.get::<*const c_void>() };
|
||||
/// assert!(res.is_null());
|
||||
/// ```
|
||||
pub unsafe fn get<T: 'static + Copy + Encode>(&self) -> T {
|
||||
debug_assert!(
|
||||
self.contains_encoding::<T>(),
|
||||
"wrong encoding. NSValue tried to return something with encoding {}, but the encoding of the given type was {}",
|
||||
self.encoding().unwrap_or("(NULL)"),
|
||||
T::ENCODING,
|
||||
);
|
||||
let mut value = MaybeUninit::<T>::uninit();
|
||||
let ptr: *mut c_void = value.as_mut_ptr().cast();
|
||||
let _: () = unsafe { msg_send![self, getValue: ptr] };
|
||||
// SAFETY: We know that `getValue:` initialized the value, and user
|
||||
// ensures that it is safe to access.
|
||||
unsafe { value.assume_init() }
|
||||
}
|
||||
|
||||
pub fn get_range(&self) -> Option<NSRange> {
|
||||
if self.contains_encoding::<NSRange>() {
|
||||
// SAFETY: We just checked that this contains an NSRange
|
||||
Some(unsafe { msg_send![self, rangeValue] })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_point(&self) -> Option<NSPoint> {
|
||||
if self.contains_encoding::<NSPoint>() {
|
||||
// SAFETY: We just checked that this contains an NSPoint
|
||||
//
|
||||
// Note: The documentation says that `pointValue`, `sizeValue` and
|
||||
// `rectValue` is only available on macOS, but turns out that they
|
||||
// are actually available everywhere!
|
||||
let res = unsafe { msg_send![self, pointValue] };
|
||||
Some(res)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> Option<NSSize> {
|
||||
if self.contains_encoding::<NSSize>() {
|
||||
// SAFETY: We just checked that this contains an NSSize
|
||||
let res = unsafe { msg_send![self, sizeValue] };
|
||||
Some(res)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_rect(&self) -> Option<NSRect> {
|
||||
if self.contains_encoding::<NSRect>() {
|
||||
// SAFETY: We just checked that this contains an NSRect
|
||||
let res = unsafe { msg_send![self, rectValue] };
|
||||
Some(res)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encoding(&self) -> Option<&str> {
|
||||
let result: Option<NonNull<c_char>> = unsafe { msg_send![self, objCType] };
|
||||
result.map(|s| unsafe { CStr::from_ptr(s.as_ptr()) }.to_str().unwrap())
|
||||
}
|
||||
|
||||
pub fn contains_encoding<T: 'static + Copy + Encode>(&self) -> bool {
|
||||
if let Some(encoding) = self.encoding() {
|
||||
T::ENCODING.equivalent_to_str(encoding)
|
||||
} else {
|
||||
panic!("missing NSValue encoding");
|
||||
}
|
||||
}
|
||||
|
||||
#[sel(isEqualToValue:)]
|
||||
fn is_equal_to_value(&self, other: &Self) -> bool;
|
||||
}
|
||||
);
|
||||
|
||||
unsafe impl NSCopying for NSValue {
|
||||
type Ownership = Shared;
|
||||
type Output = NSValue;
|
||||
}
|
||||
|
||||
impl alloc::borrow::ToOwned for NSValue {
|
||||
type Owned = Id<NSValue, Shared>;
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
self.copy()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for NSValue {
|
||||
#[doc(alias = "isEqualToValue:")]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
// Use isEqualToValue: instaed of isEqual: since it is faster
|
||||
self.is_equal_to_value(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl hash::Hash for NSValue {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
// Delegate to NSObject
|
||||
(**self).hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NSValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let enc = self.encoding().unwrap_or("(NULL)");
|
||||
let bytes = &**self; // Delegate to -[NSObject description]
|
||||
f.debug_struct("NSValue")
|
||||
.field("encoding", &enc)
|
||||
.field("bytes", bytes)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::format;
|
||||
use core::{ptr, slice};
|
||||
|
||||
use super::*;
|
||||
use crate::rc::{RcTestObject, ThreadTestData};
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
let val = NSValue::new(13u32);
|
||||
assert_eq!(unsafe { val.get::<u32>() }, 13);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_not_retain() {
|
||||
let obj = RcTestObject::new();
|
||||
let expected = ThreadTestData::current();
|
||||
|
||||
let val = NSValue::new::<*const RcTestObject>(&*obj);
|
||||
expected.assert_current();
|
||||
|
||||
assert!(ptr::eq(unsafe { val.get::<*const RcTestObject>() }, &*obj));
|
||||
expected.assert_current();
|
||||
|
||||
let _clone = val.clone();
|
||||
expected.assert_current();
|
||||
|
||||
let _copy = val.copy();
|
||||
expected.assert_current();
|
||||
|
||||
drop(val);
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equality() {
|
||||
let val1 = NSValue::new(123u32);
|
||||
let val2 = NSValue::new(123u32);
|
||||
assert_eq!(val1, val1);
|
||||
assert_eq!(val1, val2);
|
||||
|
||||
let val3 = NSValue::new(456u32);
|
||||
assert_ne!(val1, val3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equality_across_types() {
|
||||
let val1 = NSValue::new(123i32);
|
||||
let val2 = NSValue::new(123u32);
|
||||
|
||||
// Test that `objCType` is checked when comparing equality
|
||||
assert_ne!(val1, val2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "the debug output changes depending on OS version"]
|
||||
fn test_debug() {
|
||||
let expected = if cfg!(feature = "gnustep-1-7") {
|
||||
r#"NSValue { encoding: "C", bytes: (C) <ab> }"#
|
||||
} else if cfg!(newer_apple) {
|
||||
r#"NSValue { encoding: "C", bytes: {length = 1, bytes = 0xab} }"#
|
||||
} else {
|
||||
r#"NSValue { encoding: "C", bytes: <ab> }"#
|
||||
};
|
||||
assert_eq!(format!("{:?}", NSValue::new(171u8)), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nsrange() {
|
||||
let range = NSRange::from(1..2);
|
||||
let val = NSValue::new(range);
|
||||
assert_eq!(val.get_range(), Some(range));
|
||||
assert_eq!(val.get_point(), None);
|
||||
assert_eq!(val.get_size(), None);
|
||||
assert_eq!(val.get_rect(), None);
|
||||
// NSValue -getValue is broken on GNUStep for some types
|
||||
#[cfg(not(feature = "gnustep-1-7"))]
|
||||
assert_eq!(unsafe { val.get::<NSRange>() }, range);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nspoint() {
|
||||
let point = NSPoint::new(1.0, 2.0);
|
||||
let val = NSValue::new(point);
|
||||
assert_eq!(val.get_point(), Some(point));
|
||||
#[cfg(not(feature = "gnustep-1-7"))]
|
||||
assert_eq!(unsafe { val.get::<NSPoint>() }, point);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nssize() {
|
||||
let point = NSSize::new(1.0, 2.0);
|
||||
let val = NSValue::new(point);
|
||||
assert_eq!(val.get_size(), Some(point));
|
||||
#[cfg(not(feature = "gnustep-1-7"))]
|
||||
assert_eq!(unsafe { val.get::<NSSize>() }, point);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nsrect() {
|
||||
let rect = NSRect::new(NSPoint::new(1.0, 2.0), NSSize::new(3.0, 4.0));
|
||||
let val = NSValue::new(rect);
|
||||
assert_eq!(val.get_rect(), Some(rect));
|
||||
#[cfg(not(feature = "gnustep-1-7"))]
|
||||
assert_eq!(unsafe { val.get::<NSRect>() }, rect);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_str() {
|
||||
let s = "abc";
|
||||
let val = NSValue::new(s.as_ptr());
|
||||
assert!(val.contains_encoding::<*const u8>());
|
||||
let slice = unsafe { slice::from_raw_parts(val.get(), s.len()) };
|
||||
let s2 = str::from_utf8(slice).unwrap();
|
||||
assert_eq!(s2, s);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_cstr() {
|
||||
// The following Apple article says that NSValue can't easily store
|
||||
// C-strings, but apparently that doesn't apply to us!
|
||||
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/NumbersandValues/Articles/Values.html#//apple_ref/doc/uid/20000174-BAJJHDEG>
|
||||
let s = CStr::from_bytes_with_nul(b"test123\0").unwrap();
|
||||
let val = NSValue::new(s.as_ptr());
|
||||
assert!(val.contains_encoding::<*const c_char>());
|
||||
let s2 = unsafe { CStr::from_ptr(val.get()) };
|
||||
assert_eq!(s2, s);
|
||||
}
|
||||
}
|
||||
95
third-party/vendor/objc2/src/foundation/zone.rs
vendored
Normal file
95
third-party/vendor/objc2/src/foundation/zone.rs
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
use core::fmt;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
use crate::ffi;
|
||||
#[cfg(feature = "gnustep-1-7")]
|
||||
use crate::Encode;
|
||||
use crate::{Encoding, RefEncode};
|
||||
|
||||
/// A type used to identify and manage memory zones.
|
||||
///
|
||||
/// Zones are ignored on all newer platforms, you should very rarely need to
|
||||
/// use this, but may be useful if you need to implement `copyWithZone:` or
|
||||
/// `allocWithZone:`.
|
||||
///
|
||||
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nszone?language=objc).
|
||||
pub struct NSZone {
|
||||
// Use `objc_object` to mark the types as !Send, !Sync and UnsafeCell.
|
||||
//
|
||||
// This works since `objc_object` is a ZST
|
||||
_inner: ffi::objc_object,
|
||||
}
|
||||
|
||||
impl fmt::Debug for NSZone {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "<NSZone {:p}>", self)
|
||||
}
|
||||
}
|
||||
|
||||
// Note: We don't know anything about the internals of `NSZone`, so best not
|
||||
// to make it `Send` and `Sync` for now.
|
||||
|
||||
impl UnwindSafe for NSZone {}
|
||||
impl RefUnwindSafe for NSZone {}
|
||||
|
||||
unsafe impl RefEncode for NSZone {
|
||||
#[cfg(feature = "apple")]
|
||||
const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Struct("_NSZone", &[]));
|
||||
#[cfg(feature = "gnustep-1-7")]
|
||||
const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Struct(
|
||||
"_NSZone",
|
||||
&[
|
||||
// Functions
|
||||
Encoding::Pointer(&Encoding::Unknown),
|
||||
Encoding::Pointer(&Encoding::Unknown),
|
||||
Encoding::Pointer(&Encoding::Unknown),
|
||||
Encoding::Pointer(&Encoding::Unknown),
|
||||
Encoding::Pointer(&Encoding::Unknown),
|
||||
Encoding::Pointer(&Encoding::Unknown),
|
||||
// Stats
|
||||
Encoding::Pointer(&Encoding::Unknown),
|
||||
// Zone granularity
|
||||
usize::ENCODING,
|
||||
// Name of zone
|
||||
Encoding::Object,
|
||||
// Next zone - note that the contents of this doesn't matter,
|
||||
// since this is nested far enough that the encoding string just
|
||||
// ends up ignoring it.
|
||||
Encoding::Pointer(&Encoding::Struct("_NSZone", &[])),
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::string::ToString;
|
||||
use core::ptr;
|
||||
|
||||
use super::*;
|
||||
use crate::foundation::NSObject;
|
||||
use crate::msg_send_id;
|
||||
use crate::rc::{Allocated, Id, Owned};
|
||||
use crate::ClassType;
|
||||
|
||||
#[test]
|
||||
fn alloc_with_zone() {
|
||||
let zone: *const NSZone = ptr::null();
|
||||
let _obj: Id<Allocated<NSObject>, Owned> =
|
||||
unsafe { msg_send_id![NSObject::class(), allocWithZone: zone] };
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_encoding() {
|
||||
let expected = if cfg!(all(feature = "gnustep-1-7", target_pointer_width = "64")) {
|
||||
"^{_NSZone=^?^?^?^?^?^?^?Q@^{_NSZone}}"
|
||||
} else if cfg!(all(
|
||||
feature = "gnustep-1-7",
|
||||
not(target_pointer_width = "64")
|
||||
)) {
|
||||
"^{_NSZone=^?^?^?^?^?^?^?I@^{_NSZone}}"
|
||||
} else {
|
||||
"^{_NSZone=}"
|
||||
};
|
||||
assert_eq!(NSZone::ENCODING_REF.to_string(), expected);
|
||||
}
|
||||
}
|
||||
260
third-party/vendor/objc2/src/lib.rs
vendored
Normal file
260
third-party/vendor/objc2/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
//! # Objective-C interface and runtime bindings
|
||||
//!
|
||||
//! Objective-C is[^note] the standard programming language on Apple platforms
|
||||
//! like macOS, iOS, iPadOS, tvOS and watchOS. It is an object-oriented
|
||||
//! language centered around "sending messages" to its instances - this can
|
||||
//! for the most part be viewed as a simple method call.
|
||||
//!
|
||||
//! Most of the core libraries and frameworks that are in use on Apple systems
|
||||
//! are written in Objective-C, and hence we would like the ability to
|
||||
//! interract with these using Rust; this crate enables you to do that, in
|
||||
//! as safe a manner as possible.
|
||||
//!
|
||||
//! [^note]: Yes, I know, "was", Swift now exists. All the existing frameworks
|
||||
//! are written in Objective-C though, so the point still holds.
|
||||
//!
|
||||
//!
|
||||
//! ## Basic usage
|
||||
//!
|
||||
//! This example illustrates major parts of the functionality in this crate:
|
||||
//!
|
||||
//! First, we get a reference to the `NSObject`'s [`runtime::Class`] using the
|
||||
//! [`class!`] macro.
|
||||
//! Next, we creates a new [`runtime::Object`] pointer, and ensure it is
|
||||
//! deallocated after we've used it by putting it into an [`rc::Owned`]
|
||||
//! [`rc::Id`].
|
||||
//! Now we're free to send messages to the object to our hearts desire using
|
||||
//! the [`msg_send!`] or [`msg_send_id!`] macros (depending on the return type
|
||||
//! of the method).
|
||||
//! Finally, the `Id<Object, _>` goes out of scope, and the object is released
|
||||
//! and deallocated.
|
||||
//!
|
||||
#![cfg_attr(feature = "apple", doc = "```")]
|
||||
#![cfg_attr(not(feature = "apple"), doc = "```no_run")]
|
||||
//! use objc2::{class, msg_send, msg_send_id};
|
||||
//! use objc2::ffi::NSUInteger;
|
||||
//! use objc2::rc::{Id, Owned, Shared};
|
||||
//! use objc2::runtime::Object;
|
||||
//!
|
||||
//! let cls = class!(NSObject);
|
||||
//!
|
||||
//! // Creation
|
||||
//!
|
||||
//! let obj1: Id<Object, Owned> = unsafe { msg_send_id![cls, new] };
|
||||
//! let obj2: Id<Object, Owned> = unsafe {
|
||||
//! // Equivalent to using `new`
|
||||
//! msg_send_id![msg_send_id![cls, alloc], init]
|
||||
//! };
|
||||
//!
|
||||
//! // Usage
|
||||
//!
|
||||
//! let hash1: NSUInteger = unsafe { msg_send![&obj1, hash] };
|
||||
//! let hash2: NSUInteger = unsafe { msg_send![&obj2, hash] };
|
||||
//! assert_ne!(hash1, hash2);
|
||||
//!
|
||||
//! let is_kind: bool = unsafe { msg_send![&obj1, isKindOfClass: cls] };
|
||||
//! assert!(is_kind);
|
||||
//!
|
||||
//! // We're going to create a new reference to the first object, so
|
||||
//! // relinquish mutable ownership.
|
||||
//! let obj1: Id<Object, Shared> = obj1.into();
|
||||
//! let obj1_self: Id<Object, Shared> = unsafe { msg_send_id![&obj1, self] };
|
||||
//! let is_equal: bool = unsafe { msg_send![&obj1, isEqual: &*obj1_self] };
|
||||
//! assert!(is_equal);
|
||||
//!
|
||||
//! // Deallocation on drop
|
||||
//! ```
|
||||
//!
|
||||
//! Note that this very simple example contains **a lot** of `unsafe` (which
|
||||
//! should all ideally be justified with a `// SAFETY` comment). This is
|
||||
//! required because our compiler can verify very little about the Objective-C
|
||||
//! invocation, including all argument and return types used in [`msg_send!`];
|
||||
//! we could have just as easily accidentally made `hash` an `f32`, or any
|
||||
//! other type, and this would trigger undefined behaviour!
|
||||
//!
|
||||
//! Making the ergonomics better is something that is currently being worked
|
||||
//! on, the [`foundation`] module contains more ergonomic usage of at
|
||||
//! least parts of the `Foundation` framework.
|
||||
//!
|
||||
//! Anyhow, all of this `unsafe` nicely leads us to another feature that this
|
||||
//! crate has:
|
||||
//!
|
||||
//! [`runtime::Class`]: crate::runtime::Class
|
||||
//! [`runtime::Object`]: crate::runtime::Object
|
||||
//! [`rc::Owned`]: crate::rc::Owned
|
||||
//! [`rc::Id`]: crate::rc::Id
|
||||
//! [`foundation`]: crate::foundation
|
||||
//!
|
||||
//!
|
||||
//! ## Encodings and message type verification
|
||||
//!
|
||||
//! The Objective-C runtime includes encodings for each method that describe
|
||||
//! the argument and return types. See the [`objc2-encode`] crate for the
|
||||
//! full overview of what this is (its types are re-exported in this crate).
|
||||
//!
|
||||
//! The important part is: To make message sending safer, all arguments and
|
||||
//! return values for messages must implement [`Encode`]. This allows the Rust
|
||||
//! compiler to prevent you from passing e.g. a [`Box`] into Objective-C,
|
||||
//! which would both be UB and leak the box.
|
||||
//!
|
||||
//! Furthermore, we can take advantage of the encodings provided by the
|
||||
//! runtime to verify that the types used in Rust actually match the types
|
||||
//! encoded for the method. This is not a perfect solution for ensuring safety
|
||||
//! (some Rust types have the same Objective-C encoding, but are not
|
||||
//! equivalent), but it gets us much closer to it!
|
||||
//!
|
||||
//! To use this functionality, enable the `"verify_message"` cargo feature
|
||||
//! while debugging. With this feature enabled, encodings are checked every
|
||||
//! time you send a message, and the message send will panic if they are not
|
||||
//! equivalent.
|
||||
//!
|
||||
//! To take the example above, if we changed the `hash` method's return type
|
||||
//! as in the following example, it panics when the feature is enabled:
|
||||
//!
|
||||
#![cfg_attr(
|
||||
all(feature = "apple", feature = "verify_message"),
|
||||
doc = "```should_panic"
|
||||
)]
|
||||
#![cfg_attr(
|
||||
not(all(feature = "apple", feature = "verify_message")),
|
||||
doc = "```no_run"
|
||||
)]
|
||||
//! # use objc2::{class, msg_send, msg_send_id};
|
||||
//! # use objc2::rc::{Id, Owned};
|
||||
//! # use objc2::runtime::Object;
|
||||
//! #
|
||||
//! # let cls = class!(NSObject);
|
||||
//! # let obj1: Id<Object, Owned> = unsafe { msg_send_id![cls, new] };
|
||||
//! #
|
||||
//! // Wrong return type - this is UB!
|
||||
//! let hash1: f32 = unsafe { msg_send![&obj1, hash] };
|
||||
//! ```
|
||||
//!
|
||||
//! [`objc2-encode`]: objc2_encode
|
||||
//! [`Box`]: std::boxed::Box
|
||||
//!
|
||||
//!
|
||||
//! ## Crate features
|
||||
//!
|
||||
//! This crate exports several optional cargo features, see [`Cargo.toml`] for
|
||||
//! an overview and description of these.
|
||||
//!
|
||||
//! [`Cargo.toml`]: https://github.com/madsmtm/objc2/blob/master/objc2/Cargo.toml
|
||||
//!
|
||||
//!
|
||||
//! ## Support for other Operating Systems
|
||||
//!
|
||||
//! The bindings can be used on Linux or *BSD utilizing the
|
||||
//! [GNUstep Objective-C runtime](https://www.github.com/gnustep/libobjc2),
|
||||
//! see the [`objc-sys`][`objc_sys`] crate for how to configure this.
|
||||
//!
|
||||
//!
|
||||
//! ## Other functionality
|
||||
//!
|
||||
//! That was a quick introduction, this library also has [support for handling
|
||||
//! exceptions][exc], [the ability to dynamically declare Objective-C
|
||||
//! classes][declare], [advanced reference-counting utilities][rc], and more -
|
||||
//! peruse the documentation at will!
|
||||
//!
|
||||
//! [exc]: crate::exception
|
||||
//! [declare]: crate::declare
|
||||
//! [rc]: crate::rc
|
||||
|
||||
#![no_std]
|
||||
#![cfg_attr(
|
||||
feature = "unstable-autoreleasesafe",
|
||||
feature(negative_impls, auto_traits)
|
||||
)]
|
||||
#![cfg_attr(feature = "unstable-c-unwind", feature(c_unwind))]
|
||||
#![cfg_attr(feature = "unstable-docsrs", feature(doc_auto_cfg))]
|
||||
#![warn(elided_lifetimes_in_paths)]
|
||||
#![warn(missing_docs)]
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unreachable_pub)]
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![warn(clippy::cargo)]
|
||||
#![warn(clippy::ptr_as_ptr)]
|
||||
// Update in Cargo.toml as well.
|
||||
#![doc(html_root_url = "https://docs.rs/objc2/0.3.0-beta.3.patch-leaks.3")]
|
||||
|
||||
#[cfg(not(feature = "alloc"))]
|
||||
compile_error!("The `alloc` feature currently must be enabled.");
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
compile_error!("The `std` feature currently must be enabled.");
|
||||
|
||||
extern crate alloc;
|
||||
extern crate std;
|
||||
|
||||
// The example uses NSObject without doing the __gnustep_hack
|
||||
#[cfg(all(feature = "apple", doctest))]
|
||||
#[doc = include_str!("../README.md")]
|
||||
extern "C" {}
|
||||
|
||||
pub use objc2_encode as encode;
|
||||
pub use objc_sys as ffi;
|
||||
|
||||
#[doc(no_inline)]
|
||||
pub use objc2_encode::{Encode, EncodeArguments, Encoding, RefEncode};
|
||||
|
||||
pub use crate::class_type::ClassType;
|
||||
pub use crate::message::{Message, MessageArguments, MessageReceiver};
|
||||
#[cfg(feature = "malloc")]
|
||||
pub use crate::verify::VerificationError;
|
||||
|
||||
#[cfg(feature = "objc2-proc-macros")]
|
||||
#[doc(hidden)]
|
||||
pub use objc2_proc_macros::__hash_idents;
|
||||
|
||||
#[cfg(not(feature = "objc2-proc-macros"))]
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __hash_idents {
|
||||
// Noop; used to make our other macros a bit easier to read
|
||||
($($x:tt)*) => {$($x)*};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod __macro_helpers;
|
||||
mod cache;
|
||||
mod class_type;
|
||||
pub mod declare;
|
||||
pub mod exception;
|
||||
#[cfg(feature = "foundation")]
|
||||
pub mod foundation;
|
||||
mod macros;
|
||||
mod message;
|
||||
pub mod rc;
|
||||
pub mod runtime;
|
||||
#[cfg(test)]
|
||||
mod test_utils;
|
||||
#[cfg(feature = "malloc")]
|
||||
mod verify;
|
||||
|
||||
/// Hacky way to make GNUStep link properly to Foundation while testing.
|
||||
///
|
||||
/// This is a temporary solution to make our CI work for now!
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "gnustep-1-7")]
|
||||
pub mod __gnustep_hack {
|
||||
use super::runtime::Class;
|
||||
|
||||
extern "C" {
|
||||
// The linking changed in libobjc2 v2.0
|
||||
#[cfg_attr(feature = "gnustep-2-0", link_name = "._OBJC_CLASS_NSObject")]
|
||||
#[cfg_attr(not(feature = "gnustep-2-0"), link_name = "_OBJC_CLASS_NSObject")]
|
||||
static OBJC_CLASS_NSObject: Class;
|
||||
// Others:
|
||||
// __objc_class_name_NSObject
|
||||
// _OBJC_CLASS_REF_NSObject
|
||||
}
|
||||
|
||||
pub unsafe fn get_class_to_force_linkage() -> &'static Class {
|
||||
unsafe { core::ptr::read_volatile(&&OBJC_CLASS_NSObject) }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ensure_linkage() {
|
||||
unsafe { get_class_to_force_linkage() };
|
||||
}
|
||||
}
|
||||
1010
third-party/vendor/objc2/src/macros.rs
vendored
Normal file
1010
third-party/vendor/objc2/src/macros.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
102
third-party/vendor/objc2/src/macros/__rewrite_self_arg.rs
vendored
Normal file
102
third-party/vendor/objc2/src/macros/__rewrite_self_arg.rs
vendored
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __rewrite_self_arg {
|
||||
{
|
||||
($out_macro:path)
|
||||
($($args:tt)*)
|
||||
$($macro_args:tt)*
|
||||
} => {
|
||||
$crate::__rewrite_self_arg! {
|
||||
($out_macro)
|
||||
// Duplicate args out so that we can match on `self`, while still
|
||||
// use it as a function argument
|
||||
@($($args)*)
|
||||
@($($args)*)
|
||||
$($macro_args)*
|
||||
}
|
||||
};
|
||||
|
||||
// Instance method
|
||||
{
|
||||
($out_macro:path)
|
||||
@(&mut self $($__rest_args:tt)*)
|
||||
@(&mut $self:ident $(, $($rest:tt)*)?)
|
||||
$($macro_args:tt)*
|
||||
} => {
|
||||
$out_macro! {
|
||||
$($macro_args)*
|
||||
@(instance_method)
|
||||
@(
|
||||
$self: &mut Self,
|
||||
_: $crate::runtime::Sel,
|
||||
)
|
||||
@($($($rest)*)?)
|
||||
}
|
||||
};
|
||||
{
|
||||
($out_macro:path)
|
||||
@(&self $($__rest_args:tt)*)
|
||||
@(&$self:ident $(, $($rest:tt)*)?)
|
||||
$($macro_args:tt)*
|
||||
} => {
|
||||
$out_macro! {
|
||||
$($macro_args)*
|
||||
@(instance_method)
|
||||
@(
|
||||
$self: &Self,
|
||||
_: $crate::runtime::Sel,
|
||||
)
|
||||
@($($($rest)*)?)
|
||||
}
|
||||
};
|
||||
{
|
||||
($out_macro:path)
|
||||
@(mut self: $__self_ty:ty $(, $($__rest_args:tt)*)?)
|
||||
@(mut $self:ident: $self_ty:ty $(, $($rest:tt)*)?)
|
||||
$($macro_args:tt)*
|
||||
} => {
|
||||
$out_macro! {
|
||||
$($macro_args)*
|
||||
@(instance_method)
|
||||
@(
|
||||
mut $self: $self_ty,
|
||||
_: $crate::runtime::Sel,
|
||||
)
|
||||
@($($($rest)*)?)
|
||||
}
|
||||
};
|
||||
{
|
||||
($out_macro:path)
|
||||
@(self: $__self_ty:ty $(, $($__rest_args:tt)*)?)
|
||||
@($self:ident: $self_ty:ty $(, $($rest:tt)*)?)
|
||||
$($macro_args:tt)*
|
||||
} => {
|
||||
$out_macro! {
|
||||
$($macro_args)*
|
||||
@(instance_method)
|
||||
@(
|
||||
$self: $self_ty,
|
||||
_: $crate::runtime::Sel,
|
||||
)
|
||||
@($($($rest)*)?)
|
||||
}
|
||||
};
|
||||
|
||||
// Class method
|
||||
{
|
||||
($out_macro:path)
|
||||
@($($__args:tt)*)
|
||||
@($($args:tt)*)
|
||||
$($macro_args:tt)*
|
||||
} => {
|
||||
$out_macro! {
|
||||
$($macro_args)*
|
||||
@(class_method)
|
||||
@(
|
||||
_: &$crate::runtime::Class,
|
||||
_: $crate::runtime::Sel,
|
||||
)
|
||||
@($($args)*)
|
||||
}
|
||||
};
|
||||
}
|
||||
831
third-party/vendor/objc2/src/macros/declare_class.rs
vendored
Normal file
831
third-party/vendor/objc2/src/macros/declare_class.rs
vendored
Normal file
|
|
@ -0,0 +1,831 @@
|
|||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __inner_declare_class {
|
||||
{@rewrite_methods @($($output:tt)*)} => {};
|
||||
{
|
||||
// Unsafe variant
|
||||
@rewrite_methods
|
||||
@($($output:tt)*)
|
||||
|
||||
$(#[$($m:tt)*])*
|
||||
unsafe fn $name:ident($($args:tt)*) $(-> $ret:ty)? $body:block
|
||||
|
||||
$($rest:tt)*
|
||||
} => {
|
||||
$crate::__rewrite_self_arg! {
|
||||
($crate::__inner_declare_class)
|
||||
($($args)*)
|
||||
|
||||
// Split the function into parts, and send the arguments down to
|
||||
// be used later on
|
||||
@$($output)*
|
||||
@($(#[$($m)*])*)
|
||||
@(unsafe extern "C")
|
||||
@($name)
|
||||
@($($ret)?)
|
||||
@($body)
|
||||
// Will add @(kind)
|
||||
// Will add @(args_start)
|
||||
// Will add @(args_rest)
|
||||
}
|
||||
|
||||
$crate::__inner_declare_class! {
|
||||
@rewrite_methods
|
||||
@($($output)*)
|
||||
|
||||
$($rest)*
|
||||
}
|
||||
};
|
||||
{
|
||||
// Safe variant
|
||||
@rewrite_methods
|
||||
@($($output:tt)*)
|
||||
|
||||
$(#[$($m:tt)*])*
|
||||
fn $name:ident($($args:tt)*) $(-> $ret:ty)? $body:block
|
||||
|
||||
$($rest:tt)*
|
||||
} => {
|
||||
$crate::__rewrite_self_arg! {
|
||||
($crate::__inner_declare_class)
|
||||
($($args)*)
|
||||
@$($output)*
|
||||
@($(#[$($m)*])*)
|
||||
@(extern "C")
|
||||
@($name)
|
||||
@($($ret)?)
|
||||
@($body)
|
||||
|
||||
// Same as above
|
||||
}
|
||||
|
||||
$crate::__inner_declare_class! {
|
||||
@rewrite_methods
|
||||
@($($output)*)
|
||||
|
||||
$($rest)*
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
@method_out
|
||||
@($(#[$($m:tt)*])*)
|
||||
@($($qualifiers:tt)*)
|
||||
@($name:ident)
|
||||
@($($ret:ty)?)
|
||||
@($($body:tt)*)
|
||||
@($($_kind:tt)*)
|
||||
@($($args_start:tt)*)
|
||||
@($($args_rest:tt)*)
|
||||
} => {
|
||||
$crate::__fn_args! {
|
||||
($crate::__inner_declare_class)
|
||||
($($args_rest)*,)
|
||||
()
|
||||
()
|
||||
@method_out_inner
|
||||
@($(#[$($m)*])*)
|
||||
@($($qualifiers)*)
|
||||
@($name)
|
||||
@($($ret)?)
|
||||
@($($body)*)
|
||||
@($($_kind)*)
|
||||
@($($args_start)*)
|
||||
// Will add @(args_converted)
|
||||
// Will add @(body_prefix)
|
||||
}
|
||||
};
|
||||
|
||||
// No return type
|
||||
{
|
||||
@method_out_inner
|
||||
@($(#[$($m:tt)*])*)
|
||||
@($($qualifiers:tt)*)
|
||||
@($name:ident)
|
||||
@()
|
||||
@($($body:tt)*)
|
||||
@($($_kind:tt)*)
|
||||
@($($args_start:tt)*)
|
||||
@($($args_converted:tt)*)
|
||||
@($($body_prefix:tt)*)
|
||||
} => {
|
||||
$crate::__attribute_helper! {
|
||||
@strip_sel
|
||||
$(@[$($m)*])*
|
||||
($($qualifiers)* fn $name($($args_start)* $($args_converted)*) {
|
||||
$($body_prefix)*
|
||||
$($body)*
|
||||
})
|
||||
}
|
||||
};
|
||||
// With return type
|
||||
{
|
||||
@method_out_inner
|
||||
@($(#[$($m:tt)*])*)
|
||||
@($($qualifiers:tt)*)
|
||||
@($name:ident)
|
||||
@($ret:ty)
|
||||
@($($body:tt)*)
|
||||
@($($_kind:tt)*)
|
||||
@($($args_start:tt)*)
|
||||
@($($args_converted:tt)*)
|
||||
@($($body_prefix:tt)*)
|
||||
} => {
|
||||
$crate::__attribute_helper! {
|
||||
@strip_sel
|
||||
$(@[$($m)*])*
|
||||
($($qualifiers)* fn $name($($args_start)* $($args_converted)*) -> <$ret as $crate::encode::EncodeConvert>::__Inner {
|
||||
$($body_prefix)*
|
||||
<$ret as $crate::encode::EncodeConvert>::__into_inner($($body)*)
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
@register_out($builder:ident)
|
||||
@($(#[$($m:tt)*])*)
|
||||
@($($qualifiers:tt)*)
|
||||
@($name:ident)
|
||||
@($($_ret:tt)*)
|
||||
@($($_body:tt)*)
|
||||
@(class_method)
|
||||
@($($args_start:tt)*)
|
||||
@($($args_rest:tt)*)
|
||||
} => {
|
||||
$builder.add_class_method(
|
||||
$crate::__attribute_helper! {
|
||||
@extract_sel
|
||||
($crate::__inner_declare_class)
|
||||
($(#[$($m)*])*)
|
||||
@call_sel
|
||||
},
|
||||
Self::$name as $crate::__fn_ptr! {
|
||||
@($($qualifiers)*) $($args_start)* $($args_rest)*
|
||||
},
|
||||
);
|
||||
};
|
||||
{
|
||||
@register_out($builder:ident)
|
||||
@($(#[$($m:tt)*])*)
|
||||
@($($qualifiers:tt)*)
|
||||
@($name:ident)
|
||||
@($($_ret:tt)*)
|
||||
@($($_body:tt)*)
|
||||
@(instance_method)
|
||||
@($($args_start:tt)*)
|
||||
@($($args_rest:tt)*)
|
||||
} => {
|
||||
$builder.add_method(
|
||||
$crate::__attribute_helper! {
|
||||
@extract_sel
|
||||
($crate::__inner_declare_class)
|
||||
($(#[$($m)*])*)
|
||||
@call_sel
|
||||
},
|
||||
Self::$name as $crate::__fn_ptr! {
|
||||
@($($qualifiers)*) $($args_start)* $($args_rest)*
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
{
|
||||
@call_sel
|
||||
@($($sel:tt)*)
|
||||
} => {
|
||||
$crate::sel!($($sel)*)
|
||||
};
|
||||
}
|
||||
|
||||
/// Create function pointer type with inferred arguments.
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __fn_ptr {
|
||||
(
|
||||
@($($qualifiers:tt)*)
|
||||
$($(mut)? $($param:ident)? $(_)?: $param_ty:ty),* $(,)?
|
||||
) => {
|
||||
$($qualifiers)* fn($($crate::__fn_ptr!(@__to_anonymous $param_ty)),*) -> _
|
||||
};
|
||||
(@__to_anonymous $param_ty:ty) => { _ }
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __fn_args {
|
||||
// Ignore `_`
|
||||
{
|
||||
($out_macro:path)
|
||||
(_: $param_ty:ty, $($rest:tt)*)
|
||||
($($args_converted:tt)*)
|
||||
($($body_prefix:tt)*)
|
||||
$($macro_args:tt)*
|
||||
} => {
|
||||
$crate::__fn_args! {
|
||||
($out_macro)
|
||||
($($rest)*)
|
||||
($($args_converted)* _: $param_ty,)
|
||||
($($body_prefix)*)
|
||||
$($macro_args)*
|
||||
}
|
||||
};
|
||||
// Convert mut
|
||||
{
|
||||
($out_macro:path)
|
||||
(mut $param:ident: $param_ty:ty, $($rest:tt)*)
|
||||
($($args_converted:tt)*)
|
||||
($($body_prefix:tt)*)
|
||||
$($macro_args:tt)*
|
||||
} => {
|
||||
$crate::__fn_args! {
|
||||
($out_macro)
|
||||
($($rest)*)
|
||||
($($args_converted)* $param: <$param_ty as $crate::encode::EncodeConvert>::__Inner,)
|
||||
(
|
||||
$($body_prefix)*
|
||||
let mut $param = <$param_ty as $crate::encode::EncodeConvert>::__from_inner($param);
|
||||
)
|
||||
$($macro_args)*
|
||||
}
|
||||
};
|
||||
// Convert
|
||||
{
|
||||
($out_macro:path)
|
||||
($param:ident: $param_ty:ty, $($rest:tt)*)
|
||||
($($args_converted:tt)*)
|
||||
($($body_prefix:tt)*)
|
||||
$($macro_args:tt)*
|
||||
} => {
|
||||
$crate::__fn_args! {
|
||||
($out_macro)
|
||||
($($rest)*)
|
||||
($($args_converted)* $param: <$param_ty as $crate::encode::EncodeConvert>::__Inner,)
|
||||
(
|
||||
$($body_prefix)*
|
||||
let $param = <$param_ty as $crate::encode::EncodeConvert>::__from_inner($param);
|
||||
)
|
||||
$($macro_args)*
|
||||
}
|
||||
};
|
||||
// Output result
|
||||
{
|
||||
($out_macro:path)
|
||||
($(,)*)
|
||||
($($args_converted:tt)*)
|
||||
($($body_prefix:tt)*)
|
||||
$($macro_args:tt)*
|
||||
} => {
|
||||
$out_macro! {
|
||||
$($macro_args)*
|
||||
@($($args_converted)*)
|
||||
@($($body_prefix)*)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Declare a new Objective-C class.
|
||||
///
|
||||
/// This is mostly just a convenience macro on top of [`extern_class!`] and
|
||||
/// the functionality in the [`declare`] module, but it can really help
|
||||
/// with cutting down on boilerplate, in particular when defining delegate
|
||||
/// classes!
|
||||
///
|
||||
///
|
||||
/// # Specification
|
||||
///
|
||||
/// This macro consists of three parts:
|
||||
/// - The class definition + ivar definition + inheritance specification.
|
||||
/// - A set of method definitions.
|
||||
/// - A set of protocol definitions.
|
||||
///
|
||||
///
|
||||
/// ## Class and ivar definition
|
||||
///
|
||||
/// The class definition works a lot like [`extern_class!`], with the added
|
||||
/// functionality that you can define custom instance variables on your class,
|
||||
/// which are then wrapped in a [`declare::Ivar`] and made accessible
|
||||
/// through the class. (E.g. you can use `self.my_ivar` as if it was a normal
|
||||
/// Rust struct).
|
||||
///
|
||||
/// Note that the class name should be unique across the entire application!
|
||||
/// You can declare the class with the desired unique name like
|
||||
/// `"MyCrateCustomObject"` by specifying it in `ClassType::NAME`, and then
|
||||
/// give the exposed type a different name like `CustomObject`.
|
||||
///
|
||||
/// The class is guaranteed to have been created and registered with the
|
||||
/// Objective-C runtime after the [`ClassType::class`] function has been
|
||||
/// called.
|
||||
///
|
||||
/// If any of the instance variables require being `Drop`'ed (e.g. are wrapped
|
||||
/// in [`declare::IvarDrop`]), this macro will generate a `dealloc` method
|
||||
/// automatically.
|
||||
///
|
||||
/// [`ClassType::class`]: crate::ClassType::class
|
||||
/// [`declare::IvarDrop`]: crate::declare::IvarDrop
|
||||
///
|
||||
///
|
||||
/// ## Method definitions
|
||||
///
|
||||
/// Within the `impl` block you can define two types of functions;
|
||||
/// ["associated functions"] and ["methods"]. These are then mapped to the
|
||||
/// Objective-C equivalents "class methods" and "instance methods". In
|
||||
/// particular, if you use `self` your method will be registered as an
|
||||
/// instance method, and if you don't it will be registered as a class method.
|
||||
///
|
||||
/// The desired selector can be specified using the `#[sel(my:selector:)]`
|
||||
/// attribute, similar to the [`extern_methods!`] macro.
|
||||
///
|
||||
/// A transformation step is performed on the functions (to make them have the
|
||||
/// correct ABI) and hence they shouldn't really be called manually. (You
|
||||
/// can't mark them as `pub` for the same reason). Instead, use the
|
||||
/// [`extern_methods!`] macro to create a Rust interface to the methods.
|
||||
///
|
||||
/// If the argument or return type is [`bool`], a conversion is performed to
|
||||
/// make it behave similarly to the Objective-C `BOOL`. Use [`runtime::Bool`]
|
||||
/// if you want to control this manually.
|
||||
///
|
||||
/// ["associated functions"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods
|
||||
/// ["methods"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods
|
||||
/// [`extern_methods!`]: crate::extern_methods
|
||||
/// [`msg_send!`]: crate::msg_send
|
||||
/// [`runtime::Bool`]: crate::runtime::Bool
|
||||
///
|
||||
///
|
||||
/// ## Protocol definitions
|
||||
///
|
||||
/// You can specify protocols that the class should implement, along with any
|
||||
/// required/optional methods for said protocols.
|
||||
///
|
||||
/// The methods work exactly as normal, they're only put "under" the protocol
|
||||
/// definition to make things easier to read.
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Using this macro requires writing a few `unsafe` markers:
|
||||
///
|
||||
/// `unsafe impl ClassType for T` has the following safety requirements:
|
||||
/// - Same as [`extern_class!`] (the inheritance chain has to be correct).
|
||||
/// - Any instance variables you specify under the struct definition must
|
||||
/// either be able to be created using [`MaybeUninit::zeroed`], or be
|
||||
/// properly initialized in an `init` method.
|
||||
///
|
||||
/// `unsafe impl T { ... }` asserts that the types match those that are
|
||||
/// expected when the method is invoked from Objective-C. Note that there are
|
||||
/// no safe-guards here; you can easily write `i8`, but if Objective-C thinks
|
||||
/// it's an `u32`, it will cause UB when called!
|
||||
///
|
||||
/// `unsafe impl Protocol<P> for T { ... }` requires that all required methods
|
||||
/// of the specified protocol is implemented, and that any extra requirements
|
||||
/// (implicit or explicit) that the protocol has are upheld. The methods in
|
||||
/// this definition has the same safety requirements as above.
|
||||
///
|
||||
/// [`MaybeUninit::zeroed`]: core::mem::MaybeUninit::zeroed
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Declare a class `MyCustomObject` that inherits `NSObject`, has a few
|
||||
/// instance variables and methods, and implements the `NSCopying` protocol.
|
||||
///
|
||||
/// ```
|
||||
/// use std::os::raw::c_int;
|
||||
/// use objc2::declare::{Ivar, IvarDrop};
|
||||
/// use objc2::rc::{Id, Owned, Shared};
|
||||
/// use objc2::foundation::{NSCopying, NSObject, NSString, NSZone};
|
||||
/// use objc2::{declare_class, msg_send, msg_send_id, ns_string, ClassType};
|
||||
/// #
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// declare_class!(
|
||||
/// struct MyCustomObject {
|
||||
/// foo: u8,
|
||||
/// pub bar: c_int,
|
||||
/// string: IvarDrop<Id<NSString, Shared>>,
|
||||
/// }
|
||||
///
|
||||
/// unsafe impl ClassType for MyCustomObject {
|
||||
/// type Super = NSObject;
|
||||
/// // Optionally specify a different name
|
||||
/// // const NAME: &'static str = "MyCustomObject";
|
||||
/// }
|
||||
///
|
||||
/// unsafe impl MyCustomObject {
|
||||
/// #[sel(initWithFoo:)]
|
||||
/// fn init_with(&mut self, foo: u8) -> Option<&mut Self> {
|
||||
/// let this: Option<&mut Self> = unsafe {
|
||||
/// msg_send![super(self), init]
|
||||
/// };
|
||||
///
|
||||
/// // TODO: `ns_string` can't be used inside closures.
|
||||
/// let s = ns_string!("abc");
|
||||
///
|
||||
/// this.map(|this| {
|
||||
/// // Initialize instance variables
|
||||
///
|
||||
/// // Some types like `u8`, `bool`, `Option<Box<T>>` and
|
||||
/// // `Option<Id<T, O>>` are safe to zero-initialize, and
|
||||
/// // we can simply write to the variable as normal:
|
||||
/// *this.foo = foo;
|
||||
/// *this.bar = 42;
|
||||
///
|
||||
/// // For others like `&u8`, `Box<T>` or `Id<T, O>`, we have
|
||||
/// // to initialize them with `Ivar::write`:
|
||||
/// Ivar::write(&mut this.string, s.copy());
|
||||
///
|
||||
/// // All the instance variables have been initialized; our
|
||||
/// // initializer is sound
|
||||
/// this
|
||||
/// })
|
||||
/// }
|
||||
///
|
||||
/// #[sel(foo)]
|
||||
/// fn __get_foo(&self) -> u8 {
|
||||
/// *self.foo
|
||||
/// }
|
||||
///
|
||||
/// #[sel(string)]
|
||||
/// fn __get_string(&self) -> *mut NSString {
|
||||
/// Id::autorelease_return((*self.string).copy())
|
||||
/// }
|
||||
///
|
||||
/// #[sel(myClassMethod)]
|
||||
/// fn __my_class_method() -> bool {
|
||||
/// true
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// unsafe impl Protocol<NSCopying> for MyCustomObject {
|
||||
/// #[sel(copyWithZone:)]
|
||||
/// fn copy_with_zone(&self, _zone: *const NSZone) -> *mut Self {
|
||||
/// let mut obj = Self::new(*self.foo);
|
||||
/// *obj.bar = *self.bar;
|
||||
/// obj.autorelease_return()
|
||||
/// }
|
||||
/// }
|
||||
/// );
|
||||
///
|
||||
/// impl MyCustomObject {
|
||||
/// pub fn new(foo: u8) -> Id<Self, Owned> {
|
||||
/// let cls = Self::class();
|
||||
/// unsafe { msg_send_id![msg_send_id![cls, alloc], initWithFoo: foo] }
|
||||
/// }
|
||||
///
|
||||
/// pub fn get_foo(&self) -> u8 {
|
||||
/// unsafe { msg_send![self, foo] }
|
||||
/// }
|
||||
///
|
||||
/// pub fn get_string(&self) -> Id<NSString, Shared> {
|
||||
/// unsafe { msg_send_id![self, string] }
|
||||
/// }
|
||||
///
|
||||
/// pub fn my_class_method() -> bool {
|
||||
/// unsafe { msg_send![Self::class(), myClassMethod] }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// unsafe impl NSCopying for MyCustomObject {
|
||||
/// type Ownership = Owned;
|
||||
/// type Output = Self;
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let obj = MyCustomObject::new(3);
|
||||
/// assert_eq!(*obj.foo, 3);
|
||||
/// assert_eq!(*obj.bar, 42);
|
||||
/// assert_eq!(*obj.string, NSString::from_str("abc"));
|
||||
///
|
||||
/// let obj = obj.copy();
|
||||
/// assert_eq!(obj.get_foo(), 3);
|
||||
/// assert_eq!(obj.get_string(), NSString::from_str("abc"));
|
||||
///
|
||||
/// assert!(MyCustomObject::my_class_method());
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Approximately equivalent to the following ARC-enabled Objective-C code.
|
||||
///
|
||||
/// ```text
|
||||
/// #import <Foundation/Foundation.h>
|
||||
///
|
||||
/// @interface MyCustomObject: NSObject <NSCopying> {
|
||||
/// // Public ivar
|
||||
/// int bar;
|
||||
/// }
|
||||
///
|
||||
/// - (instancetype)initWithFoo:(uint8_t)foo;
|
||||
/// - (uint8_t)foo;
|
||||
/// - (NSString*)string;
|
||||
/// + (BOOL)myClassMethod;
|
||||
///
|
||||
/// @end
|
||||
///
|
||||
///
|
||||
/// @implementation MyCustomObject {
|
||||
/// // Private ivar
|
||||
/// uint8_t foo;
|
||||
/// NSString* _Nonnull string;
|
||||
/// }
|
||||
///
|
||||
/// - (instancetype)initWithFoo:(uint8_t)foo_arg {
|
||||
/// self = [super init];
|
||||
/// if (self) {
|
||||
/// self->foo = foo_arg;
|
||||
/// self->bar = 42;
|
||||
/// self->string = @"abc";
|
||||
/// }
|
||||
/// return self;
|
||||
/// }
|
||||
///
|
||||
/// - (uint8_t)foo {
|
||||
/// return self->foo; // Or just `foo`
|
||||
/// }
|
||||
///
|
||||
/// - (NSString*)string {
|
||||
/// return self->string;
|
||||
/// }
|
||||
///
|
||||
/// + (BOOL)myClassMethod {
|
||||
/// return YES;
|
||||
/// }
|
||||
///
|
||||
/// // NSCopying
|
||||
///
|
||||
/// - (id)copyWithZone:(NSZone *)_zone {
|
||||
/// MyCustomObject* obj = [[MyCustomObject alloc] initWithFoo: self->foo];
|
||||
/// obj->bar = self->bar;
|
||||
/// obj->string = self->string;
|
||||
/// return obj;
|
||||
/// }
|
||||
///
|
||||
/// @end
|
||||
/// ```
|
||||
///
|
||||
/// [`extern_class!`]: crate::extern_class
|
||||
/// [`declare`]: crate::declare
|
||||
/// [`declare::Ivar`]: crate::declare::Ivar
|
||||
#[doc(alias = "@interface")]
|
||||
#[doc(alias = "@implementation")]
|
||||
#[macro_export]
|
||||
macro_rules! declare_class {
|
||||
{
|
||||
$(#[$m:meta])*
|
||||
$v:vis struct $name:ident {
|
||||
$($ivar_v:vis $ivar:ident: $ivar_ty:ty,)*
|
||||
}
|
||||
|
||||
unsafe impl ClassType for $for:ty {
|
||||
$(#[inherits($($inheritance_rest:ty),+)])?
|
||||
type Super = $superclass:ty;
|
||||
|
||||
$(const NAME: &'static str = $name_const:literal;)?
|
||||
}
|
||||
|
||||
$($methods:tt)*
|
||||
} => {
|
||||
$(
|
||||
#[allow(non_camel_case_types)]
|
||||
$ivar_v struct $ivar {
|
||||
__priv: (),
|
||||
}
|
||||
|
||||
unsafe impl $crate::declare::IvarType for $ivar {
|
||||
type Type = $ivar_ty;
|
||||
const NAME: &'static str = stringify!($ivar);
|
||||
}
|
||||
)*
|
||||
|
||||
$crate::__inner_extern_class! {
|
||||
@__inner
|
||||
$(#[$m])*
|
||||
// SAFETY: Upheld by caller
|
||||
$v struct $name () {
|
||||
// SAFETY:
|
||||
// - The ivars are in a type used as an Objective-C object.
|
||||
// - The ivar is added to the class below.
|
||||
// - Rust prevents having two fields with the same name.
|
||||
// - Caller upholds that the ivars are properly initialized.
|
||||
$($ivar_v $ivar: $crate::declare::Ivar<$ivar>,)*
|
||||
}
|
||||
|
||||
unsafe impl () for $for {
|
||||
INHERITS = [$superclass, $($($inheritance_rest,)+)? $crate::runtime::Object];
|
||||
}
|
||||
}
|
||||
|
||||
// Creation
|
||||
unsafe impl ClassType for $for {
|
||||
type Super = $superclass;
|
||||
const NAME: &'static str = $crate::__select_name!($name; $($name_const)?);
|
||||
|
||||
fn class() -> &'static $crate::runtime::Class {
|
||||
// TODO: Use `core::cell::LazyCell`
|
||||
use $crate::__macro_helpers::Once;
|
||||
|
||||
static REGISTER_CLASS: Once = Once::new();
|
||||
|
||||
REGISTER_CLASS.call_once(|| {
|
||||
let superclass = <$superclass as $crate::ClassType>::class();
|
||||
let err_str = concat!(
|
||||
"could not create new class ",
|
||||
$crate::__select_name!($name; $($name_const)?),
|
||||
". Perhaps a class with that name already exists?",
|
||||
);
|
||||
let mut builder = $crate::declare::ClassBuilder::new(Self::NAME, superclass).expect(err_str);
|
||||
|
||||
// Ivars
|
||||
$(
|
||||
builder.add_static_ivar::<$ivar>();
|
||||
)*
|
||||
|
||||
// Check whether we need to add a `dealloc` method
|
||||
if false $(
|
||||
|| <<$ivar as $crate::declare::IvarType>::Type as $crate::declare::InnerIvarType>::__MAY_DROP
|
||||
)* {
|
||||
unsafe {
|
||||
builder.add_method(
|
||||
$crate::sel!(dealloc),
|
||||
Self::__objc2_dealloc as unsafe extern "C" fn(_, _),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Implement protocols and methods
|
||||
$crate::__declare_class_methods!(
|
||||
@register_out(builder)
|
||||
$($methods)*
|
||||
);
|
||||
|
||||
let _cls = builder.register();
|
||||
});
|
||||
|
||||
// We just registered the class, so it should be available
|
||||
$crate::runtime::Class::get(Self::NAME).unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_super(&self) -> &Self::Super {
|
||||
&self.__inner
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_super_mut(&mut self) -> &mut Self::Super {
|
||||
&mut self.__inner
|
||||
}
|
||||
}
|
||||
|
||||
impl $for {
|
||||
// See the following links for more details:
|
||||
// - <https://clang.llvm.org/docs/AutomaticReferenceCounting.html#dealloc>
|
||||
// - <https://developer.apple.com/documentation/objectivec/nsobject/1571947-dealloc>
|
||||
// - <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-SW2>
|
||||
unsafe extern "C" fn __objc2_dealloc(&mut self, _cmd: $crate::runtime::Sel) {
|
||||
$(
|
||||
let ptr: *mut $crate::declare::Ivar<$ivar> = &mut self.$ivar;
|
||||
// SAFETY: The ivar is valid, and since this is the
|
||||
// `dealloc` method, we know the ivars are never going to
|
||||
// be touched again.
|
||||
unsafe { $crate::__macro_helpers::drop_in_place(ptr) };
|
||||
)*
|
||||
|
||||
// Invoke the super class' `dealloc` method.
|
||||
//
|
||||
// Note: ARC does this automatically, so most Objective-C code
|
||||
// in the wild don't contain this; but we don't have ARC, so
|
||||
// we must do this.
|
||||
unsafe { $crate::msg_send![super(self), dealloc] }
|
||||
}
|
||||
}
|
||||
|
||||
// Methods
|
||||
$crate::__declare_class_methods!(
|
||||
@method_out
|
||||
$($methods)*
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __select_name {
|
||||
($_name:ident; $name_const:literal) => {
|
||||
$name_const
|
||||
};
|
||||
($name:ident;) => {
|
||||
$crate::__macro_helpers::stringify!($name)
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __declare_class_methods {
|
||||
(@method_out) => {};
|
||||
// With protocol
|
||||
(
|
||||
@method_out
|
||||
|
||||
$(#[$m:meta])*
|
||||
unsafe impl Protocol<$protocol:ident> for $for:ty {
|
||||
$($methods:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
$(#[$m])*
|
||||
impl $for {
|
||||
$crate::__inner_declare_class! {
|
||||
@rewrite_methods
|
||||
@(method_out)
|
||||
$($methods)*
|
||||
}
|
||||
}
|
||||
|
||||
$crate::__declare_class_methods!(
|
||||
@method_out
|
||||
$($rest)*
|
||||
);
|
||||
};
|
||||
// Without protocol
|
||||
(
|
||||
@method_out
|
||||
|
||||
$(#[$m:meta])*
|
||||
unsafe impl $for:ty {
|
||||
$($methods:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
$(#[$m])*
|
||||
impl $for {
|
||||
$crate::__inner_declare_class! {
|
||||
@rewrite_methods
|
||||
@(method_out)
|
||||
$($methods)*
|
||||
}
|
||||
}
|
||||
|
||||
$crate::__declare_class_methods!(
|
||||
@method_out
|
||||
$($rest)*
|
||||
);
|
||||
};
|
||||
|
||||
(@register_out($builder:ident)) => {};
|
||||
// With protocol
|
||||
(
|
||||
@register_out($builder:ident)
|
||||
|
||||
$(#[$m:meta])*
|
||||
unsafe impl Protocol<$protocol:ident> for $for:ty {
|
||||
$($methods:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
// Implement protocol
|
||||
let err_str = concat!("could not find protocol ", stringify!($protocol));
|
||||
$builder.add_protocol($crate::runtime::Protocol::get(stringify!($protocol)).expect(err_str));
|
||||
|
||||
// SAFETY: Upheld by caller
|
||||
unsafe {
|
||||
$crate::__inner_declare_class! {
|
||||
@rewrite_methods
|
||||
@(register_out($builder))
|
||||
|
||||
$($methods)*
|
||||
}
|
||||
}
|
||||
|
||||
$crate::__declare_class_methods!(
|
||||
@register_out($builder)
|
||||
$($rest)*
|
||||
);
|
||||
};
|
||||
// Without protocol
|
||||
(
|
||||
@register_out($builder:ident)
|
||||
|
||||
$(#[$m:meta])*
|
||||
unsafe impl $for:ty {
|
||||
$($methods:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
// SAFETY: Upheld by caller
|
||||
unsafe {
|
||||
$crate::__inner_declare_class! {
|
||||
@rewrite_methods
|
||||
@(register_out($builder))
|
||||
|
||||
$($methods)*
|
||||
}
|
||||
}
|
||||
|
||||
$crate::__declare_class_methods!(
|
||||
@register_out($builder)
|
||||
$($rest)*
|
||||
);
|
||||
};
|
||||
}
|
||||
491
third-party/vendor/objc2/src/macros/extern_class.rs
vendored
Normal file
491
third-party/vendor/objc2/src/macros/extern_class.rs
vendored
Normal file
|
|
@ -0,0 +1,491 @@
|
|||
/// Create a new type to represent an Objective-C class.
|
||||
///
|
||||
/// This is similar to an `@interface` declaration in Objective-C.
|
||||
///
|
||||
/// The given struct name should correspond to a valid Objective-C class,
|
||||
/// whose instances have the encoding [`Encoding::Object`]. (as an example:
|
||||
/// `NSAutoreleasePool` does not have this!)
|
||||
///
|
||||
/// You must specify the superclass of this class, similar to how you would
|
||||
/// in Objective-C.
|
||||
///
|
||||
/// Due to Rust trait limitations, specifying e.g. the superclass `NSData`
|
||||
/// would not give you easy access to `NSObject`'s functionality. Therefore,
|
||||
/// you may specify additional parts of the inheritance chain using the
|
||||
/// `#[inherits(...)]` attribute.
|
||||
///
|
||||
/// [`Encoding::Object`]: crate::Encoding::Object
|
||||
///
|
||||
///
|
||||
/// # Specification
|
||||
///
|
||||
/// The syntax is similar enough to Rust syntax that if you invoke the macro
|
||||
/// with parentheses (as opposed to curly brackets), [`rustfmt` will be able to
|
||||
/// format the contents][rustfmt-macros].
|
||||
///
|
||||
/// This creates an opaque struct containing the superclass (which means that
|
||||
/// auto traits are inherited from the superclass), and implements the
|
||||
/// following traits for it to allow easier usage as an Objective-C object:
|
||||
///
|
||||
/// - [`RefEncode`][crate::RefEncode]
|
||||
/// - [`Message`][crate::Message]
|
||||
/// - [`ClassType`][crate::ClassType]
|
||||
/// - [`Deref<Target = $superclass>`][core::ops::Deref]
|
||||
/// - [`DerefMut`][core::ops::DerefMut]
|
||||
/// - [`AsRef<$inheritance_chain>`][AsRef]
|
||||
/// - [`AsMut<$inheritance_chain>`][AsMut]
|
||||
/// - [`Borrow<$inheritance_chain>`][core::borrow::Borrow]
|
||||
/// - [`BorrowMut<$inheritance_chain>`][core::borrow::BorrowMut]
|
||||
///
|
||||
/// The macro allows specifying fields on the struct, but _only_ zero-sized
|
||||
/// types like [`PhantomData`] and [`declare::Ivar`] are allowed here!
|
||||
///
|
||||
/// [rustfmt-macros]: https://github.com/rust-lang/rustfmt/discussions/5437
|
||||
/// [`PhantomData`]: core::marker::PhantomData
|
||||
/// [`declare::Ivar`]: crate::declare::Ivar
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The specified superclass must be correct. The object must also respond to
|
||||
/// standard memory management messages (this is upheld if [`NSObject`] is
|
||||
/// part of its inheritance chain).
|
||||
///
|
||||
/// [`NSObject`]: crate::foundation::NSObject
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create a new type to represent the `NSFormatter` class.
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::NSObject;
|
||||
/// use objc2::rc::{Id, Shared};
|
||||
/// use objc2::{ClassType, extern_class, msg_send_id};
|
||||
/// #
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// extern_class!(
|
||||
/// /// An example description.
|
||||
/// #[derive(PartialEq, Eq, Hash)] // Uses the superclass' implementation
|
||||
/// // Specify the class and struct name to be used
|
||||
/// pub struct NSFormatter;
|
||||
///
|
||||
/// // Specify the superclass, in this case `NSObject`
|
||||
/// unsafe impl ClassType for NSFormatter {
|
||||
/// type Super = NSObject;
|
||||
/// }
|
||||
/// );
|
||||
///
|
||||
/// // Provided by the implementation of `ClassType`
|
||||
/// let cls = NSFormatter::class();
|
||||
///
|
||||
/// // `NSFormatter` implements `Message`:
|
||||
/// let obj: Id<NSFormatter, Shared> = unsafe { msg_send_id![cls, new] };
|
||||
/// ```
|
||||
///
|
||||
/// Represent the `NSDateFormatter` class, using the `NSFormatter` type we
|
||||
/// declared previously to specify as its superclass.
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::NSObject;
|
||||
/// use objc2::{extern_class, ClassType};
|
||||
/// #
|
||||
/// # extern_class!(
|
||||
/// # #[derive(PartialEq, Eq, Hash)]
|
||||
/// # pub struct NSFormatter;
|
||||
/// #
|
||||
/// # unsafe impl ClassType for NSFormatter {
|
||||
/// # type Super = NSObject;
|
||||
/// # }
|
||||
/// # );
|
||||
///
|
||||
/// extern_class!(
|
||||
/// #[derive(PartialEq, Eq, Hash)]
|
||||
/// pub struct NSDateFormatter;
|
||||
///
|
||||
/// unsafe impl ClassType for NSDateFormatter {
|
||||
/// // Specify the correct inheritance chain
|
||||
/// #[inherits(NSObject)]
|
||||
/// type Super = NSFormatter;
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// See the source code of `objc2::foundation` in general for more examples.
|
||||
#[doc(alias = "@interface")]
|
||||
#[macro_export]
|
||||
macro_rules! extern_class {
|
||||
(
|
||||
$(#[$m:meta])*
|
||||
$v:vis struct $name:ident;
|
||||
|
||||
unsafe impl ClassType for $for:ty {
|
||||
$(#[inherits($($inheritance_rest:ty),+)])?
|
||||
type Super = $superclass:ty;
|
||||
}
|
||||
) => {
|
||||
// Just shorthand syntax for the following
|
||||
$crate::extern_class!(
|
||||
$(#[$m])*
|
||||
$v struct $name {}
|
||||
|
||||
unsafe impl ClassType for $for {
|
||||
$(#[inherits($($inheritance_rest),+)])?
|
||||
type Super = $superclass;
|
||||
}
|
||||
);
|
||||
};
|
||||
(
|
||||
$(#[$m:meta])*
|
||||
$v:vis struct $name:ident {
|
||||
$($field_vis:vis $field:ident: $field_ty:ty,)*
|
||||
}
|
||||
|
||||
unsafe impl ClassType for $for:ty {
|
||||
$(#[inherits($($inheritance_rest:ty),+)])?
|
||||
type Super = $superclass:ty;
|
||||
}
|
||||
) => {
|
||||
$crate::__inner_extern_class!(
|
||||
$(#[$m])*
|
||||
$v struct $name<> {
|
||||
$($field_vis $field: $field_ty,)*
|
||||
}
|
||||
|
||||
unsafe impl<> ClassType for $for {
|
||||
$(#[inherits($($inheritance_rest),+)])?
|
||||
type Super = $superclass;
|
||||
}
|
||||
);
|
||||
|
||||
const _: () = {
|
||||
if $crate::__macro_helpers::size_of::<$name>() != 0 {
|
||||
panic!(concat!(
|
||||
"the struct ",
|
||||
stringify!($name),
|
||||
" is not zero-sized!",
|
||||
))
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __impl_as_ref_borrow {
|
||||
{
|
||||
impl ($($t:tt)*) for $for:ty;
|
||||
} => {};
|
||||
{
|
||||
impl ($($t:tt)*) for $for:ty; $item:ty, $($tail:ty,)*
|
||||
} => {
|
||||
impl<$($t)*> $crate::__macro_helpers::AsRef<$item> for $for {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &$item {
|
||||
// Triggers Deref coercion depending on return type
|
||||
&*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($t)*> $crate::__macro_helpers::AsMut<$item> for $for {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut $item {
|
||||
// Triggers DerefMut coercion depending on return type
|
||||
&mut *self
|
||||
}
|
||||
}
|
||||
|
||||
// Borrow and BorrowMut are correct, since subclasses behaves
|
||||
// identical to the class they inherit (message sending doesn't care).
|
||||
//
|
||||
// In particular, `Eq`, `Ord` and `Hash` all give the same results
|
||||
// after borrow.
|
||||
|
||||
impl<$($t)*> $crate::__macro_helpers::Borrow<$item> for $for {
|
||||
#[inline]
|
||||
fn borrow(&self) -> &$item {
|
||||
// Triggers Deref coercion depending on return type
|
||||
&*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($t)*> $crate::__macro_helpers::BorrowMut<$item> for $for {
|
||||
#[inline]
|
||||
fn borrow_mut(&mut self) -> &mut $item {
|
||||
// Triggers Deref coercion depending on return type
|
||||
&mut *self
|
||||
}
|
||||
}
|
||||
|
||||
$crate::__impl_as_ref_borrow! {
|
||||
impl ($($t)*) for $for; $($tail,)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __inner_extern_class {
|
||||
// TODO: Expose this variant in the `object` macro.
|
||||
(
|
||||
$(#[$m:meta])*
|
||||
$v:vis struct $name:ident<$($t_struct:ident $(: $b_struct:ident $(= $default:ty)?)?),*> {
|
||||
$($field_vis:vis $field:ident: $field_ty:ty,)*
|
||||
}
|
||||
|
||||
unsafe impl<$($t_for:ident $(: $b_for:ident)?),*> ClassType for $for:ty {
|
||||
$(#[inherits($($inheritance_rest:ty),+)])?
|
||||
type Super = $superclass:ty;
|
||||
}
|
||||
) => {
|
||||
$crate::__inner_extern_class! {
|
||||
@__inner
|
||||
$(#[$m])*
|
||||
$v struct $name ($($t_struct $(: $b_struct $(= $default)?)?),*) {
|
||||
$($field_vis $field: $field_ty,)*
|
||||
}
|
||||
|
||||
unsafe impl ($($t_for $(: $b_for)?),*) for $for {
|
||||
INHERITS = [$superclass, $($($inheritance_rest,)+)? $crate::runtime::Object];
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<$($t_for $(: $b_for)?),*> ClassType for $for {
|
||||
type Super = $superclass;
|
||||
const NAME: &'static str = stringify!($name);
|
||||
|
||||
#[inline]
|
||||
fn class() -> &'static $crate::runtime::Class {
|
||||
$crate::class!($name)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_super(&self) -> &Self::Super {
|
||||
&self.__inner
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_super_mut(&mut self) -> &mut Self::Super {
|
||||
&mut self.__inner
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
@__inner
|
||||
|
||||
$(#[$m:meta])*
|
||||
$v:vis struct $name:ident ($($t_struct:tt)*) {
|
||||
$($field_vis:vis $field:ident: $field_ty:ty,)*
|
||||
}
|
||||
|
||||
unsafe impl ($($t:tt)*) for $for:ty {
|
||||
INHERITS = [$superclass:ty $(, $inheritance_rest:ty)*];
|
||||
}
|
||||
) => {
|
||||
$(#[$m])*
|
||||
// TODO: repr(transparent) when the inner pointer is no longer a ZST.
|
||||
#[repr(C)]
|
||||
$v struct $name<$($t_struct)*> {
|
||||
__inner: $superclass,
|
||||
// Additional fields (should only be zero-sized PhantomData or ivars).
|
||||
$($field_vis $field: $field_ty,)*
|
||||
}
|
||||
|
||||
// SAFETY:
|
||||
// - The item is FFI-safe with `#[repr(C)]`.
|
||||
// - The encoding is taken from the inner item, and caller verifies
|
||||
// that it actually inherits said object.
|
||||
// - The rest of the struct's fields are ZSTs, so they don't influence
|
||||
// the layout.
|
||||
unsafe impl<$($t)*> $crate::RefEncode for $for {
|
||||
const ENCODING_REF: $crate::Encoding
|
||||
= <$superclass as $crate::RefEncode>::ENCODING_REF;
|
||||
}
|
||||
|
||||
// SAFETY: This is essentially just a newtype wrapper over `Object`
|
||||
// (we even ensure that `Object` is always last in our inheritance
|
||||
// tree), so it is always safe to reinterpret as that.
|
||||
//
|
||||
// That the object must work with standard memory management is upheld
|
||||
// by the caller.
|
||||
unsafe impl<$($t)*> $crate::Message for $for {}
|
||||
|
||||
// SAFETY: An instance can always be _used_ in exactly the same way as
|
||||
// its superclasses (though not necessarily _constructed_ in the same
|
||||
// way, but `Deref` doesn't allow this).
|
||||
//
|
||||
// Remember; while we (the Rust side) may intentionally be forgetting
|
||||
// which instance we're holding, the Objective-C side will remember,
|
||||
// and will always dispatch to the correct method implementations.
|
||||
//
|
||||
// Any lifetime information that the object may have been holding is
|
||||
// safely kept in the returned reference.
|
||||
//
|
||||
// Generics are discarded (for example in the case of `&NSArray<T, O>`
|
||||
// to `&NSObject`), but if the generic contained a lifetime, that
|
||||
// lifetime is still included in the returned reference.
|
||||
//
|
||||
// Note that you can easily have two different variables pointing to
|
||||
// the same object, `x: &T` and `y: &T::Target`, and this would be
|
||||
// perfectly safe!
|
||||
impl<$($t)*> $crate::__macro_helpers::Deref for $for {
|
||||
type Target = $superclass;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.__inner
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: Mutability does not change anything in the above
|
||||
// consideration, the lifetime of `&mut Self::Target` is still tied to
|
||||
// `&mut self`.
|
||||
//
|
||||
// Usually we don't want to allow `&mut` of immutable objects like
|
||||
// `NSString`, because their `NSCopying` implementation returns the
|
||||
// same object, and would violate aliasing rules.
|
||||
//
|
||||
// But `&mut NSMutableString` -> `&mut NSString` safe, since the
|
||||
// `NSCopying` implementation of `NSMutableString` is used, and that
|
||||
// is guaranteed to return a different object.
|
||||
impl<$($t)*> $crate::__macro_helpers::DerefMut for $for {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.__inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($t)*> $crate::__macro_helpers::AsRef<Self> for $for {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($t)*> $crate::__macro_helpers::AsMut<Self> for $for {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
$crate::__impl_as_ref_borrow! {
|
||||
impl ($($t)*) for $for; $superclass, $($inheritance_rest,)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __attribute_helper {
|
||||
// Convert a set of attributes described with `@[...]` to `#[...]`, while
|
||||
// parsing out the `sel(...)` attribute.
|
||||
{
|
||||
@strip_sel
|
||||
@[sel($($_sel_args:tt)*)]
|
||||
$(@[$($m_rest:tt)*])*
|
||||
|
||||
$(#[$($m:tt)*])*
|
||||
($($fn:tt)*)
|
||||
} => {
|
||||
$crate::__attribute_helper! {
|
||||
@strip_sel
|
||||
$(@[$($m_rest)*])*
|
||||
|
||||
$(#[$($m)*])*
|
||||
($($fn)*)
|
||||
}
|
||||
};
|
||||
{
|
||||
@strip_sel
|
||||
@[$($m_checked:tt)*]
|
||||
$(@[$($m_rest:tt)*])*
|
||||
|
||||
$(#[$($m:tt)*])*
|
||||
($($fn:tt)*)
|
||||
} => {
|
||||
$crate::__attribute_helper! {
|
||||
@strip_sel
|
||||
$(@[$($m_rest)*])*
|
||||
|
||||
$(#[$($m)*])*
|
||||
#[$($m_checked)*]
|
||||
($($fn)*)
|
||||
}
|
||||
};
|
||||
{
|
||||
@strip_sel
|
||||
$(#[$($m:tt)*])*
|
||||
($($fn:tt)*)
|
||||
} => {
|
||||
$(#[$($m)*])*
|
||||
$($fn)*
|
||||
};
|
||||
|
||||
// Extract the `#[sel(...)]` attribute and send it to another macro
|
||||
{
|
||||
@extract_sel
|
||||
($out_macro:path)
|
||||
(
|
||||
#[sel($($sel:tt)*)]
|
||||
$($rest:tt)*
|
||||
)
|
||||
$($macro_args:tt)*
|
||||
} => {{
|
||||
$crate::__attribute_helper! {
|
||||
@extract_sel_duplicate
|
||||
$($rest)*
|
||||
}
|
||||
|
||||
$out_macro!(
|
||||
$($macro_args)*
|
||||
@($($sel)*)
|
||||
)
|
||||
}};
|
||||
{
|
||||
@extract_sel
|
||||
($out_macro:path)
|
||||
(
|
||||
#[$($m_checked:tt)*]
|
||||
$($rest:tt)*
|
||||
)
|
||||
$($macro_args:tt)*
|
||||
} => {{
|
||||
$crate::__attribute_helper! {
|
||||
@extract_sel
|
||||
($out_macro)
|
||||
($($rest)*)
|
||||
$($macro_args)*
|
||||
}
|
||||
}};
|
||||
{
|
||||
@extract_sel
|
||||
($out_macro:path)
|
||||
()
|
||||
$($macro_args:tt)*
|
||||
} => {{
|
||||
compile_error!("Must specify the desired selector using `#[sel(...)]`");
|
||||
}};
|
||||
|
||||
{
|
||||
@extract_sel_duplicate
|
||||
#[sel($($_sel_args:tt)*)]
|
||||
$($rest:tt)*
|
||||
} => {{
|
||||
compile_error!("Cannot not specify a selector twice!");
|
||||
}};
|
||||
{
|
||||
@extract_sel_duplicate
|
||||
#[$($m_checked:tt)*]
|
||||
$($rest:tt)*
|
||||
} => {{
|
||||
$crate::__attribute_helper! {
|
||||
@extract_sel_duplicate
|
||||
$($rest)*
|
||||
}
|
||||
}};
|
||||
{@extract_sel_duplicate} => {};
|
||||
|
||||
}
|
||||
395
third-party/vendor/objc2/src/macros/extern_methods.rs
vendored
Normal file
395
third-party/vendor/objc2/src/macros/extern_methods.rs
vendored
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
/// Define methods on an external class.
|
||||
///
|
||||
/// This is a convenience macro to easily generate associated functions and
|
||||
/// methods that call [`msg_send!`][crate::msg_send] appropriately.
|
||||
///
|
||||
///
|
||||
/// # Specification
|
||||
///
|
||||
/// Within the `impl` block you can define two types of functions without
|
||||
/// bodies; ["associated functions"] and ["methods"]. These are then mapped to
|
||||
/// the Objective-C equivalents "class methods" and "instance methods", and an
|
||||
/// appropriate body is created for you. In particular, if you use `self` your
|
||||
/// method will assumbed to be an instance method, and if you don't it will be
|
||||
/// assumed to be a class method.
|
||||
///
|
||||
/// The desired selector can be specified using the `#[sel(my:selector:)]`
|
||||
/// attribute. The name of the function doesn't matter.
|
||||
///
|
||||
/// If you specify a function/method with a body, the macro will simply ignore
|
||||
/// it.
|
||||
///
|
||||
/// ["associated functions"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods
|
||||
/// ["methods"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// You must ensure that any methods you declare with the `#[sel(...)]`
|
||||
/// attribute upholds the safety guarantees decribed in the
|
||||
/// [`msg_send!`][crate::msg_send] macro, _or_ are marked `unsafe`.
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Let's create a quick interface to the [`NSCalendar`] class:
|
||||
///
|
||||
/// [`NSCalendar`]: https://developer.apple.com/documentation/foundation/nscalendar?language=objc
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::foundation::{NSObject, NSRange, NSString, NSUInteger};
|
||||
/// use objc2::rc::{Id, Shared};
|
||||
/// use objc2::{extern_class, extern_methods, msg_send_id, Encode, Encoding, ClassType};
|
||||
/// #
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// extern_class!(
|
||||
/// #[derive(PartialEq, Eq, Hash)]
|
||||
/// pub struct NSCalendar;
|
||||
///
|
||||
/// unsafe impl ClassType for NSCalendar {
|
||||
/// type Super = NSObject;
|
||||
/// }
|
||||
/// );
|
||||
///
|
||||
/// pub type NSCalendarIdentifier = NSString;
|
||||
///
|
||||
/// #[repr(usize)] // NSUInteger
|
||||
/// pub enum NSCalendarUnit {
|
||||
/// Hour = 32,
|
||||
/// Minute = 64,
|
||||
/// Second = 128,
|
||||
/// // TODO: More units
|
||||
/// }
|
||||
///
|
||||
/// unsafe impl Encode for NSCalendarUnit {
|
||||
/// const ENCODING: Encoding = usize::ENCODING;
|
||||
/// }
|
||||
///
|
||||
/// extern_methods!(
|
||||
/// /// Creation methods.
|
||||
/// // TODO: Support methods returning `Id`
|
||||
/// unsafe impl NSCalendar {
|
||||
/// pub fn current() -> Id<Self, Shared> {
|
||||
/// unsafe { msg_send_id![Self::class(), currentCalendar] }
|
||||
/// }
|
||||
///
|
||||
/// pub fn new(identifier: &NSCalendarIdentifier) -> Id<Self, Shared> {
|
||||
/// unsafe {
|
||||
/// msg_send_id![
|
||||
/// msg_send_id![Self::class(), alloc],
|
||||
/// initWithCalendarIdentifier: identifier,
|
||||
/// ]
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// /// Accessor methods.
|
||||
/// // SAFETY: `first_weekday` is correctly defined
|
||||
/// unsafe impl NSCalendar {
|
||||
/// #[sel(firstWeekday)]
|
||||
/// pub fn first_weekday(&self) -> NSUInteger;
|
||||
///
|
||||
/// pub fn am_symbol(&self) -> Id<NSString, Shared> {
|
||||
/// unsafe { msg_send_id![self, amSymbol] }
|
||||
/// }
|
||||
///
|
||||
/// #[sel(date:matchesComponents:)]
|
||||
/// // `unsafe` because we don't have definitions for `NSDate` and
|
||||
/// // `NSDateComponents` yet, so the user must ensure that is what's
|
||||
/// // passed.
|
||||
/// pub unsafe fn date_matches(&self, date: &NSObject, components: &NSObject) -> bool;
|
||||
///
|
||||
/// #[sel(maximumRangeOfUnit:)]
|
||||
/// pub fn max_range(&self, unit: NSCalendarUnit) -> NSRange;
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// The `extern_methods!` declaration then becomes:
|
||||
///
|
||||
/// ```
|
||||
/// # use objc2::foundation::{NSObject, NSRange, NSString, NSUInteger};
|
||||
/// # use objc2::rc::{Id, Shared};
|
||||
/// # use objc2::{extern_class, extern_methods, msg_send_id, Encode, Encoding, ClassType};
|
||||
/// #
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
/// #
|
||||
/// # extern_class!(
|
||||
/// # #[derive(PartialEq, Eq, Hash)]
|
||||
/// # pub struct NSCalendar;
|
||||
/// #
|
||||
/// # unsafe impl ClassType for NSCalendar {
|
||||
/// # type Super = NSObject;
|
||||
/// # }
|
||||
/// # );
|
||||
/// #
|
||||
/// # pub type NSCalendarIdentifier = NSString;
|
||||
/// #
|
||||
/// # #[repr(usize)] // NSUInteger
|
||||
/// # pub enum NSCalendarUnit {
|
||||
/// # Hour = 32,
|
||||
/// # Minute = 64,
|
||||
/// # Second = 128,
|
||||
/// # // TODO: More units
|
||||
/// # }
|
||||
/// #
|
||||
/// # unsafe impl Encode for NSCalendarUnit {
|
||||
/// # const ENCODING: Encoding = usize::ENCODING;
|
||||
/// # }
|
||||
/// #
|
||||
/// # use objc2::msg_send;
|
||||
/// /// Creation methods.
|
||||
/// impl NSCalendar {
|
||||
/// pub fn current() -> Id<Self, Shared> {
|
||||
/// unsafe { msg_send_id![Self::class(), currentCalendar] }
|
||||
/// }
|
||||
///
|
||||
/// pub fn new(identifier: &NSCalendarIdentifier) -> Id<Self, Shared> {
|
||||
/// unsafe {
|
||||
/// msg_send_id![
|
||||
/// msg_send_id![Self::class(), alloc],
|
||||
/// initWithCalendarIdentifier: identifier,
|
||||
/// ]
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// /// Accessor methods.
|
||||
/// impl NSCalendar {
|
||||
/// pub fn first_weekday(&self) -> NSUInteger {
|
||||
/// unsafe { msg_send![self, firstWeekday] }
|
||||
/// }
|
||||
///
|
||||
/// pub fn am_symbol(&self) -> Id<NSString, Shared> {
|
||||
/// unsafe { msg_send_id![self, amSymbol] }
|
||||
/// }
|
||||
///
|
||||
/// pub unsafe fn date_matches(&self, date: &NSObject, components: &NSObject) -> bool {
|
||||
/// unsafe { msg_send![self, date: date, matchesComponents: components] }
|
||||
/// }
|
||||
///
|
||||
/// pub fn max_range(&self, unit: NSCalendarUnit) -> NSRange {
|
||||
/// unsafe { msg_send![self, maximumRangeOfUnit: unit] }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! extern_methods {
|
||||
(
|
||||
$(
|
||||
$(#[$impl_m:meta])*
|
||||
unsafe impl<$($t:ident $(: $b:ident $(+ $rest:ident)*)?),*> $type:ty {
|
||||
$($methods:tt)*
|
||||
}
|
||||
)+
|
||||
) => {
|
||||
$(
|
||||
$(#[$impl_m])*
|
||||
impl<$($t $(: $b $(+ $rest)*)?),*> $type {
|
||||
$crate::__inner_extern_methods! {
|
||||
@rewrite_methods
|
||||
$($methods)*
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
(
|
||||
$(
|
||||
$(#[$impl_m:meta])*
|
||||
unsafe impl $type:ty {
|
||||
$($methods:tt)*
|
||||
}
|
||||
)+
|
||||
) => {
|
||||
$(
|
||||
$(#[$impl_m])*
|
||||
impl $type {
|
||||
$crate::__inner_extern_methods! {
|
||||
@rewrite_methods
|
||||
$($methods)*
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __inner_extern_methods {
|
||||
{@rewrite_methods} => {};
|
||||
{
|
||||
@rewrite_methods
|
||||
// Unsafe variant
|
||||
$(#[$($m:tt)*])*
|
||||
$v:vis unsafe fn $name:ident($($args:tt)*) $(-> $ret:ty)?;
|
||||
|
||||
$($rest:tt)*
|
||||
} => {
|
||||
// Detect instance vs. class method.
|
||||
$crate::__rewrite_self_arg! {
|
||||
($crate::__inner_extern_methods)
|
||||
($($args)*)
|
||||
@method_out
|
||||
@($(#[$($m)*])*)
|
||||
@($v unsafe fn $name($($args)*) $(-> $ret)?)
|
||||
// Will add @(kind)
|
||||
// Will add @(args_start)
|
||||
// Will add @(args_rest)
|
||||
}
|
||||
|
||||
$crate::__inner_extern_methods! {
|
||||
@rewrite_methods
|
||||
$($rest)*
|
||||
}
|
||||
};
|
||||
{
|
||||
@rewrite_methods
|
||||
// Safe variant
|
||||
$(#[$($m:tt)*])*
|
||||
$v:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?;
|
||||
|
||||
$($rest:tt)*
|
||||
} => {
|
||||
$crate::__rewrite_self_arg! {
|
||||
($crate::__inner_extern_methods)
|
||||
($($args)*)
|
||||
@method_out
|
||||
@($(#[$($m)*])*)
|
||||
@($v fn $name($($args)*) $(-> $ret)?)
|
||||
}
|
||||
|
||||
$crate::__inner_extern_methods! {
|
||||
@rewrite_methods
|
||||
$($rest)*
|
||||
}
|
||||
};
|
||||
{
|
||||
@rewrite_methods
|
||||
// Other items that people might want to put here (e.g. functions with
|
||||
// a body).
|
||||
$associated_item:item
|
||||
|
||||
$($rest:tt)*
|
||||
} => {
|
||||
$associated_item
|
||||
|
||||
$crate::__inner_extern_methods! {
|
||||
@rewrite_methods
|
||||
$($rest)*
|
||||
}
|
||||
};
|
||||
{
|
||||
@method_out
|
||||
@($(#[$($m:tt)*])*)
|
||||
@($($function_start:tt)*)
|
||||
@($($kind:tt)*)
|
||||
@($($args_start:tt)*)
|
||||
@($($args_rest:tt)*)
|
||||
} => {
|
||||
$crate::__attribute_helper! {
|
||||
@strip_sel
|
||||
$(@[$($m)*])*
|
||||
($($function_start)* {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
$crate::__attribute_helper! {
|
||||
@extract_sel
|
||||
($crate::__inner_extern_methods)
|
||||
($(#[$($m)*])*)
|
||||
@unsafe_method_body
|
||||
@($($kind)*)
|
||||
@($($args_start)*)
|
||||
@($($args_rest)*)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
@unsafe_method_body
|
||||
@(instance_method)
|
||||
@(
|
||||
$self:ident: $self_ty:ty,
|
||||
_: $sel_ty:ty,
|
||||
)
|
||||
@($($args_rest:tt)*)
|
||||
@($($sel:tt)*)
|
||||
} => {
|
||||
$crate::__collect_msg_send!(
|
||||
$crate::msg_send;
|
||||
$self;
|
||||
($($sel)*);
|
||||
($($args_rest)*);
|
||||
)
|
||||
};
|
||||
{
|
||||
@unsafe_method_body
|
||||
@(class_method)
|
||||
@(
|
||||
_: $cls_ty:ty,
|
||||
_: $sel_ty:ty,
|
||||
)
|
||||
@($($args_rest:tt)*)
|
||||
@($($sel:tt)*)
|
||||
} => {
|
||||
$crate::__collect_msg_send!(
|
||||
$crate::msg_send;
|
||||
Self::class();
|
||||
($($sel)*);
|
||||
($($args_rest)*);
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/// Zip selector and arguments, and forward to macro.
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __collect_msg_send {
|
||||
// Selector with no arguments
|
||||
(
|
||||
$macro:path;
|
||||
$obj:expr;
|
||||
($sel:ident);
|
||||
();
|
||||
) => {{
|
||||
$macro![$obj, $sel]
|
||||
}};
|
||||
|
||||
// Base case
|
||||
(
|
||||
$macro:path;
|
||||
$obj:expr;
|
||||
();
|
||||
();
|
||||
$($output:tt)+
|
||||
) => {{
|
||||
$macro![$obj, $($output)+]
|
||||
}};
|
||||
|
||||
// tt-munch each argument
|
||||
(
|
||||
$macro:path;
|
||||
$obj:expr;
|
||||
($sel:ident : $($sel_rest:tt)*);
|
||||
($arg:ident: $arg_ty:ty $(, $($args_rest:tt)*)?);
|
||||
$($output:tt)*
|
||||
) => {{
|
||||
$crate::__collect_msg_send!(
|
||||
$macro;
|
||||
$obj;
|
||||
($($sel_rest)*);
|
||||
($($($args_rest)*)?);
|
||||
$($output)*
|
||||
$sel: $arg,
|
||||
)
|
||||
}};
|
||||
|
||||
// If couldn't zip selector and arguments, show useful error message
|
||||
($($_any:tt)*) => {{
|
||||
compile_error!("Number of arguments in function and selector did not match!")
|
||||
}};
|
||||
}
|
||||
32
third-party/vendor/objc2/src/message/apple/arm.rs
vendored
Normal file
32
third-party/vendor/objc2/src/message/apple/arm.rs
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
use core::mem;
|
||||
|
||||
use super::MsgSendFn;
|
||||
use crate::ffi;
|
||||
use crate::runtime::Imp;
|
||||
use crate::{Encode, Encoding};
|
||||
|
||||
/// Double-word sized fundamental data types don't use stret, but any
|
||||
/// composite type larger than 4 bytes does.
|
||||
///
|
||||
/// <https://web.archive.org/web/20191016000656/http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042f/IHI0042F_aapcs.pdf>
|
||||
/// <https://developer.arm.com/documentation/ihi0042/latest>
|
||||
unsafe impl<T: Encode> MsgSendFn for T {
|
||||
const MSG_SEND: Imp = {
|
||||
if let Encoding::LongLong | Encoding::ULongLong | Encoding::Double = T::ENCODING {
|
||||
ffi::objc_msgSend
|
||||
} else if mem::size_of::<T>() <= 4 {
|
||||
ffi::objc_msgSend
|
||||
} else {
|
||||
ffi::objc_msgSend_stret
|
||||
}
|
||||
};
|
||||
const MSG_SEND_SUPER: Imp = {
|
||||
if let Encoding::LongLong | Encoding::ULongLong | Encoding::Double = T::ENCODING {
|
||||
ffi::objc_msgSendSuper
|
||||
} else if mem::size_of::<T>() <= 4 {
|
||||
ffi::objc_msgSendSuper
|
||||
} else {
|
||||
ffi::objc_msgSendSuper_stret
|
||||
}
|
||||
};
|
||||
}
|
||||
12
third-party/vendor/objc2/src/message/apple/arm64.rs
vendored
Normal file
12
third-party/vendor/objc2/src/message/apple/arm64.rs
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
use super::MsgSendFn;
|
||||
use crate::ffi;
|
||||
use crate::runtime::Imp;
|
||||
use crate::Encode;
|
||||
|
||||
/// `objc_msgSend_stret` is not even available in arm64.
|
||||
///
|
||||
/// <https://twitter.com/gparker/status/378079715824660480>
|
||||
unsafe impl<T: Encode> MsgSendFn for T {
|
||||
const MSG_SEND: Imp = ffi::objc_msgSend;
|
||||
const MSG_SEND_SUPER: Imp = ffi::objc_msgSendSuper;
|
||||
}
|
||||
61
third-party/vendor/objc2/src/message/apple/mod.rs
vendored
Normal file
61
third-party/vendor/objc2/src/message/apple/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
use super::conditional_try;
|
||||
use crate::encode::Encode;
|
||||
use crate::ffi;
|
||||
use crate::runtime::{Class, Imp, Object, Sel};
|
||||
use crate::MessageArguments;
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
#[path = "x86.rs"]
|
||||
mod arch;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[path = "x86_64.rs"]
|
||||
mod arch;
|
||||
#[cfg(target_arch = "arm")]
|
||||
#[path = "arm.rs"]
|
||||
mod arch;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[path = "arm64.rs"]
|
||||
mod arch;
|
||||
|
||||
/// On the above architectures we can statically find the correct method to
|
||||
/// call from the return type, by looking at its `Encode` implementation.
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
unsafe trait MsgSendFn: Encode {
|
||||
const MSG_SEND: Imp;
|
||||
const MSG_SEND_SUPER: Imp;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub(crate) unsafe fn send_unverified<A, R>(receiver: *mut Object, sel: Sel, args: A) -> R
|
||||
where
|
||||
A: MessageArguments,
|
||||
R: Encode,
|
||||
{
|
||||
let msg_send_fn = R::MSG_SEND;
|
||||
unsafe { conditional_try(|| A::__invoke(msg_send_fn, receiver, sel, args)) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub(crate) unsafe fn send_super_unverified<A, R>(
|
||||
receiver: *mut Object,
|
||||
superclass: &Class,
|
||||
sel: Sel,
|
||||
args: A,
|
||||
) -> R
|
||||
where
|
||||
A: MessageArguments,
|
||||
R: Encode,
|
||||
{
|
||||
let superclass: *const Class = superclass;
|
||||
let mut sup = ffi::objc_super {
|
||||
receiver: receiver.cast(),
|
||||
super_class: superclass.cast(),
|
||||
};
|
||||
let receiver: *mut ffi::objc_super = &mut sup;
|
||||
let receiver = receiver.cast();
|
||||
|
||||
let msg_send_fn = R::MSG_SEND_SUPER;
|
||||
unsafe { conditional_try(|| A::__invoke(msg_send_fn, receiver, sel, args)) }
|
||||
}
|
||||
31
third-party/vendor/objc2/src/message/apple/x86.rs
vendored
Normal file
31
third-party/vendor/objc2/src/message/apple/x86.rs
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
use core::mem;
|
||||
|
||||
use super::MsgSendFn;
|
||||
use crate::ffi;
|
||||
use crate::runtime::Imp;
|
||||
use crate::{Encode, Encoding};
|
||||
|
||||
/// Structures 1 or 2 bytes in size are placed in EAX.
|
||||
/// Structures 4 or 8 bytes in size are placed in: EAX and EDX.
|
||||
/// Structures of other sizes are placed at the address supplied by the caller.
|
||||
///
|
||||
/// <https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html>
|
||||
unsafe impl<T: Encode> MsgSendFn for T {
|
||||
const MSG_SEND: Imp = {
|
||||
// See https://github.com/apple-oss-distributions/objc4/blob/objc4-818.2/runtime/message.h#L156-L172
|
||||
if let Encoding::Float | Encoding::Double | Encoding::LongDouble = T::ENCODING {
|
||||
ffi::objc_msgSend_fpret
|
||||
} else if let 0 | 1 | 2 | 4 | 8 = mem::size_of::<T>() {
|
||||
ffi::objc_msgSend
|
||||
} else {
|
||||
ffi::objc_msgSend_stret
|
||||
}
|
||||
};
|
||||
const MSG_SEND_SUPER: Imp = {
|
||||
if let 0 | 1 | 2 | 4 | 8 = mem::size_of::<T>() {
|
||||
ffi::objc_msgSendSuper
|
||||
} else {
|
||||
ffi::objc_msgSendSuper_stret
|
||||
}
|
||||
};
|
||||
}
|
||||
33
third-party/vendor/objc2/src/message/apple/x86_64.rs
vendored
Normal file
33
third-party/vendor/objc2/src/message/apple/x86_64.rs
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
use core::mem;
|
||||
|
||||
use super::MsgSendFn;
|
||||
use crate::ffi;
|
||||
use crate::runtime::Imp;
|
||||
use crate::{Encode, Encoding};
|
||||
|
||||
/// If the size of an object is larger than two eightbytes, it has class
|
||||
/// MEMORY. If the type has class MEMORY, then the caller provides space for
|
||||
/// the return value and passes the address of this storage.
|
||||
///
|
||||
/// <https://www.uclibc.org/docs/psABI-x86_64.pdf>
|
||||
unsafe impl<T: Encode> MsgSendFn for T {
|
||||
const MSG_SEND: Imp = {
|
||||
// See https://github.com/apple-oss-distributions/objc4/blob/objc4-818.2/runtime/message.h#L156-L172
|
||||
if let Encoding::LongDouble = T::ENCODING {
|
||||
ffi::objc_msgSend_fpret
|
||||
} else if let Encoding::LongDoubleComplex = T::ENCODING {
|
||||
ffi::objc_msgSend_fp2ret
|
||||
} else if mem::size_of::<T>() <= 16 {
|
||||
ffi::objc_msgSend
|
||||
} else {
|
||||
ffi::objc_msgSend_stret
|
||||
}
|
||||
};
|
||||
const MSG_SEND_SUPER: Imp = {
|
||||
if mem::size_of::<T>() <= 16 {
|
||||
ffi::objc_msgSendSuper
|
||||
} else {
|
||||
ffi::objc_msgSendSuper_stret
|
||||
}
|
||||
};
|
||||
}
|
||||
69
third-party/vendor/objc2/src/message/gnustep.rs
vendored
Normal file
69
third-party/vendor/objc2/src/message/gnustep.rs
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
use core::hint;
|
||||
use core::mem;
|
||||
|
||||
use super::conditional_try;
|
||||
use crate::encode::Encode;
|
||||
use crate::ffi;
|
||||
use crate::runtime::{Class, Imp, Object, Sel};
|
||||
use crate::MessageArguments;
|
||||
|
||||
#[inline]
|
||||
fn unwrap_msg_send_fn(msg_send_fn: Option<Imp>) -> Imp {
|
||||
match msg_send_fn {
|
||||
Some(msg_send_fn) => msg_send_fn,
|
||||
None => {
|
||||
// SAFETY: This will never be NULL, even if the selector is not
|
||||
// found a callable function pointer will still be returned!
|
||||
//
|
||||
// `clang` doesn't insert a NULL check here either.
|
||||
unsafe { hint::unreachable_unchecked() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) unsafe fn send_unverified<A, R>(receiver: *mut Object, sel: Sel, args: A) -> R
|
||||
where
|
||||
A: MessageArguments,
|
||||
R: Encode,
|
||||
{
|
||||
// If `receiver` is NULL, objc_msg_lookup will return a standard C-method
|
||||
// taking two arguments, the receiver and the selector. Transmuting and
|
||||
// calling such a function with multiple parameters is UB, so instead we
|
||||
// just return NULL directly.
|
||||
if receiver.is_null() {
|
||||
// SAFETY: Caller guarantees that messages to NULL-receivers only
|
||||
// return pointers, and a mem::zeroed pointer is just a NULL-pointer.
|
||||
return unsafe { mem::zeroed() };
|
||||
}
|
||||
|
||||
let msg_send_fn = unsafe { ffi::objc_msg_lookup(receiver.cast(), sel.as_ptr()) };
|
||||
let msg_send_fn = unwrap_msg_send_fn(msg_send_fn);
|
||||
unsafe { conditional_try(|| A::__invoke(msg_send_fn, receiver, sel, args)) }
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) unsafe fn send_super_unverified<A, R>(
|
||||
receiver: *mut Object,
|
||||
superclass: &Class,
|
||||
sel: Sel,
|
||||
args: A,
|
||||
) -> R
|
||||
where
|
||||
A: MessageArguments,
|
||||
R: Encode,
|
||||
{
|
||||
if receiver.is_null() {
|
||||
// SAFETY: Same as in `send_unverified`.
|
||||
return unsafe { mem::zeroed() };
|
||||
}
|
||||
|
||||
let superclass: *const Class = superclass;
|
||||
let sup = ffi::objc_super {
|
||||
receiver: receiver.cast(),
|
||||
super_class: superclass.cast(),
|
||||
};
|
||||
let msg_send_fn = unsafe { ffi::objc_msg_lookup_super(&sup, sel.as_ptr()) };
|
||||
let msg_send_fn = unwrap_msg_send_fn(msg_send_fn);
|
||||
unsafe { conditional_try(|| A::__invoke(msg_send_fn, receiver, sel, args)) }
|
||||
}
|
||||
543
third-party/vendor/objc2/src/message/mod.rs
vendored
Normal file
543
third-party/vendor/objc2/src/message/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,543 @@
|
|||
use core::mem;
|
||||
use core::mem::ManuallyDrop;
|
||||
use core::ptr::NonNull;
|
||||
|
||||
use crate::encode::{Encode, EncodeArguments, EncodeConvert, RefEncode};
|
||||
use crate::rc::{Id, Owned, Ownership};
|
||||
use crate::runtime::{Class, Imp, Object, Sel};
|
||||
use crate::ClassType;
|
||||
|
||||
#[cfg(feature = "catch-all")]
|
||||
#[track_caller]
|
||||
unsafe fn conditional_try<R: EncodeConvert>(f: impl FnOnce() -> R) -> R {
|
||||
let f = core::panic::AssertUnwindSafe(f);
|
||||
match unsafe { crate::exception::catch(f) } {
|
||||
Ok(r) => r,
|
||||
Err(exception) => {
|
||||
if let Some(exception) = exception {
|
||||
panic!("uncaught {:?}", exception)
|
||||
} else {
|
||||
panic!("uncaught exception nil")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "catch-all"))]
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
unsafe fn conditional_try<R: EncodeConvert>(f: impl FnOnce() -> R) -> R {
|
||||
f()
|
||||
}
|
||||
|
||||
#[cfg(feature = "verify_message")]
|
||||
#[track_caller]
|
||||
fn panic_verify(cls: &Class, sel: Sel, err: crate::VerificationError) -> ! {
|
||||
panic!(
|
||||
"invalid message send to {}[{:?} {:?}]: {}",
|
||||
if cls.is_metaclass() { "+" } else { "-" },
|
||||
cls,
|
||||
sel,
|
||||
err
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(feature = "apple")]
|
||||
#[path = "apple/mod.rs"]
|
||||
mod platform;
|
||||
#[cfg(feature = "gnustep-1-7")]
|
||||
#[path = "gnustep.rs"]
|
||||
mod platform;
|
||||
|
||||
use self::platform::{send_super_unverified, send_unverified};
|
||||
|
||||
/// Types that can be sent Objective-C messages.
|
||||
///
|
||||
/// Implementing this provides [`MessageReceiver`] implementations for common
|
||||
/// pointer types and references to the type, which allows using them as the
|
||||
/// receiver (first argument) in the [`msg_send!`][`crate::msg_send`] macro.
|
||||
///
|
||||
/// This trait also allows the object to be used in [`rc::Id`][`Id`].
|
||||
///
|
||||
/// This is a subtrait of [`RefEncode`], meaning the type must also implement
|
||||
/// that, almost always as [`Encoding::Object`].
|
||||
///
|
||||
/// [`Encoding::Object`]: crate::Encoding::Object
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The type must represent an Objective-C object, meaning it:
|
||||
/// - Must be valid to reinterpret as [`runtime::Object`][`Object`].
|
||||
/// - Must be able to be the receiver of an Objective-C message sent with
|
||||
/// [`objc_msgSend`] or similar.
|
||||
/// - Must respond to the standard memory management `retain`, `release` and
|
||||
/// `autorelease` messages.
|
||||
///
|
||||
/// [`objc_msgSend`]: https://developer.apple.com/documentation/objectivec/1456712-objc_msgsend
|
||||
///
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::runtime::Object;
|
||||
/// use objc2::{Encoding, Message, RefEncode};
|
||||
///
|
||||
/// #[repr(C)]
|
||||
/// struct MyObject {
|
||||
/// // This has the exact same layout as `Object`
|
||||
/// inner: Object
|
||||
/// }
|
||||
///
|
||||
/// unsafe impl RefEncode for MyObject {
|
||||
/// const ENCODING_REF: Encoding = Encoding::Object;
|
||||
/// }
|
||||
///
|
||||
/// unsafe impl Message for MyObject {}
|
||||
///
|
||||
/// // `*mut MyObject` and other pointer/reference types to the object can
|
||||
/// // now be used in `msg_send!`
|
||||
/// //
|
||||
/// // And `Id<MyObject, O>` can now be constructed.
|
||||
/// ```
|
||||
pub unsafe trait Message: RefEncode {}
|
||||
|
||||
unsafe impl Message for Object {}
|
||||
|
||||
// TODO: Make this fully private
|
||||
pub(crate) mod private {
|
||||
use super::*;
|
||||
|
||||
pub trait Sealed {}
|
||||
|
||||
impl<T: Message + ?Sized> Sealed for *const T {}
|
||||
impl<T: Message + ?Sized> Sealed for *mut T {}
|
||||
impl<T: Message + ?Sized> Sealed for NonNull<T> {}
|
||||
|
||||
impl<'a, T: Message + ?Sized> Sealed for &'a T {}
|
||||
impl<'a, T: Message + ?Sized> Sealed for &'a mut T {}
|
||||
|
||||
impl<'a, T: Message + ?Sized, O: Ownership> Sealed for &'a Id<T, O> {}
|
||||
impl<'a, T: Message + ?Sized> Sealed for &'a mut Id<T, Owned> {}
|
||||
|
||||
impl<T: Message + ?Sized, O: Ownership> Sealed for ManuallyDrop<Id<T, O>> {}
|
||||
|
||||
impl Sealed for *const Class {}
|
||||
impl<'a> Sealed for &'a Class {}
|
||||
}
|
||||
|
||||
/// Types that can directly be used as the receiver of Objective-C messages.
|
||||
///
|
||||
/// Examples include objects, classes, and blocks.
|
||||
///
|
||||
/// This is a sealed trait (for now) that is automatically implemented for
|
||||
/// pointers to types implementing [`Message`], so that code can be generic
|
||||
/// over the message receiver.
|
||||
///
|
||||
/// This is mostly an implementation detail; you'll want to implement
|
||||
/// [`Message`] for your type instead.
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This is a sealed trait, and should not need to be implemented. Open an
|
||||
/// issue if you know a use-case where this restrition should be lifted!
|
||||
pub unsafe trait MessageReceiver: private::Sealed + Sized {
|
||||
#[doc(hidden)]
|
||||
type __Inner: ?Sized;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn __as_raw_receiver(self) -> *mut Object;
|
||||
|
||||
/// Sends a message to self with the given selector and arguments.
|
||||
///
|
||||
/// The correct version of `objc_msgSend` will be chosen based on the
|
||||
/// return type. For more information, see the section on "Sending
|
||||
/// Messages" in Apple's [documentation][runtime].
|
||||
///
|
||||
/// If the selector is known at compile-time, it is recommended to use the
|
||||
/// [`msg_send!`] macro rather than this method.
|
||||
///
|
||||
/// [runtime]: https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This shares the same safety requirements as [`msg_send!`].
|
||||
///
|
||||
/// The added invariant is that the selector must take the same number of
|
||||
/// arguments as is given.
|
||||
///
|
||||
/// [`msg_send!`]: crate::msg_send
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
unsafe fn send_message<A, R>(self, sel: Sel, args: A) -> R
|
||||
where
|
||||
A: MessageArguments,
|
||||
R: EncodeConvert,
|
||||
{
|
||||
let this = self.__as_raw_receiver();
|
||||
// TODO: Always enable this when `debug_assertions` are on.
|
||||
#[cfg(feature = "verify_message")]
|
||||
{
|
||||
// SAFETY: Caller ensures only valid or NULL pointers.
|
||||
let this = unsafe { this.as_ref() };
|
||||
let cls = if let Some(this) = this {
|
||||
this.class()
|
||||
} else {
|
||||
panic!("messsaging {:?} to nil", sel);
|
||||
};
|
||||
|
||||
if let Err(err) = cls.verify_sel::<A, R>(sel) {
|
||||
panic_verify(cls, sel, err);
|
||||
}
|
||||
}
|
||||
unsafe { EncodeConvert::__from_inner(send_unverified(this, sel, args)) }
|
||||
}
|
||||
|
||||
/// Sends a message to a specific superclass with the given selector and
|
||||
/// arguments.
|
||||
///
|
||||
/// The correct version of `objc_msgSend_super` will be chosen based on the
|
||||
/// return type. For more information, see the section on "Sending
|
||||
/// Messages" in Apple's [documentation][runtime].
|
||||
///
|
||||
/// If the selector is known at compile-time, it is recommended to use the
|
||||
/// [`msg_send!(super(...), ...)`] macro rather than this method.
|
||||
///
|
||||
/// [runtime]: https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This shares the same safety requirements as
|
||||
/// [`msg_send!(super(...), ...)`].
|
||||
///
|
||||
/// The added invariant is that the selector must take the same number of
|
||||
/// arguments as is given.
|
||||
///
|
||||
/// [`msg_send!(super(...), ...)`]: crate::msg_send
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
unsafe fn send_super_message<A, R>(self, superclass: &Class, sel: Sel, args: A) -> R
|
||||
where
|
||||
A: MessageArguments,
|
||||
R: EncodeConvert,
|
||||
{
|
||||
let this = self.__as_raw_receiver();
|
||||
#[cfg(feature = "verify_message")]
|
||||
{
|
||||
if this.is_null() {
|
||||
panic!("messsaging {:?} to nil", sel);
|
||||
}
|
||||
if let Err(err) = superclass.verify_sel::<A, R>(sel) {
|
||||
panic_verify(superclass, sel, err);
|
||||
}
|
||||
}
|
||||
unsafe { EncodeConvert::__from_inner(send_super_unverified(this, superclass, sel, args)) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
#[doc(hidden)]
|
||||
unsafe fn __send_super_message_static<A, R>(self, sel: Sel, args: A) -> R
|
||||
where
|
||||
Self::__Inner: ClassType,
|
||||
<Self::__Inner as ClassType>::Super: ClassType,
|
||||
A: MessageArguments,
|
||||
R: EncodeConvert,
|
||||
{
|
||||
unsafe { self.send_super_message(<Self::__Inner as ClassType>::Super::class(), sel, args) }
|
||||
}
|
||||
}
|
||||
|
||||
// Note that we implement MessageReceiver for unsized types as well, this is
|
||||
// to support `extern type`s in the future, not because we want to allow DSTs.
|
||||
|
||||
unsafe impl<T: Message + ?Sized> MessageReceiver for *const T {
|
||||
type __Inner = T;
|
||||
|
||||
#[inline]
|
||||
fn __as_raw_receiver(self) -> *mut Object {
|
||||
(self as *mut T).cast()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Message + ?Sized> MessageReceiver for *mut T {
|
||||
type __Inner = T;
|
||||
|
||||
#[inline]
|
||||
fn __as_raw_receiver(self) -> *mut Object {
|
||||
self.cast()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Message + ?Sized> MessageReceiver for NonNull<T> {
|
||||
type __Inner = T;
|
||||
|
||||
#[inline]
|
||||
fn __as_raw_receiver(self) -> *mut Object {
|
||||
self.as_ptr().cast()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, T: Message + ?Sized> MessageReceiver for &'a T {
|
||||
type __Inner = T;
|
||||
|
||||
#[inline]
|
||||
fn __as_raw_receiver(self) -> *mut Object {
|
||||
let ptr: *const T = self;
|
||||
(ptr as *mut T).cast()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, T: Message + ?Sized> MessageReceiver for &'a mut T {
|
||||
type __Inner = T;
|
||||
|
||||
#[inline]
|
||||
fn __as_raw_receiver(self) -> *mut Object {
|
||||
let ptr: *mut T = self;
|
||||
ptr.cast()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, T: Message + ?Sized, O: Ownership> MessageReceiver for &'a Id<T, O> {
|
||||
type __Inner = T;
|
||||
|
||||
#[inline]
|
||||
fn __as_raw_receiver(self) -> *mut Object {
|
||||
(Id::as_ptr(self) as *mut T).cast()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, T: Message + ?Sized> MessageReceiver for &'a mut Id<T, Owned> {
|
||||
type __Inner = T;
|
||||
|
||||
#[inline]
|
||||
fn __as_raw_receiver(self) -> *mut Object {
|
||||
Id::as_mut_ptr(self).cast()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Message + ?Sized, O: Ownership> MessageReceiver for ManuallyDrop<Id<T, O>> {
|
||||
type __Inner = T;
|
||||
|
||||
#[inline]
|
||||
fn __as_raw_receiver(self) -> *mut Object {
|
||||
Id::consume_as_ptr(self).cast()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl MessageReceiver for *const Class {
|
||||
type __Inner = Class;
|
||||
|
||||
#[inline]
|
||||
fn __as_raw_receiver(self) -> *mut Object {
|
||||
(self as *mut Class).cast()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> MessageReceiver for &'a Class {
|
||||
type __Inner = Class;
|
||||
|
||||
#[inline]
|
||||
fn __as_raw_receiver(self) -> *mut Object {
|
||||
let ptr: *const Class = self;
|
||||
(ptr as *mut Class).cast()
|
||||
}
|
||||
}
|
||||
|
||||
/// Types that may be used as the arguments of an Objective-C message.
|
||||
///
|
||||
/// This is implemented for tuples of up to 12 arguments, where each argument
|
||||
/// implements [`Encode`][crate::Encode] (or can be converted from one).
|
||||
///
|
||||
///
|
||||
/// # 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 MessageArguments: EncodeArguments {
|
||||
/// Invoke an [`Imp`] with the given object, selector, and arguments.
|
||||
///
|
||||
/// This method is the primitive used when sending messages and should not
|
||||
/// be called directly; instead, use the `msg_send!` macro or, in cases
|
||||
/// with a dynamic selector, the [`MessageReceiver::send_message`] method.
|
||||
#[doc(hidden)]
|
||||
unsafe fn __invoke<R: Encode>(imp: Imp, obj: *mut Object, sel: Sel, args: Self) -> R;
|
||||
}
|
||||
|
||||
macro_rules! message_args_impl {
|
||||
($($a:ident: $t:ident),*) => (
|
||||
unsafe impl<$($t: EncodeConvert),*> MessageArguments for ($($t,)*) {
|
||||
#[inline]
|
||||
unsafe fn __invoke<R: Encode>(imp: Imp, obj: *mut Object, sel: Sel, ($($a,)*): Self) -> R {
|
||||
// The imp must be cast to the appropriate function pointer
|
||||
// type before being called; the msgSend functions are not
|
||||
// parametric, but instead "trampolines" to the actual
|
||||
// method implementations.
|
||||
#[cfg(not(feature = "unstable-c-unwind"))]
|
||||
let imp: unsafe extern "C" fn(*mut Object, Sel $(, $t::__Inner)*) -> R = unsafe {
|
||||
mem::transmute(imp)
|
||||
};
|
||||
#[cfg(feature = "unstable-c-unwind")]
|
||||
let imp: unsafe extern "C-unwind" fn(*mut Object, Sel $(, $t::__Inner)*) -> R = unsafe {
|
||||
mem::transmute(imp)
|
||||
};
|
||||
// TODO: On x86_64 it would be more efficient to use a GOT
|
||||
// entry here (e.g. adding `nonlazybind` in LLVM).
|
||||
// Same can be said of e.g. `objc_retain` and `objc_release`.
|
||||
unsafe { imp(obj, sel $(, EncodeConvert::__into_inner($a))*) }
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
message_args_impl!();
|
||||
message_args_impl!(a: A);
|
||||
message_args_impl!(a: A, b: B);
|
||||
message_args_impl!(a: A, b: B, c: C);
|
||||
message_args_impl!(a: A, b: B, c: C, d: D);
|
||||
message_args_impl!(a: A, b: B, c: C, d: D, e: E);
|
||||
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F);
|
||||
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);
|
||||
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
|
||||
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
|
||||
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
|
||||
message_args_impl!(
|
||||
a: A,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D,
|
||||
e: E,
|
||||
f: F,
|
||||
g: G,
|
||||
h: H,
|
||||
i: I,
|
||||
j: J,
|
||||
k: K
|
||||
);
|
||||
message_args_impl!(
|
||||
a: A,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D,
|
||||
e: E,
|
||||
f: F,
|
||||
g: G,
|
||||
h: H,
|
||||
i: I,
|
||||
j: J,
|
||||
k: K,
|
||||
l: L
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::rc::{Id, Owned};
|
||||
use crate::test_utils;
|
||||
use crate::{msg_send, msg_send_id};
|
||||
|
||||
#[allow(unused)]
|
||||
fn test_different_receivers(mut obj: Id<Object, Owned>) {
|
||||
unsafe {
|
||||
let x = &mut obj;
|
||||
let _: () = msg_send![x, mutable1];
|
||||
// let _: () = msg_send![x, mutable2];
|
||||
let _: () = msg_send![&mut *obj, mutable1];
|
||||
let _: () = msg_send![&mut *obj, mutable2];
|
||||
#[allow(clippy::needless_borrow)]
|
||||
let obj: NonNull<Object> = (&mut *obj).into();
|
||||
let _: () = msg_send![obj, mutable1];
|
||||
let _: () = msg_send![obj, mutable2];
|
||||
let obj: *mut Object = obj.as_ptr();
|
||||
let _: () = msg_send![obj, mutable1];
|
||||
let _: () = msg_send![obj, mutable2];
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_send_message() {
|
||||
let mut obj = test_utils::custom_object();
|
||||
let result: u32 = unsafe {
|
||||
let _: () = msg_send![&mut obj, setFoo: 4u32];
|
||||
msg_send![&obj, foo]
|
||||
};
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_send_message_stret() {
|
||||
let obj = test_utils::custom_object();
|
||||
let result: test_utils::CustomStruct = unsafe { msg_send![&obj, customStruct] };
|
||||
let expected = test_utils::CustomStruct {
|
||||
a: 1,
|
||||
b: 2,
|
||||
c: 3,
|
||||
d: 4,
|
||||
};
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
feature = "verify_message",
|
||||
should_panic = "messsaging description to nil"
|
||||
)]
|
||||
fn test_send_message_nil() {
|
||||
use crate::rc::Shared;
|
||||
|
||||
let nil: *mut Object = ::core::ptr::null_mut();
|
||||
|
||||
// This result should not be relied on
|
||||
let result: Option<Id<Object, Shared>> = unsafe { msg_send_id![nil, description] };
|
||||
assert!(result.is_none());
|
||||
|
||||
// This result should not be relied on
|
||||
let result: usize = unsafe { msg_send![nil, hash] };
|
||||
assert_eq!(result, 0);
|
||||
|
||||
// This result should not be relied on
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
let result: f32 = 0.0;
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
let result: f32 = unsafe { msg_send![nil, floatValue] };
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
let result: f64 = unsafe { msg_send![nil, doubleValue] };
|
||||
assert_eq!(result, 0.0);
|
||||
|
||||
// This result should not be relied on
|
||||
let result: Option<Id<Object, Shared>> =
|
||||
unsafe { msg_send_id![nil, multiple: 1u32, arguments: 2i8] };
|
||||
assert!(result.is_none());
|
||||
|
||||
// This result should not be relied on
|
||||
let result: Option<Id<Object, Shared>> = unsafe { msg_send_id![None, init] };
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_send_message_super() {
|
||||
let mut obj = test_utils::custom_subclass_object();
|
||||
let superclass = test_utils::custom_class();
|
||||
unsafe {
|
||||
let _: () = msg_send![&mut obj, setFoo: 4u32];
|
||||
let foo: u32 = msg_send![super(&obj, superclass), foo];
|
||||
assert_eq!(foo, 4);
|
||||
|
||||
// The subclass is overriden to return foo + 2
|
||||
let foo: u32 = msg_send![&obj, foo];
|
||||
assert_eq!(foo, 6);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_send_message_manuallydrop() {
|
||||
let obj = ManuallyDrop::new(test_utils::custom_object());
|
||||
unsafe {
|
||||
let _: () = msg_send![obj, release];
|
||||
};
|
||||
// `obj` is consumed, can't use here
|
||||
}
|
||||
}
|
||||
15
third-party/vendor/objc2/src/rc/allocated.rs
vendored
Normal file
15
third-party/vendor/objc2/src/rc/allocated.rs
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/// A marker type that can be used within [`Id`] to indicate that the object
|
||||
/// has been allocated but not initialized.
|
||||
///
|
||||
/// The reason we use `Option<Id<Allocated<T>, O>>` instead of just `*mut T`
|
||||
/// is:
|
||||
/// - To allow releasing allocated objects, e.g. in the face of panics.
|
||||
/// - To safely know the object is valid (albeit uninitialized).
|
||||
/// - To allow specifying ownership.
|
||||
///
|
||||
/// [`Id`]: crate::rc::Id
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug)]
|
||||
pub struct Allocated<T: ?Sized>(T);
|
||||
|
||||
// Explicitly don't implement `Deref`, `Message` nor `RefEncode`!
|
||||
329
third-party/vendor/objc2/src/rc/autorelease.rs
vendored
Normal file
329
third-party/vendor/objc2/src/rc/autorelease.rs
vendored
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
use core::cell::UnsafeCell;
|
||||
use core::ffi::c_void;
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
#[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
|
||||
use std::{cell::RefCell, thread_local, vec::Vec};
|
||||
|
||||
use crate::ffi;
|
||||
|
||||
/// An Objective-C autorelease pool.
|
||||
///
|
||||
/// The pool is drained when dropped.
|
||||
///
|
||||
/// This is not [`Send`], since `objc_autoreleasePoolPop` must be called on
|
||||
/// the same thread.
|
||||
///
|
||||
/// And this is not [`Sync`], since you can only autorelease a reference to a
|
||||
/// pool on the current thread.
|
||||
#[derive(Debug)]
|
||||
pub struct AutoreleasePool {
|
||||
/// This is an opaque handle, and is not guaranteed to be neither a valid
|
||||
/// nor aligned pointer.
|
||||
context: *mut c_void,
|
||||
/// May point to data that is mutated (even though we hold shared access).
|
||||
p: PhantomData<*mut UnsafeCell<c_void>>,
|
||||
}
|
||||
|
||||
#[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
|
||||
thread_local! {
|
||||
/// We track the thread's pools to verify that object lifetimes are only
|
||||
/// taken from the innermost pool.
|
||||
static POOLS: RefCell<Vec<*mut c_void>> = RefCell::new(Vec::new());
|
||||
}
|
||||
|
||||
impl AutoreleasePool {
|
||||
/// Construct a new autorelease pool.
|
||||
///
|
||||
/// Use the [`autoreleasepool`] block for a safe alternative.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that when handing out `&'p AutoreleasePool` to
|
||||
/// functions that this is the innermost pool.
|
||||
///
|
||||
/// Additionally, the pools must be dropped in the same order they were
|
||||
/// created.
|
||||
#[doc(alias = "objc_autoreleasePoolPush")]
|
||||
#[inline]
|
||||
unsafe fn new() -> Self {
|
||||
// TODO: Make this function pub when we're more certain of the API
|
||||
let context = unsafe { ffi::objc_autoreleasePoolPush() };
|
||||
#[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
|
||||
POOLS.with(|c| c.borrow_mut().push(context));
|
||||
Self {
|
||||
context,
|
||||
p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// This will be removed in a future version.
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
pub fn __verify_is_inner(&self) {
|
||||
#[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
|
||||
POOLS.with(|c| {
|
||||
assert_eq!(
|
||||
c.borrow().last(),
|
||||
Some(&self.context),
|
||||
"Tried to use lifetime from pool that was not innermost"
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the given autoreleased pointer object.
|
||||
///
|
||||
/// This is the preferred way to make references from autoreleased
|
||||
/// objects, since it binds the lifetime of the reference to the pool, and
|
||||
/// does some extra checks when debug assertions are enabled.
|
||||
///
|
||||
/// For the mutable counterpart see [`ptr_as_mut`](#method.ptr_as_mut).
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This is equivalent to `&*ptr`, and shares the unsafety of that, except
|
||||
/// the lifetime is bound to the pool instead of being unbounded.
|
||||
#[inline]
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
pub unsafe fn ptr_as_ref<'p, T: ?Sized>(&'p self, ptr: *const T) -> &'p T {
|
||||
self.__verify_is_inner();
|
||||
// SAFETY: Checked by the caller
|
||||
unsafe { ptr.as_ref().unwrap_unchecked() }
|
||||
}
|
||||
|
||||
/// Returns a unique reference to the given autoreleased pointer object.
|
||||
///
|
||||
/// This is the preferred way to make mutable references from autoreleased
|
||||
/// objects, since it binds the lifetime of the reference to the pool, and
|
||||
/// does some extra checks when debug assertions are enabled.
|
||||
///
|
||||
/// For the shared counterpart see [`ptr_as_ref`](#method.ptr_as_ref).
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This is equivalent to `&mut *ptr`, and shares the unsafety of that,
|
||||
/// except the lifetime is bound to the pool instead of being unbounded.
|
||||
#[inline]
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub unsafe fn ptr_as_mut<'p, T: ?Sized>(&'p self, ptr: *mut T) -> &'p mut T {
|
||||
self.__verify_is_inner();
|
||||
// SAFETY: Checked by the caller
|
||||
unsafe { ptr.as_mut().unwrap_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for AutoreleasePool {
|
||||
/// Drains the autoreleasepool.
|
||||
///
|
||||
/// The [clang documentation] says that `@autoreleasepool` blocks are not
|
||||
/// drained when exceptions occur because:
|
||||
///
|
||||
/// > Not draining the pool during an unwind is apparently required by the
|
||||
/// > Objective-C exceptions implementation.
|
||||
///
|
||||
/// However, we would like to do this anyway whenever possible, since the
|
||||
/// unwind is probably caused by Rust, and forgetting to pop the pool will
|
||||
/// likely leak memory.
|
||||
///
|
||||
/// Fortunately, the above statement was true in the past, but since
|
||||
/// [revision `371`] of objc4 (ships with MacOS 10.5) the exception is now
|
||||
/// retained when `@throw` is encountered.
|
||||
///
|
||||
/// Hence it is safe to drain the pool when unwinding.
|
||||
///
|
||||
/// TODO: Verify this claim on 32bit!
|
||||
///
|
||||
/// [clang documentation]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#autoreleasepool
|
||||
/// [revision `371`]: https://github.com/apple-oss-distributions/objc4/blob/objc4-371/runtime/objc-exception.m#L479-L482
|
||||
#[doc(alias = "objc_autoreleasePoolPop")]
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::objc_autoreleasePoolPop(self.context) }
|
||||
#[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
|
||||
POOLS.with(|c| {
|
||||
assert_eq!(
|
||||
c.borrow_mut().pop(),
|
||||
Some(self.context),
|
||||
"Popped pool that was not the innermost pool"
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Pointer for AutoreleasePool {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Pointer::fmt(&self.context, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// We use a macro here so that the documentation is included whether the
|
||||
/// feature is enabled or not.
|
||||
#[cfg(not(feature = "unstable-autoreleasesafe"))]
|
||||
macro_rules! auto_trait {
|
||||
{$(#[$fn_meta:meta])* $v:vis unsafe trait AutoreleaseSafe {}} => {
|
||||
$(#[$fn_meta])*
|
||||
$v unsafe trait AutoreleaseSafe {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-autoreleasesafe")]
|
||||
macro_rules! auto_trait {
|
||||
{$(#[$fn_meta:meta])* $v:vis unsafe trait AutoreleaseSafe {}} => {
|
||||
$(#[$fn_meta])*
|
||||
$v unsafe auto trait AutoreleaseSafe {}
|
||||
}
|
||||
}
|
||||
|
||||
auto_trait! {
|
||||
/// Marks types that are safe to pass across the closure in an
|
||||
/// [`autoreleasepool`].
|
||||
///
|
||||
/// With the `unstable-autoreleasesafe` feature enabled, this is an auto
|
||||
/// trait that is implemented for all types except [`AutoreleasePool`].
|
||||
///
|
||||
/// Otherwise it is just a dummy trait that is implemented for all types;
|
||||
/// the safety invariants are checked with debug assertions instead.
|
||||
///
|
||||
/// You should not normally need to implement this trait yourself.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Must not be implemented for types that interract with the autorelease
|
||||
/// pool. So if you reimplement the [`AutoreleasePool`] struct or
|
||||
/// likewise, this should be negatively implemented for that.
|
||||
///
|
||||
/// This can easily be accomplished with an `PhantomData<AutoreleasePool>`
|
||||
/// if the `unstable-autoreleasesafe` feature is enabled.
|
||||
pub unsafe trait AutoreleaseSafe {}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unstable-autoreleasesafe"))]
|
||||
unsafe impl<T: ?Sized> AutoreleaseSafe for T {}
|
||||
|
||||
#[cfg(feature = "unstable-autoreleasesafe")]
|
||||
impl !AutoreleaseSafe for AutoreleasePool {}
|
||||
|
||||
/// Execute `f` in the context of a new autorelease pool. The pool is
|
||||
/// drained after the execution of `f` completes.
|
||||
///
|
||||
/// This corresponds to `@autoreleasepool` blocks in Objective-C and
|
||||
/// Swift.
|
||||
///
|
||||
/// The pool is passed as a reference to the enclosing function to give it
|
||||
/// a lifetime parameter that autoreleased objects can refer to.
|
||||
///
|
||||
/// The given reference must not be used in an inner `autoreleasepool`,
|
||||
/// doing so will panic with debug assertions enabled, and be a compile
|
||||
/// error in a future release. You can test the compile error with the
|
||||
/// `unstable-autoreleasesafe` crate feature on nightly Rust.
|
||||
///
|
||||
/// Note that this is mostly useful for preventing leaks (as any Objective-C
|
||||
/// method may leak internally). If implementing an interface to an object,
|
||||
/// you should try to return retained pointers with [`msg_send_id!`] wherever
|
||||
/// you can instead, since having to use this function can be quite cumbersome
|
||||
/// for your users!
|
||||
///
|
||||
/// [`msg_send_id!`]: crate::msg_send_id
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use core::mem::ManuallyDrop;
|
||||
/// use objc2::{class, msg_send, msg_send_id};
|
||||
/// use objc2::rc::{autoreleasepool, AutoreleasePool, Id, Owned};
|
||||
/// use objc2::runtime::Object;
|
||||
///
|
||||
/// fn needs_lifetime_from_pool<'p>(pool: &'p AutoreleasePool) -> &'p mut Object {
|
||||
/// let obj: Id<Object, Owned> = unsafe { msg_send_id![class!(NSObject), new] };
|
||||
/// let obj = ManuallyDrop::new(obj);
|
||||
/// let obj: *mut Object = unsafe { msg_send![obj, autorelease] };
|
||||
/// // Lifetime of the returned reference is bounded by the pool
|
||||
/// unsafe { pool.ptr_as_mut(obj) }
|
||||
///
|
||||
/// // Or simply
|
||||
/// // let obj: Id<Object, Owned> = unsafe { msg_send_id![class!(NSObject), new] };
|
||||
/// // obj.autorelease(pool)
|
||||
/// }
|
||||
///
|
||||
/// autoreleasepool(|pool| {
|
||||
/// // Create `obj` and autorelease it to the pool
|
||||
/// let obj = needs_lifetime_from_pool(pool);
|
||||
/// // ... use `obj` here
|
||||
/// // `obj` is deallocated when the pool ends
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// Fails to compile because `obj` does not live long enough for us to
|
||||
/// safely take it out of the pool:
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// # use objc2::{class, msg_send_id};
|
||||
/// # use objc2::rc::{autoreleasepool, AutoreleasePool, Id, Owned};
|
||||
/// # use objc2::runtime::Object;
|
||||
/// #
|
||||
/// # fn needs_lifetime_from_pool<'p>(pool: &'p AutoreleasePool) -> &'p mut Object {
|
||||
/// # let obj: Id<Object, Owned> = unsafe { msg_send_id![class!(NSObject), new] };
|
||||
/// # obj.autorelease(pool)
|
||||
/// # }
|
||||
/// #
|
||||
/// let obj = autoreleasepool(|pool| {
|
||||
/// let obj = needs_lifetime_from_pool(pool);
|
||||
/// // Use `obj`
|
||||
/// obj
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// Incorrect usage which panics (with debug assertions enabled) because we
|
||||
/// tried to pass an outer pool to an inner pool:
|
||||
///
|
||||
#[cfg_attr(feature = "unstable-autoreleasesafe", doc = "```compile_fail")]
|
||||
#[cfg_attr(not(feature = "unstable-autoreleasesafe"), doc = "```should_panic")]
|
||||
/// # use objc2::{class, msg_send_id};
|
||||
/// # use objc2::rc::{autoreleasepool, AutoreleasePool, Id, Owned};
|
||||
/// # use objc2::runtime::Object;
|
||||
/// #
|
||||
/// # fn needs_lifetime_from_pool<'p>(pool: &'p AutoreleasePool) -> &'p mut Object {
|
||||
/// # let obj: Id<Object, Owned> = unsafe { msg_send_id![class!(NSObject), new] };
|
||||
/// # obj.autorelease(pool)
|
||||
/// # }
|
||||
/// #
|
||||
/// autoreleasepool(|outer_pool| {
|
||||
/// let obj = autoreleasepool(|inner_pool| {
|
||||
/// let obj = needs_lifetime_from_pool(outer_pool);
|
||||
/// obj
|
||||
/// });
|
||||
/// // `obj` could wrongly be used here because its lifetime was
|
||||
/// // assigned to the outer pool, even though it was released by the
|
||||
/// // inner pool already.
|
||||
/// });
|
||||
/// #
|
||||
/// # panic!("Does not panic in release mode, so for testing we make it!");
|
||||
/// ```
|
||||
#[doc(alias = "@autoreleasepool")]
|
||||
#[inline]
|
||||
pub fn autoreleasepool<T, F>(f: F) -> T
|
||||
where
|
||||
for<'p> F: FnOnce(&'p AutoreleasePool) -> T + AutoreleaseSafe,
|
||||
{
|
||||
let pool = unsafe { AutoreleasePool::new() };
|
||||
f(&pool)
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "unstable-autoreleasesafe"))]
|
||||
mod tests {
|
||||
use super::AutoreleaseSafe;
|
||||
use crate::runtime::Object;
|
||||
|
||||
fn requires_autoreleasesafe<T: AutoreleaseSafe>() {}
|
||||
|
||||
#[test]
|
||||
fn test_autoreleasesafe() {
|
||||
requires_autoreleasesafe::<usize>();
|
||||
requires_autoreleasesafe::<*mut Object>();
|
||||
requires_autoreleasesafe::<&mut Object>();
|
||||
}
|
||||
}
|
||||
879
third-party/vendor/objc2/src/rc/id.rs
vendored
Normal file
879
third-party/vendor/objc2/src/rc/id.rs
vendored
Normal file
|
|
@ -0,0 +1,879 @@
|
|||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::{self, ManuallyDrop};
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use core::ptr::NonNull;
|
||||
|
||||
use super::Allocated;
|
||||
use super::AutoreleasePool;
|
||||
use super::{Owned, Ownership, Shared};
|
||||
use crate::ffi;
|
||||
use crate::{ClassType, Message};
|
||||
|
||||
/// An pointer for Objective-C reference counted objects.
|
||||
///
|
||||
/// [`Id`] strongly references or "retains" the given object `T`, and
|
||||
/// "releases" it again when dropped, thereby ensuring it will be deallocated
|
||||
/// at the right time.
|
||||
///
|
||||
/// An [`Id`] can either be [`Owned`] or [`Shared`], represented with the `O`
|
||||
/// type parameter.
|
||||
///
|
||||
/// If owned, it is guaranteed that there are no other references to the
|
||||
/// object, and the [`Id`] can therefore be mutably dereferenced.
|
||||
///
|
||||
/// If shared, however, it can only be immutably dereferenced because there
|
||||
/// may be other references to the object, since a shared [`Id`] can be cloned
|
||||
/// to provide exactly that.
|
||||
///
|
||||
/// An [`Id<T, Owned>`] can be safely converted to a [`Id<T, Shared>`] using
|
||||
/// [`Id::into_shared`] or `From`/`Into`. The opposite is not safely possible,
|
||||
/// but the unsafe option [`Id::from_shared`] is provided.
|
||||
///
|
||||
/// `Option<Id<T, O>>` is guaranteed to have the same size as a pointer to the
|
||||
/// object.
|
||||
///
|
||||
///
|
||||
/// # Comparison to `std` types
|
||||
///
|
||||
/// `Id<T, Owned>` can be thought of as the Objective-C equivalent of [`Box`]
|
||||
/// from the standard library: It is a unique pointer to some allocated
|
||||
/// object, and that means you're allowed to get a mutable reference to it.
|
||||
///
|
||||
/// Likewise, `Id<T, Shared>` is the Objective-C equivalent of [`Arc`]: It is
|
||||
/// a reference-counting pointer that, when cloned, increases the reference
|
||||
/// count.
|
||||
///
|
||||
/// [`Box`]: alloc::boxed::Box
|
||||
/// [`Arc`]: alloc::sync::Arc
|
||||
///
|
||||
/// # Caveats
|
||||
///
|
||||
/// If the inner type implements [`Drop`], that implementation will not be
|
||||
/// called, since there is no way to ensure that the Objective-C runtime will
|
||||
/// do so. If you need to run some code when the object is destroyed,
|
||||
/// implement the `dealloc` method instead.
|
||||
///
|
||||
/// This allows `?Sized` types `T`, but the intention is to only support when
|
||||
/// `T` is an `extern type` (yet unstable).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use objc2::msg_send_id;
|
||||
/// use objc2::runtime::{Class, Object};
|
||||
/// use objc2::rc::{Id, Owned, Shared, WeakId};
|
||||
///
|
||||
/// let cls = Class::get("NSObject").unwrap();
|
||||
/// let obj: Id<Object, Owned> = unsafe { msg_send_id![cls, new] };
|
||||
/// // obj will be released when it goes out of scope
|
||||
///
|
||||
/// // share the object so we can clone it
|
||||
/// let obj: Id<_, Shared> = obj.into();
|
||||
/// let another_ref = obj.clone();
|
||||
/// // dropping our other reference will decrement the retain count
|
||||
/// drop(another_ref);
|
||||
///
|
||||
/// let weak = WeakId::new(&obj);
|
||||
/// assert!(weak.load().is_some());
|
||||
/// // After the object is deallocated, our weak pointer returns none
|
||||
/// drop(obj);
|
||||
/// assert!(weak.load().is_none());
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use objc2::{class, msg_send_id};
|
||||
/// # use objc2::runtime::Object;
|
||||
/// # use objc2::rc::{Id, Owned, Shared};
|
||||
/// # type T = Object;
|
||||
/// let mut owned: Id<T, Owned>;
|
||||
/// # owned = unsafe { msg_send_id![class!(NSObject), new] };
|
||||
/// let mut_ref: &mut T = &mut *owned;
|
||||
/// // Do something with `&mut T` here
|
||||
///
|
||||
/// let shared: Id<T, Shared> = owned.into();
|
||||
/// let cloned: Id<T, Shared> = shared.clone();
|
||||
/// // Do something with `&T` here
|
||||
/// ```
|
||||
#[repr(transparent)]
|
||||
// TODO: Figure out if `Message` bound on `T` would be better here?
|
||||
// TODO: Add `ptr::Thin` bound on `T` to allow for only extern types
|
||||
// TODO: Consider changing the name of Id -> Retain
|
||||
pub struct Id<T: ?Sized, O: Ownership> {
|
||||
/// A pointer to the contained object. The pointer is always retained.
|
||||
///
|
||||
/// It is important that this is `NonNull`, since we want to dereference
|
||||
/// it later, and be able to use the null-pointer optimization.
|
||||
///
|
||||
/// Additionally, covariance is correct because we're either the unique
|
||||
/// owner of `T` (O = Owned), or `T` is immutable (O = Shared).
|
||||
ptr: NonNull<T>,
|
||||
/// Necessary for dropck even though we never actually run T's destructor,
|
||||
/// because it might have a `dealloc` that assumes that contained
|
||||
/// references outlive the type.
|
||||
///
|
||||
/// See <https://doc.rust-lang.org/nightly/nomicon/phantom-data.html>
|
||||
item: PhantomData<T>,
|
||||
/// To prevent warnings about unused type parameters.
|
||||
own: PhantomData<O>,
|
||||
/// Marks the type as !UnwindSafe. Later on we'll re-enable this.
|
||||
///
|
||||
/// See <https://github.com/rust-lang/rust/issues/93367> for why this is
|
||||
/// required.
|
||||
notunwindsafe: PhantomData<&'static mut ()>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized, O: Ownership> Id<T, O> {
|
||||
#[inline]
|
||||
unsafe fn new_nonnull(ptr: NonNull<T>) -> Self {
|
||||
Self {
|
||||
ptr,
|
||||
item: PhantomData,
|
||||
own: PhantomData,
|
||||
notunwindsafe: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message + ?Sized, O: Ownership> Id<Allocated<T>, O> {
|
||||
#[inline]
|
||||
pub(crate) unsafe fn new_allocated(ptr: *mut T) -> Option<Self> {
|
||||
// SAFETY: Upheld by the caller
|
||||
NonNull::new(ptr as *mut Allocated<T>).map(|ptr| unsafe { Self::new_nonnull(ptr) })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) unsafe fn assume_init(this: Self) -> Id<T, O> {
|
||||
let ptr = ManuallyDrop::new(this).ptr;
|
||||
|
||||
// NonNull::cast
|
||||
let ptr = ptr.as_ptr() as *mut T;
|
||||
let ptr = unsafe { NonNull::new_unchecked(ptr) };
|
||||
|
||||
// SAFETY: The pointer is valid.
|
||||
// Caller verifies that the object is allocated.
|
||||
unsafe { Id::new_nonnull(ptr) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message + ?Sized, O: Ownership> Id<T, O> {
|
||||
/// Constructs an [`Id`] to an object that already has +1 retain count.
|
||||
///
|
||||
/// This is useful when you have a retain count that has been handed off
|
||||
/// from somewhere else, usually Objective-C methods like `init`, `alloc`,
|
||||
/// `new`, `copy`, or methods with the `ns_returns_retained` attribute.
|
||||
///
|
||||
/// Since most of the above methods create new objects, and you therefore
|
||||
/// hold unique access to the object, you would often set the ownership to
|
||||
/// be [`Owned`].
|
||||
///
|
||||
/// But some immutable objects (like `NSString`) don't always return
|
||||
/// unique references, so in those case you would use [`Shared`].
|
||||
///
|
||||
/// Returns `None` if the pointer was null.
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure the given object has +1 retain count, and that
|
||||
/// the object pointer otherwise follows the same safety requirements as
|
||||
/// in [`Id::retain`].
|
||||
///
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use objc2::{class, msg_send, msg_send_id};
|
||||
/// # use objc2::runtime::{Class, Object};
|
||||
/// # use objc2::rc::{Id, Owned};
|
||||
/// let cls: &Class;
|
||||
/// # let cls = class!(NSObject);
|
||||
/// let obj: &mut Object = unsafe { msg_send![cls, alloc] };
|
||||
/// let obj: Id<Object, Owned> = unsafe { Id::new(msg_send![obj, init]).unwrap() };
|
||||
/// // Or utilizing `msg_send_id`:
|
||||
/// let obj = unsafe { msg_send_id![cls, alloc] };
|
||||
/// let obj: Id<Object, Owned> = unsafe { msg_send_id![obj, init] };
|
||||
/// // Or in this case simply just:
|
||||
/// let obj: Id<Object, Owned> = unsafe { msg_send_id![cls, new] };
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use objc2::{class, msg_send_id};
|
||||
/// # use objc2::runtime::Object;
|
||||
/// # use objc2::rc::{Id, Shared};
|
||||
/// # type NSString = Object;
|
||||
/// let cls = class!(NSString);
|
||||
/// // NSString is immutable, so don't create an owned reference to it
|
||||
/// let obj: Id<NSString, Shared> = unsafe { msg_send_id![cls, new] };
|
||||
/// ```
|
||||
#[inline]
|
||||
// Note: We don't take a reference as a parameter since it would be too
|
||||
// easy to accidentally create two aliasing mutable references.
|
||||
pub unsafe fn new(ptr: *mut T) -> Option<Id<T, O>> {
|
||||
// Should optimize down to nothing.
|
||||
// SAFETY: Upheld by the caller
|
||||
NonNull::new(ptr).map(|ptr| unsafe { Id::new_nonnull(ptr) })
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the object.
|
||||
///
|
||||
/// The pointer is valid for at least as long as the `Id` is held.
|
||||
///
|
||||
/// See [`Id::as_mut_ptr`] for the mutable equivalent.
|
||||
///
|
||||
/// This is an associated method, and must be called as `Id::as_ptr(obj)`.
|
||||
#[inline]
|
||||
pub fn as_ptr(this: &Id<T, O>) -> *const T {
|
||||
this.ptr.as_ptr()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn consume_as_ptr(this: ManuallyDrop<Self>) -> *mut T {
|
||||
this.ptr.as_ptr()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn option_into_ptr(obj: Option<Self>) -> *mut T {
|
||||
// Difficult to write this in an ergonomic way with ?Sized
|
||||
// So we just hack it with transmute!
|
||||
|
||||
// SAFETY: Option<Id<T, _>> has the same size as *mut T
|
||||
unsafe { mem::transmute::<ManuallyDrop<Option<Self>>, *mut T>(ManuallyDrop::new(obj)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message + ?Sized> Id<T, Owned> {
|
||||
/// Returns a raw mutable pointer to the object.
|
||||
///
|
||||
/// The pointer is valid for at least as long as the `Id` is held.
|
||||
///
|
||||
/// See [`Id::as_ptr`] for the immutable equivalent.
|
||||
///
|
||||
/// This is an associated method, and must be called as
|
||||
/// `Id::as_mut_ptr(obj)`.
|
||||
#[inline]
|
||||
pub fn as_mut_ptr(this: &mut Id<T, Owned>) -> *mut T {
|
||||
this.ptr.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add ?Sized bound
|
||||
impl<T: Message, O: Ownership> Id<T, O> {
|
||||
/// Convert the type of the given object to another.
|
||||
///
|
||||
/// This is equivalent to a `cast` between two pointers.
|
||||
///
|
||||
/// See [`Id::into_super`] for a safe alternative.
|
||||
///
|
||||
/// This is common to do when you know that an object is a subclass of
|
||||
/// a specific class (e.g. casting an instance of `NSString` to `NSObject`
|
||||
/// is safe because `NSString` is a subclass of `NSObject`).
|
||||
///
|
||||
/// All `'static` objects can safely be cast to [`Object`], since that
|
||||
/// assumes no specific class.
|
||||
///
|
||||
/// [`Object`]: crate::runtime::Object
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// You must ensure that the object can be reinterpreted as the given
|
||||
/// type.
|
||||
///
|
||||
/// If `T` is not `'static`, you must ensure that `U` ensures that the
|
||||
/// data contained by `T` is kept alive for as long as `U` lives.
|
||||
///
|
||||
/// Additionally, you must ensure that any safety invariants that the new
|
||||
/// type has are upheld.
|
||||
#[inline]
|
||||
pub unsafe fn cast<U: Message>(this: Self) -> Id<U, O> {
|
||||
let ptr = ManuallyDrop::new(this).ptr.cast();
|
||||
// SAFETY: The object is forgotten, so we have +1 retain count.
|
||||
//
|
||||
// Caller verifies that the returned object is of the correct type.
|
||||
unsafe { Id::new_nonnull(ptr) }
|
||||
}
|
||||
|
||||
/// Retains the given object pointer.
|
||||
///
|
||||
/// This is useful when you have been given a pointer to an object from
|
||||
/// some API, and you would like to ensure that the object stays around
|
||||
/// so that you can work with it.
|
||||
///
|
||||
/// If said API is a normal Objective-C method, you probably want to use
|
||||
/// [`Id::retain_autoreleased`] instead.
|
||||
///
|
||||
/// This is rarely used to construct owned [`Id`]s, see [`Id::new`] for
|
||||
/// that.
|
||||
///
|
||||
/// Returns `None` if the pointer was null.
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that the ownership is correct; that is, there
|
||||
/// must be no [`Owned`] pointers or mutable references to the same
|
||||
/// object, and when creating owned [`Id`]s, there must be no other
|
||||
/// pointers or references to the object.
|
||||
///
|
||||
/// Additionally, the pointer must be valid as a reference (aligned,
|
||||
/// dereferencable and initialized, see the [`std::ptr`] module for more
|
||||
/// information).
|
||||
///
|
||||
/// Finally, if you do not know the concrete type of `T`, it may not be
|
||||
/// `'static`, and hence you must ensure that the data that `T` references
|
||||
/// lives for as long as `T`.
|
||||
///
|
||||
/// [`std::ptr`]: core::ptr
|
||||
//
|
||||
// This would be illegal:
|
||||
// ```no_run
|
||||
// let owned: Id<T, Owned>;
|
||||
// // Lifetime information is discarded
|
||||
// let retained: Id<T, Shared> = unsafe { Id::retain(&*owned) };
|
||||
// // Which means we can still mutate `Owned`:
|
||||
// let x: &mut T = &mut *owned;
|
||||
// // While we have an immutable reference
|
||||
// let y: &T = &*retained;
|
||||
// ```
|
||||
#[doc(alias = "objc_retain")]
|
||||
#[inline]
|
||||
pub unsafe fn retain(ptr: *mut T) -> Option<Id<T, O>> {
|
||||
// SAFETY: The caller upholds that the pointer is valid
|
||||
let res: *mut T = unsafe { ffi::objc_retain(ptr.cast()) }.cast();
|
||||
debug_assert_eq!(res, ptr, "objc_retain did not return the same pointer");
|
||||
// SAFETY: We just retained the object, so it has +1 retain count
|
||||
unsafe { Self::new(res) }
|
||||
}
|
||||
|
||||
/// Retains a previously autoreleased object pointer.
|
||||
///
|
||||
/// This is useful when calling Objective-C methods that return
|
||||
/// autoreleased objects, see [Cocoa's Memory Management Policy][mmRules].
|
||||
///
|
||||
/// This has exactly the same semantics as [`Id::retain`], except it can
|
||||
/// sometimes avoid putting the object into the autorelease pool, possibly
|
||||
/// yielding increased speed and reducing memory pressure.
|
||||
///
|
||||
/// Note: This relies heavily on being inlined right after [`msg_send!`],
|
||||
/// be careful not accidentally require instructions between these.
|
||||
///
|
||||
/// [mmRules]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html
|
||||
/// [`msg_send!`]: crate::msg_send
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Same as [`Id::retain`].
|
||||
#[doc(alias = "objc_retainAutoreleasedReturnValue")]
|
||||
#[inline]
|
||||
pub unsafe fn retain_autoreleased(ptr: *mut T) -> Option<Id<T, O>> {
|
||||
// Add magic nop instruction to participate in the fast autorelease
|
||||
// scheme.
|
||||
//
|
||||
// See `callerAcceptsOptimizedReturn` in `objc-object.h`:
|
||||
// https://github.com/apple-oss-distributions/objc4/blob/objc4-838/runtime/objc-object.h#L1209-L1377
|
||||
//
|
||||
// We will unconditionally emit these instructions, even if they end
|
||||
// up being unused (for example because we're unlucky with inlining,
|
||||
// some other work is done between the objc_msgSend and this, or the
|
||||
// runtime version is too old to support it).
|
||||
//
|
||||
// It may seem like there should be a better way to do this, but
|
||||
// emitting raw assembly is exactly what Clang and Swift does:
|
||||
// swiftc: https://github.com/apple/swift/blob/swift-5.5.3-RELEASE/lib/IRGen/GenObjC.cpp#L148-L173
|
||||
// Clang: https://github.com/llvm/llvm-project/blob/889317d47b7f046cf0e68746da8f7f264582fb5b/clang/lib/CodeGen/CGObjC.cpp#L2339-L2373
|
||||
//
|
||||
// Resources:
|
||||
// - https://www.mikeash.com/pyblog/friday-qa-2011-09-30-automatic-reference-counting.html
|
||||
// - https://www.galloway.me.uk/2012/02/how-does-objc_retainautoreleasedreturnvalue-work/
|
||||
// - https://github.com/gfx-rs/metal-rs/issues/222
|
||||
// - https://news.ycombinator.com/item?id=29311736
|
||||
// - https://stackoverflow.com/a/23765612
|
||||
//
|
||||
// SAFETY:
|
||||
// Based on https://doc.rust-lang.org/stable/reference/inline-assembly.html#rules-for-inline-assembly
|
||||
//
|
||||
// We don't care about the value of the register (so it's okay to be
|
||||
// undefined), and its value is preserved.
|
||||
//
|
||||
// nomem: No reads or writes to memory are performed (this `mov`
|
||||
// operates entirely on registers).
|
||||
// preserves_flags: `mov` doesn't modify any flags.
|
||||
// nostack: We don't touch the stack.
|
||||
|
||||
// Only worth doing on the Apple runtime.
|
||||
// Not supported on TARGET_OS_WIN32.
|
||||
#[cfg(all(feature = "apple", not(target_os = "windows")))]
|
||||
{
|
||||
// Supported since macOS 10.7.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
// x86_64 looks at the next call instruction.
|
||||
//
|
||||
// This is expected to be a PLT entry - if the user specifies
|
||||
// `-Zplt=no`, a GOT entry will be created instead, and this
|
||||
// will not work.
|
||||
}
|
||||
|
||||
// Supported since macOS 10.8.
|
||||
#[cfg(target_arch = "arm")]
|
||||
unsafe {
|
||||
core::arch::asm!("mov r7, r7", options(nomem, preserves_flags, nostack))
|
||||
};
|
||||
|
||||
// Supported since macOS 10.10.
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
unsafe {
|
||||
core::arch::asm!("mov fp, fp", options(nomem, preserves_flags, nostack))
|
||||
};
|
||||
|
||||
// Supported since macOS 10.12.
|
||||
#[cfg(target_arch = "x86")]
|
||||
unsafe {
|
||||
core::arch::asm!("mov ebp, ebp", options(nomem, preserves_flags, nostack))
|
||||
};
|
||||
}
|
||||
|
||||
// SAFETY: Same as `retain`, this is just an optimization.
|
||||
let res: *mut T = unsafe { ffi::objc_retainAutoreleasedReturnValue(ptr.cast()) }.cast();
|
||||
|
||||
// Ideally, we'd be able to specify that the above call should never
|
||||
// be tail-call optimized (become a `jmp` instruction instead of a
|
||||
// `call`); Rust doesn't really have a way of doing this currently, so
|
||||
// we just emit a simple `nop` to make such tail-call optimizations
|
||||
// less likely to occur.
|
||||
//
|
||||
// This is brittle! We should find a better solution!
|
||||
#[cfg(all(feature = "apple", not(target_os = "windows"), target_arch = "x86_64"))]
|
||||
{
|
||||
// SAFETY: Similar to above.
|
||||
unsafe { core::arch::asm!("nop", options(nomem, preserves_flags, nostack)) };
|
||||
// TODO: Possibly more efficient alternative? Also consider PLT.
|
||||
// #![feature(asm_sym)]
|
||||
// core::arch::asm!(
|
||||
// "mov rdi, rax",
|
||||
// "call {}",
|
||||
// sym objc2::ffi::objc_retainAutoreleasedReturnValue,
|
||||
// inout("rax") obj,
|
||||
// clobber_abi("C"),
|
||||
// );
|
||||
}
|
||||
|
||||
debug_assert_eq!(
|
||||
res, ptr,
|
||||
"objc_retainAutoreleasedReturnValue did not return the same pointer"
|
||||
);
|
||||
unsafe { Self::new(res) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn autorelease_inner(self) -> *mut T {
|
||||
// Note that this (and the actual `autorelease`) is not an associated
|
||||
// function. This breaks the guideline that smart pointers shouldn't
|
||||
// add inherent methods, but since autoreleasing only works on already
|
||||
// retained objects it is hard to imagine a case where the inner type
|
||||
// has a method with the same name.
|
||||
|
||||
let ptr = ManuallyDrop::new(self).ptr.as_ptr();
|
||||
// SAFETY: The `ptr` is guaranteed to be valid and have at least one
|
||||
// retain count.
|
||||
// And because of the ManuallyDrop, we don't call the Drop
|
||||
// implementation, so the object won't also be released there.
|
||||
let res: *mut T = unsafe { ffi::objc_autorelease(ptr.cast()) }.cast();
|
||||
debug_assert_eq!(res, ptr, "objc_autorelease did not return the same pointer");
|
||||
res
|
||||
}
|
||||
|
||||
/// Autoreleases and prepares the [`Id`] to be returned to Objective-C.
|
||||
///
|
||||
/// The object is not immediately released, but will be when the innermost
|
||||
/// autorelease pool is drained.
|
||||
///
|
||||
/// This is useful when [declaring your own methods][declare] where you
|
||||
/// will often find yourself in need of returning autoreleased objects to
|
||||
/// properly follow [Cocoa's Memory Management Policy][mmRules].
|
||||
///
|
||||
/// To that end, you could use [`Id::autorelease`], but that would require
|
||||
/// you to have an [`AutoreleasePool`] object at hand, which you clearly
|
||||
/// won't have in such cases. This function doesn't require a `pool`
|
||||
/// object (but as a downside returns a pointer instead of a reference).
|
||||
///
|
||||
/// This is also more efficient than a normal `autorelease`, it makes a
|
||||
/// best effort attempt to hand off ownership of the retain count to a
|
||||
/// subsequent call to `objc_retainAutoreleasedReturnValue` /
|
||||
/// [`Id::retain_autoreleased`] in the enclosing call frame. Note: This
|
||||
/// optimization relies heavily on this function being tail called, so be
|
||||
/// careful to call this function at the end of your method.
|
||||
///
|
||||
/// [declare]: crate::declare
|
||||
/// [mmRules]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use objc2::{class, msg_send_id, sel};
|
||||
/// use objc2::declare::ClassBuilder;
|
||||
/// use objc2::rc::{Id, Owned};
|
||||
/// use objc2::runtime::{Class, Object, Sel};
|
||||
/// #
|
||||
/// # #[cfg(feature = "gnustep-1-7")]
|
||||
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
///
|
||||
/// let mut builder = ClassBuilder::new("ExampleObject", class!(NSObject)).unwrap();
|
||||
///
|
||||
/// extern "C" fn get(cls: &Class, _cmd: Sel) -> *mut Object {
|
||||
/// let obj: Id<Object, Owned> = unsafe { msg_send_id![cls, new] };
|
||||
/// obj.autorelease_return()
|
||||
/// }
|
||||
///
|
||||
/// unsafe {
|
||||
/// builder.add_class_method(
|
||||
/// sel!(get),
|
||||
/// get as extern "C" fn(_, _) -> _,
|
||||
/// );
|
||||
/// }
|
||||
///
|
||||
/// let cls = builder.register();
|
||||
/// ```
|
||||
#[doc(alias = "objc_autoreleaseReturnValue")]
|
||||
#[must_use = "If you don't intend to use the object any more, just drop it as usual"]
|
||||
#[inline]
|
||||
pub fn autorelease_return(self) -> *mut T {
|
||||
// See `autorelease_inner` for why this is an inherent method
|
||||
|
||||
let ptr = ManuallyDrop::new(self).ptr.as_ptr();
|
||||
// SAFETY: Same as `autorelease_inner`, this is just an optimization.
|
||||
let res: *mut T = unsafe { ffi::objc_autoreleaseReturnValue(ptr.cast()) }.cast();
|
||||
debug_assert_eq!(
|
||||
res, ptr,
|
||||
"objc_autoreleaseReturnValue did not return the same pointer"
|
||||
);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Consider something like this
|
||||
// #[cfg(block)]
|
||||
// impl<T: Block, O> Id<T, O> {
|
||||
// #[doc(alias = "objc_retainBlock")]
|
||||
// pub unsafe fn retain_block(block: *mut T) -> Option<Self> {
|
||||
// todo!()
|
||||
// }
|
||||
// }
|
||||
|
||||
// TODO: Add ?Sized bound
|
||||
impl<T: Message> Id<T, Owned> {
|
||||
/// Autoreleases the owned [`Id`], returning a mutable reference bound to
|
||||
/// the pool.
|
||||
///
|
||||
/// The object is not immediately released, but will be when the innermost
|
||||
/// / current autorelease pool (given as a parameter) is drained.
|
||||
#[doc(alias = "objc_autorelease")]
|
||||
#[must_use = "If you don't intend to use the object any more, just drop it as usual"]
|
||||
#[inline]
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub fn autorelease<'p>(self, pool: &'p AutoreleasePool) -> &'p mut T {
|
||||
let ptr = self.autorelease_inner();
|
||||
// SAFETY: The pointer is valid as a reference, and we've consumed
|
||||
// the unique access to the `Id` so mutability is safe.
|
||||
unsafe { pool.ptr_as_mut(ptr) }
|
||||
}
|
||||
|
||||
/// Promote a shared [`Id`] to an owned one, allowing it to be mutated.
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that there are no other pointers (including
|
||||
/// [`WeakId`][`super::WeakId`] pointers) to the same object.
|
||||
///
|
||||
/// This also means that the given [`Id`] should have a retain count of
|
||||
/// exactly 1 (except when autoreleases are involved).
|
||||
///
|
||||
/// In general, this is wildly unsafe, do see if you can find a different
|
||||
/// solution!
|
||||
#[inline]
|
||||
pub unsafe fn from_shared(obj: Id<T, Shared>) -> Self {
|
||||
// Note: We can't debug_assert retainCount because of autoreleases
|
||||
let ptr = ManuallyDrop::new(obj).ptr;
|
||||
// SAFETY: The pointer is valid
|
||||
// Ownership rules are upheld by the caller
|
||||
unsafe { <Id<T, Owned>>::new_nonnull(ptr) }
|
||||
}
|
||||
|
||||
/// Convert an owned to a shared [`Id`], allowing it to be cloned.
|
||||
///
|
||||
/// This is also implemented as a `From` conversion, but this name is more
|
||||
/// explicit, which may be useful in some cases.
|
||||
#[inline]
|
||||
pub fn into_shared(obj: Self) -> Id<T, Shared> {
|
||||
let ptr = ManuallyDrop::new(obj).ptr;
|
||||
// SAFETY: The pointer is valid, and ownership is simply decreased
|
||||
unsafe { <Id<T, Shared>>::new_nonnull(ptr) }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add ?Sized bound
|
||||
impl<T: Message> Id<T, Shared> {
|
||||
/// Autoreleases the shared [`Id`], returning an aliased reference bound
|
||||
/// to the pool.
|
||||
///
|
||||
/// The object is not immediately released, but will be when the innermost
|
||||
/// / current autorelease pool (given as a parameter) is drained.
|
||||
#[doc(alias = "objc_autorelease")]
|
||||
#[must_use = "If you don't intend to use the object any more, just drop it as usual"]
|
||||
#[inline]
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
pub fn autorelease<'p>(self, pool: &'p AutoreleasePool) -> &'p T {
|
||||
let ptr = self.autorelease_inner();
|
||||
// SAFETY: The pointer is valid as a reference
|
||||
unsafe { pool.ptr_as_ref(ptr) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ClassType + 'static, O: Ownership> Id<T, O>
|
||||
where
|
||||
T::Super: 'static,
|
||||
{
|
||||
/// Convert the object into it's superclass.
|
||||
#[inline]
|
||||
pub fn into_super(this: Self) -> Id<T::Super, O> {
|
||||
// SAFETY:
|
||||
// - The casted-to type is a superclass of the type.
|
||||
// - Both types are `'static` (this could maybe be relaxed a bit, but
|
||||
// let's just be on the safe side)!
|
||||
unsafe { Self::cast::<T::Super>(this) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message> From<Id<T, Owned>> for Id<T, Shared> {
|
||||
/// Convert an owned to a shared [`Id`], allowing it to be cloned.
|
||||
///
|
||||
/// Same as [`Id::into_shared`].
|
||||
#[inline]
|
||||
fn from(obj: Id<T, Owned>) -> Self {
|
||||
Id::into_shared(obj)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add ?Sized bound
|
||||
impl<T: Message> Clone for Id<T, Shared> {
|
||||
/// Makes a clone of the shared object.
|
||||
///
|
||||
/// This increases the object's reference count.
|
||||
#[doc(alias = "objc_retain")]
|
||||
#[doc(alias = "retain")]
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
// SAFETY: The pointer is valid
|
||||
let obj = unsafe { Id::retain(self.ptr.as_ptr()) };
|
||||
// SAFETY: `objc_retain` always returns the same object pointer, and
|
||||
// the pointer is guaranteed non-null by Id.
|
||||
unsafe { obj.unwrap_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
/// `#[may_dangle]` (see [this][dropck_eyepatch]) doesn't apply here since we
|
||||
/// don't run `T`'s destructor (rather, we want to discourage having `T`s with
|
||||
/// a destructor); and even if we did run the destructor, it would not be safe
|
||||
/// to add since we cannot verify that a `dealloc` method doesn't access
|
||||
/// borrowed data.
|
||||
///
|
||||
/// [dropck_eyepatch]: https://doc.rust-lang.org/nightly/nomicon/dropck.html#an-escape-hatch
|
||||
impl<T: ?Sized, O: Ownership> Drop for Id<T, O> {
|
||||
/// Releases the retained object.
|
||||
///
|
||||
/// The contained object's destructor (if it has one) is never run!
|
||||
#[doc(alias = "objc_release")]
|
||||
#[doc(alias = "release")]
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
// We could technically run the destructor for `T` when `O = Owned`,
|
||||
// and when `O = Shared` with (retainCount == 1), but that would be
|
||||
// confusing and inconsistent since we cannot guarantee that it's run.
|
||||
|
||||
// SAFETY: The `ptr` is guaranteed to be valid and have at least one
|
||||
// retain count
|
||||
unsafe { ffi::objc_release(self.ptr.as_ptr().cast()) };
|
||||
}
|
||||
}
|
||||
|
||||
// https://doc.rust-lang.org/nomicon/arc-mutex/arc-base.html#send-and-sync
|
||||
/// The `Send` implementation requires `T: Sync` because `Id<T, Shared>` give
|
||||
/// access to `&T`.
|
||||
///
|
||||
/// Additiontally, it requires `T: Send` because if `T: !Send`, you could
|
||||
/// clone a `Id<T, Shared>`, send it to another thread, and drop the clone
|
||||
/// last, making `dealloc` get called on the other thread, and violate
|
||||
/// `T: !Send`.
|
||||
unsafe impl<T: Sync + Send + ?Sized> Send for Id<T, Shared> {}
|
||||
|
||||
/// The `Sync` implementation requires `T: Sync` because `&Id<T, Shared>` give
|
||||
/// access to `&T`.
|
||||
///
|
||||
/// Additiontally, it requires `T: Send`, because if `T: !Send`, you could
|
||||
/// clone a `&Id<T, Shared>` from another thread, and drop the clone last,
|
||||
/// making `dealloc` get called on the other thread, and violate `T: !Send`.
|
||||
unsafe impl<T: Sync + Send + ?Sized> Sync for Id<T, Shared> {}
|
||||
|
||||
/// `Id<T, Owned>` are `Send` if `T` is `Send` because they give the same
|
||||
/// access as having a T directly.
|
||||
unsafe impl<T: Send + ?Sized> Send for Id<T, Owned> {}
|
||||
|
||||
/// `Id<T, Owned>` are `Sync` if `T` is `Sync` because they give the same
|
||||
/// access as having a `T` directly.
|
||||
unsafe impl<T: Sync + ?Sized> Sync for Id<T, Owned> {}
|
||||
|
||||
impl<T: ?Sized, O: Ownership> Deref for Id<T, O> {
|
||||
type Target = T;
|
||||
|
||||
/// Obtain an immutable reference to the object.
|
||||
// Box doesn't inline, but that's because it's a compiler built-in
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
// SAFETY: The pointer's validity is verified when the type is created
|
||||
unsafe { self.ptr.as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> DerefMut for Id<T, Owned> {
|
||||
/// Obtain a mutable reference to the object.
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
// SAFETY: The pointer's validity is verified when the type is created
|
||||
// Additionally, the owned `Id` is the unique owner of the object, so
|
||||
// mutability is safe.
|
||||
unsafe { self.ptr.as_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized, O: Ownership> fmt::Pointer for Id<T, O> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Pointer::fmt(&self.ptr.as_ptr(), f)
|
||||
}
|
||||
}
|
||||
|
||||
// This is valid without `T: Unpin` because we don't implement any projection.
|
||||
//
|
||||
// See https://doc.rust-lang.org/1.54.0/src/alloc/boxed.rs.html#1652-1675
|
||||
// and the `Arc` implementation.
|
||||
impl<T: ?Sized, O: Ownership> Unpin for Id<T, O> {}
|
||||
|
||||
impl<T: RefUnwindSafe + ?Sized, O: Ownership> RefUnwindSafe for Id<T, O> {}
|
||||
|
||||
// Same as `Arc<T>`.
|
||||
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Id<T, Shared> {}
|
||||
|
||||
// Same as `Box<T>`.
|
||||
impl<T: UnwindSafe + ?Sized> UnwindSafe for Id<T, Owned> {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::msg_send;
|
||||
use crate::rc::{autoreleasepool, RcTestObject, ThreadTestData};
|
||||
use crate::runtime::Object;
|
||||
|
||||
#[track_caller]
|
||||
fn assert_retain_count(obj: &Object, expected: usize) {
|
||||
let retain_count: usize = unsafe { msg_send![obj, retainCount] };
|
||||
assert_eq!(retain_count, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_drop() {
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
let obj = RcTestObject::new();
|
||||
expected.alloc += 1;
|
||||
expected.init += 1;
|
||||
expected.assert_current();
|
||||
|
||||
drop(obj);
|
||||
expected.release += 1;
|
||||
expected.dealloc += 1;
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_autorelease() {
|
||||
let obj: Id<_, Shared> = RcTestObject::new().into();
|
||||
let cloned = obj.clone();
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
autoreleasepool(|pool| {
|
||||
let _ref = obj.autorelease(pool);
|
||||
expected.autorelease += 1;
|
||||
expected.assert_current();
|
||||
assert_retain_count(&cloned, 2);
|
||||
});
|
||||
expected.release += 1;
|
||||
expected.assert_current();
|
||||
assert_retain_count(&cloned, 1);
|
||||
|
||||
autoreleasepool(|pool| {
|
||||
let _ref = cloned.autorelease(pool);
|
||||
expected.autorelease += 1;
|
||||
expected.assert_current();
|
||||
});
|
||||
expected.release += 1;
|
||||
expected.dealloc += 1;
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clone() {
|
||||
let obj: Id<_, Owned> = RcTestObject::new();
|
||||
assert_retain_count(&obj, 1);
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
let obj: Id<_, Shared> = obj.into();
|
||||
expected.assert_current();
|
||||
assert_retain_count(&obj, 1);
|
||||
|
||||
let cloned = obj.clone();
|
||||
expected.retain += 1;
|
||||
expected.assert_current();
|
||||
assert_retain_count(&cloned, 2);
|
||||
assert_retain_count(&obj, 2);
|
||||
|
||||
drop(obj);
|
||||
expected.release += 1;
|
||||
expected.assert_current();
|
||||
assert_retain_count(&cloned, 1);
|
||||
|
||||
drop(cloned);
|
||||
expected.release += 1;
|
||||
expected.dealloc += 1;
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_retain_autoreleased_works_as_retain() {
|
||||
let obj: Id<_, Shared> = RcTestObject::new().into();
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
let ptr = Id::as_ptr(&obj) as *mut RcTestObject;
|
||||
let _obj2: Id<_, Shared> = unsafe { Id::retain_autoreleased(ptr) }.unwrap();
|
||||
expected.retain += 1;
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cast() {
|
||||
let obj: Id<RcTestObject, _> = RcTestObject::new();
|
||||
let expected = ThreadTestData::current();
|
||||
|
||||
// SAFETY: Any object can be cast to `Object`
|
||||
let obj: Id<Object, _> = unsafe { Id::cast(obj) };
|
||||
expected.assert_current();
|
||||
|
||||
// SAFETY: The object was originally `RcTestObject`
|
||||
let _obj: Id<RcTestObject, _> = unsafe { Id::cast(obj) };
|
||||
expected.assert_current();
|
||||
}
|
||||
}
|
||||
302
third-party/vendor/objc2/src/rc/id_forwarding_impls.rs
vendored
Normal file
302
third-party/vendor/objc2/src/rc/id_forwarding_impls.rs
vendored
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
//! Trivial forwarding impls on `Id`.
|
||||
//!
|
||||
//! Kept here to keep `id.rs` free from this boilerplate.
|
||||
//!
|
||||
//! `#[inline]` is used where the standard library `Box` uses it.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use alloc::borrow;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt;
|
||||
use core::future::Future;
|
||||
use core::hash;
|
||||
use core::iter::FusedIterator;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
|
||||
use super::{Id, Owned, Ownership};
|
||||
|
||||
impl<T: PartialEq + ?Sized, O: Ownership> PartialEq for Id<T, O> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
(**self).eq(&**other)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::partialeq_ne_impl)]
|
||||
fn ne(&self, other: &Self) -> bool {
|
||||
(**self).ne(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq + ?Sized, O: Ownership> Eq for Id<T, O> {}
|
||||
|
||||
impl<T: PartialOrd + ?Sized, O: Ownership> PartialOrd for Id<T, O> {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
(**self).partial_cmp(&**other)
|
||||
}
|
||||
#[inline]
|
||||
fn lt(&self, other: &Self) -> bool {
|
||||
(**self).lt(&**other)
|
||||
}
|
||||
#[inline]
|
||||
fn le(&self, other: &Self) -> bool {
|
||||
(**self).le(&**other)
|
||||
}
|
||||
#[inline]
|
||||
fn ge(&self, other: &Self) -> bool {
|
||||
(**self).ge(&**other)
|
||||
}
|
||||
#[inline]
|
||||
fn gt(&self, other: &Self) -> bool {
|
||||
(**self).gt(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ord + ?Sized, O: Ownership> Ord for Id<T, O> {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
(**self).cmp(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: hash::Hash + ?Sized, O: Ownership> hash::Hash for Id<T, O> {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
(**self).hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: hash::Hasher + ?Sized> hash::Hasher for Id<T, Owned> {
|
||||
fn finish(&self) -> u64 {
|
||||
(**self).finish()
|
||||
}
|
||||
fn write(&mut self, bytes: &[u8]) {
|
||||
(**self).write(bytes)
|
||||
}
|
||||
fn write_u8(&mut self, i: u8) {
|
||||
(**self).write_u8(i)
|
||||
}
|
||||
fn write_u16(&mut self, i: u16) {
|
||||
(**self).write_u16(i)
|
||||
}
|
||||
fn write_u32(&mut self, i: u32) {
|
||||
(**self).write_u32(i)
|
||||
}
|
||||
fn write_u64(&mut self, i: u64) {
|
||||
(**self).write_u64(i)
|
||||
}
|
||||
fn write_u128(&mut self, i: u128) {
|
||||
(**self).write_u128(i)
|
||||
}
|
||||
fn write_usize(&mut self, i: usize) {
|
||||
(**self).write_usize(i)
|
||||
}
|
||||
fn write_i8(&mut self, i: i8) {
|
||||
(**self).write_i8(i)
|
||||
}
|
||||
fn write_i16(&mut self, i: i16) {
|
||||
(**self).write_i16(i)
|
||||
}
|
||||
fn write_i32(&mut self, i: i32) {
|
||||
(**self).write_i32(i)
|
||||
}
|
||||
fn write_i64(&mut self, i: i64) {
|
||||
(**self).write_i64(i)
|
||||
}
|
||||
fn write_i128(&mut self, i: i128) {
|
||||
(**self).write_i128(i)
|
||||
}
|
||||
fn write_isize(&mut self, i: isize) {
|
||||
(**self).write_isize(i)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display + ?Sized, O: Ownership> fmt::Display for Id<T, O> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug + ?Sized, O: Ownership> fmt::Debug for Id<T, O> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Iterator + ?Sized> Iterator for Id<I, Owned> {
|
||||
type Item = I::Item;
|
||||
fn next(&mut self) -> Option<I::Item> {
|
||||
(**self).next()
|
||||
}
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(**self).size_hint()
|
||||
}
|
||||
fn nth(&mut self, n: usize) -> Option<I::Item> {
|
||||
(**self).nth(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: DoubleEndedIterator + ?Sized> DoubleEndedIterator for Id<I, Owned> {
|
||||
fn next_back(&mut self) -> Option<I::Item> {
|
||||
(**self).next_back()
|
||||
}
|
||||
fn nth_back(&mut self, n: usize) -> Option<I::Item> {
|
||||
(**self).nth_back(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: ExactSizeIterator + ?Sized> ExactSizeIterator for Id<I, Owned> {
|
||||
fn len(&self) -> usize {
|
||||
(**self).len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: FusedIterator + ?Sized> FusedIterator for Id<I, Owned> {}
|
||||
|
||||
// TODO: Consider this impl
|
||||
// impl<'a, T, O: Ownership> IntoIterator for &'a Id<T, O>
|
||||
// where
|
||||
// &'a T: IntoIterator,
|
||||
// {
|
||||
// type Item = <&'a T as IntoIterator>::Item;
|
||||
// type IntoIter = <&'a T as IntoIterator>::IntoIter;
|
||||
//
|
||||
// fn into_iter(self) -> Self::IntoIter {
|
||||
// (**self).into_iter()
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<T, O: Ownership> borrow::Borrow<T> for Id<T, O> {
|
||||
fn borrow(&self) -> &T {
|
||||
Deref::deref(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> borrow::BorrowMut<T> for Id<T, Owned> {
|
||||
fn borrow_mut(&mut self) -> &mut T {
|
||||
DerefMut::deref_mut(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized, O: Ownership> AsRef<T> for Id<T, O> {
|
||||
fn as_ref(&self) -> &T {
|
||||
Deref::deref(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> AsMut<T> for Id<T, Owned> {
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
DerefMut::deref_mut(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Error + ?Sized, O: Ownership> Error for Id<T, O> {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
(**self).source()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: io::Read + ?Sized> io::Read for Id<T, Owned> {
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
(**self).read(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
(**self).read_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
(**self).read_to_end(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
(**self).read_to_string(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
(**self).read_exact(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: io::Write + ?Sized> io::Write for Id<T, Owned> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
(**self).write(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
(**self).write_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
(**self).flush()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
(**self).write_all(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
(**self).write_fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: io::Seek + ?Sized> io::Seek for Id<T, Owned> {
|
||||
#[inline]
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
||||
(**self).seek(pos)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
(**self).stream_position()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: io::BufRead + ?Sized> io::BufRead for Id<T, Owned> {
|
||||
#[inline]
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
(**self).fill_buf()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn consume(&mut self, amt: usize) {
|
||||
(**self).consume(amt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
(**self).read_until(byte, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
(**self).read_line(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Future + Unpin + ?Sized> Future for Id<T, Owned> {
|
||||
type Output = T::Output;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
T::poll(Pin::new(&mut *self), cx)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: impl Fn traits, CoerceUnsized, Stream and so on when stabilized
|
||||
72
third-party/vendor/objc2/src/rc/id_traits.rs
vendored
Normal file
72
third-party/vendor/objc2/src/rc/id_traits.rs
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
//! Helper traits for Id.
|
||||
|
||||
use super::{Id, Owned, Ownership};
|
||||
use crate::Message;
|
||||
|
||||
/// Helper trait for functionality on slices containing [`Id`]s.
|
||||
pub trait SliceId {
|
||||
/// The type of the items in the slice.
|
||||
type Item: ?Sized;
|
||||
|
||||
/// Convert a slice of [`Id`]s into a slice of references.
|
||||
fn as_slice_ref(&self) -> &[&Self::Item];
|
||||
|
||||
/// Convert a mutable slice of [`Id`]s into a mutable slice of references.
|
||||
fn as_slice_mut(&mut self) -> &mut [&Self::Item];
|
||||
}
|
||||
|
||||
/// Helper trait for functionality on slices containing owned [`Id`]s.
|
||||
pub trait SliceIdMut: SliceId {
|
||||
/// Convert a mutable slice of mutable [`Id`]s into a mutable slice of
|
||||
/// mutable references.
|
||||
fn as_mut_slice_mut(&mut self) -> &mut [&mut Self::Item];
|
||||
}
|
||||
|
||||
impl<T: Message + ?Sized, O: Ownership> SliceId for [Id<T, O>] {
|
||||
type Item = T;
|
||||
|
||||
fn as_slice_ref(&self) -> &[&T] {
|
||||
let ptr = self as *const Self as *const [&T];
|
||||
// SAFETY: Id<T, O> and &T have the same memory layout. Further safety
|
||||
// follows from `Deref` impl.
|
||||
unsafe { ptr.as_ref().unwrap_unchecked() }
|
||||
}
|
||||
|
||||
fn as_slice_mut(&mut self) -> &mut [&T] {
|
||||
let ptr = self as *mut Self as *mut [&T];
|
||||
// SAFETY: Id<T, O> and &T have the same memory layout. Further safety
|
||||
// follows from `Deref` impl.
|
||||
unsafe { ptr.as_mut().unwrap_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message + ?Sized> SliceIdMut for [Id<T, Owned>] {
|
||||
fn as_mut_slice_mut(&mut self) -> &mut [&mut T] {
|
||||
let ptr = self as *mut Self as *mut [&mut T];
|
||||
// SAFETY: Id<T, O> and &mut T have the same memory layout, and the
|
||||
// `Id` is `Owned` so we're allowed to hand out mutable references.
|
||||
// Further safety follows from `DerefMut` impl.
|
||||
unsafe { ptr.as_mut().unwrap_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper trait to implement [`Default`] on types whoose default value is an
|
||||
/// [`Id`].
|
||||
// TODO: Maybe make this `unsafe` and provide a default implementation?
|
||||
pub trait DefaultId {
|
||||
/// Indicates whether the default value is mutable or immutable.
|
||||
type Ownership: Ownership;
|
||||
|
||||
/// The default [`Id`] for a type.
|
||||
///
|
||||
/// On most objects the implementation would just be sending a message to
|
||||
/// the `new` selector.
|
||||
fn default_id() -> Id<Self, Self::Ownership>;
|
||||
}
|
||||
|
||||
impl<T: DefaultId + ?Sized> Default for Id<T, T::Ownership> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
T::default_id()
|
||||
}
|
||||
}
|
||||
132
third-party/vendor/objc2/src/rc/mod.rs
vendored
Normal file
132
third-party/vendor/objc2/src/rc/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
//! Utilities for reference counting Objective-C objects.
|
||||
//!
|
||||
//! These utilities in this module provide ARC-like semantics for working with
|
||||
//! Objective-C's reference counted objects.
|
||||
//!
|
||||
//! A smart pointer [`Id`] is provided to ensure that Objective-C objects are
|
||||
//! retained and released when created and dropped, respectively.
|
||||
//!
|
||||
//! To enforce aliasing rules, an `Id` can be either owned or shared; if it is
|
||||
//! owned, meaning the `Id` is the only reference to the object, it can be
|
||||
//! mutably dereferenced. An owned `Id` can be converted to a shared `Id`,
|
||||
//! which can be cloned to allow multiple references.
|
||||
//!
|
||||
//! Weak references may be created using the [`WeakId`] struct; these will not
|
||||
//! retain the object, but one can attempt to load them and obtain an `Id`, or
|
||||
//! safely fail if the object has been deallocated.
|
||||
//!
|
||||
//! See [the clang documentation][clang-arc] and [the Apple article on memory
|
||||
//! management][mem-mgmt] (similar document exists [for Core Foundation][cf])
|
||||
//! for more information on automatic and manual reference counting.
|
||||
//!
|
||||
//! It can also be useful to [enable Malloc Debugging][mem-debug] if you're trying
|
||||
//! to figure out if/where your application has memory errors and leaks.
|
||||
//!
|
||||
//! [clang-arc]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html
|
||||
//! [mem-mgmt]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html
|
||||
//! [cf]: https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/CFMemoryMgmt.html
|
||||
//! [mem-debug]: https://developer.apple.com/library/archive/documentation/Performance/Conceptual/ManagingMemory/Articles/MallocDebug.html
|
||||
//!
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
#![cfg_attr(feature = "apple", doc = "```")]
|
||||
#![cfg_attr(not(feature = "apple"), doc = "```no_run")]
|
||||
//! use objc2::{class, msg_send_id};
|
||||
//! use objc2::rc::{autoreleasepool, Id, Shared, WeakId};
|
||||
//! use objc2::runtime::Object;
|
||||
//!
|
||||
//! // Id will release the object when dropped
|
||||
//! let obj: Id<Object, Shared> = unsafe {
|
||||
//! msg_send_id![class!(NSObject), new]
|
||||
//! };
|
||||
//!
|
||||
//! // Cloning retains the object an additional time
|
||||
//! let cloned = obj.clone();
|
||||
//! autoreleasepool(|pool| {
|
||||
//! // Autorelease consumes the Id, but won't
|
||||
//! // actually release until the end of an autoreleasepool
|
||||
//! let obj_ref: &Object = cloned.autorelease(pool);
|
||||
//! });
|
||||
//!
|
||||
//! // Weak references won't retain the object
|
||||
//! let weak = WeakId::new(&obj);
|
||||
//! drop(obj);
|
||||
//! assert!(weak.load().is_none());
|
||||
//! ```
|
||||
|
||||
mod allocated;
|
||||
mod autorelease;
|
||||
mod id;
|
||||
mod id_forwarding_impls;
|
||||
mod id_traits;
|
||||
mod ownership;
|
||||
mod weak_id;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_object;
|
||||
|
||||
pub use self::allocated::Allocated;
|
||||
pub use self::autorelease::{autoreleasepool, AutoreleasePool, AutoreleaseSafe};
|
||||
pub use self::id::Id;
|
||||
pub use self::id_traits::{DefaultId, SliceId, SliceIdMut};
|
||||
pub use self::ownership::{Owned, Ownership, Shared};
|
||||
pub use self::weak_id::WeakId;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) use self::test_object::{RcTestObject, ThreadTestData};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::size_of;
|
||||
|
||||
use super::{Id, Owned, Ownership, Shared, WeakId};
|
||||
use crate::runtime::Object;
|
||||
|
||||
#[repr(C)]
|
||||
struct TestType {
|
||||
inner: Object,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct MyObject<'a> {
|
||||
inner: Object,
|
||||
p: PhantomData<&'a str>,
|
||||
}
|
||||
|
||||
/// Test that `Id<T, O>` is covariant over `T`.
|
||||
#[allow(unused)]
|
||||
fn assert_id_variance<'a, 'b, O: Ownership>(
|
||||
obj: &'a Id<MyObject<'static>, O>,
|
||||
) -> &'a Id<MyObject<'b>, O> {
|
||||
obj
|
||||
}
|
||||
|
||||
/// Test that `WeakId<T>` is covariant over `T`.
|
||||
#[allow(unused)]
|
||||
fn assert_weak_id_variance<'a, 'b>(
|
||||
obj: &'a WeakId<MyObject<'static>>,
|
||||
) -> &'a WeakId<MyObject<'b>> {
|
||||
obj
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_size_of() {
|
||||
assert_eq!(size_of::<Id<TestType, Owned>>(), size_of::<&TestType>());
|
||||
assert_eq!(size_of::<Id<TestType, Shared>>(), size_of::<&TestType>());
|
||||
assert_eq!(
|
||||
size_of::<Option<Id<TestType, Owned>>>(),
|
||||
size_of::<&TestType>()
|
||||
);
|
||||
assert_eq!(
|
||||
size_of::<Option<Id<TestType, Shared>>>(),
|
||||
size_of::<&TestType>()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
size_of::<Option<WeakId<TestType>>>(),
|
||||
size_of::<*const ()>()
|
||||
);
|
||||
}
|
||||
}
|
||||
82
third-party/vendor/objc2/src/rc/ownership.rs
vendored
Normal file
82
third-party/vendor/objc2/src/rc/ownership.rs
vendored
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
use core::fmt::Debug;
|
||||
use core::hash::Hash;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
use super::AutoreleaseSafe;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
enum Never {}
|
||||
|
||||
/// A type used to mark that a struct owns the object(s) it contains,
|
||||
/// so it has the sole references to them.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub struct Owned {
|
||||
inner: Never,
|
||||
}
|
||||
|
||||
/// A type used to mark that the object(s) a struct contains are shared,
|
||||
/// so there may be other references to them.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub struct Shared {
|
||||
inner: Never,
|
||||
}
|
||||
|
||||
mod private {
|
||||
pub trait Sealed {}
|
||||
|
||||
impl Sealed for super::Owned {}
|
||||
impl Sealed for super::Shared {}
|
||||
}
|
||||
|
||||
/// A type that marks what type of ownership a struct has over the object(s)
|
||||
/// it contains; specifically, either [`Owned`] or [`Shared`].
|
||||
///
|
||||
/// This trait is sealed and not meant to be implemented outside of the this
|
||||
/// crate.
|
||||
pub trait Ownership:
|
||||
private::Sealed
|
||||
// Special
|
||||
+ 'static
|
||||
+ Sized
|
||||
// Auto-traits
|
||||
+ Send
|
||||
+ Sync
|
||||
+ Unpin
|
||||
+ UnwindSafe
|
||||
+ RefUnwindSafe
|
||||
// Derived
|
||||
+ Clone
|
||||
+ Copy
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ PartialOrd
|
||||
+ Ord
|
||||
+ Hash
|
||||
+ Debug
|
||||
// Custom
|
||||
+ AutoreleaseSafe
|
||||
{
|
||||
}
|
||||
|
||||
impl Ownership for Owned {}
|
||||
impl Ownership for Shared {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_generic_ownership_traits() {
|
||||
fn assert_partialeq<T: PartialEq>() {}
|
||||
|
||||
assert_partialeq::<Shared>();
|
||||
assert_partialeq::<Owned>();
|
||||
|
||||
fn test_ownership_implies_partialeq<O: Ownership>() {
|
||||
assert_partialeq::<O>();
|
||||
}
|
||||
|
||||
test_ownership_implies_partialeq::<Shared>();
|
||||
test_ownership_implies_partialeq::<Owned>();
|
||||
}
|
||||
}
|
||||
175
third-party/vendor/objc2/src/rc/test_object.rs
vendored
Normal file
175
third-party/vendor/objc2/src/rc/test_object.rs
vendored
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
use core::cell::RefCell;
|
||||
use core::mem::ManuallyDrop;
|
||||
use core::ptr;
|
||||
|
||||
use super::{Id, Owned};
|
||||
use crate::foundation::{NSObject, NSZone};
|
||||
use crate::{declare_class, msg_send, ClassType};
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub(crate) struct ThreadTestData {
|
||||
pub(crate) alloc: usize,
|
||||
pub(crate) dealloc: usize,
|
||||
pub(crate) init: usize,
|
||||
pub(crate) retain: usize,
|
||||
pub(crate) copy: usize,
|
||||
pub(crate) mutable_copy: usize,
|
||||
pub(crate) release: usize,
|
||||
pub(crate) autorelease: usize,
|
||||
pub(crate) try_retain: usize,
|
||||
pub(crate) try_retain_fail: usize,
|
||||
}
|
||||
|
||||
impl ThreadTestData {
|
||||
/// Get the amount of method calls performed on the current thread.
|
||||
pub(crate) fn current() -> ThreadTestData {
|
||||
TEST_DATA.with(|data| data.borrow().clone())
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn assert_current(&self) {
|
||||
let current = Self::current();
|
||||
let mut expected = self.clone();
|
||||
if cfg!(feature = "gnustep-1-7") {
|
||||
// GNUStep doesn't have `tryRetain`, it uses `retain` directly
|
||||
let retain_diff = expected.try_retain - current.try_retain;
|
||||
expected.retain += retain_diff;
|
||||
expected.try_retain -= retain_diff;
|
||||
|
||||
// GNUStep doesn't call `autorelease` if it's overridden
|
||||
expected.autorelease = 0;
|
||||
}
|
||||
assert_eq!(current, expected);
|
||||
}
|
||||
}
|
||||
|
||||
std::thread_local! {
|
||||
pub(crate) static TEST_DATA: RefCell<ThreadTestData> = RefCell::new(Default::default());
|
||||
}
|
||||
|
||||
declare_class!(
|
||||
/// A helper object that counts how many times various reference-counting
|
||||
/// primitives are called.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) struct RcTestObject {}
|
||||
|
||||
unsafe impl ClassType for RcTestObject {
|
||||
type Super = NSObject;
|
||||
}
|
||||
|
||||
unsafe impl RcTestObject {
|
||||
#[sel(newReturningNull)]
|
||||
fn new_returning_null() -> *mut Self {
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[sel(alloc)]
|
||||
fn alloc() -> *mut Self {
|
||||
TEST_DATA.with(|data| data.borrow_mut().alloc += 1);
|
||||
let superclass = NSObject::class().metaclass();
|
||||
let zone: *const NSZone = ptr::null();
|
||||
unsafe { msg_send![super(Self::class(), superclass), allocWithZone: zone] }
|
||||
}
|
||||
|
||||
#[sel(allocWithZone:)]
|
||||
fn alloc_with_zone(zone: *const NSZone) -> *mut Self {
|
||||
TEST_DATA.with(|data| data.borrow_mut().alloc += 1);
|
||||
let superclass = NSObject::class().metaclass();
|
||||
unsafe { msg_send![super(Self::class(), superclass), allocWithZone: zone] }
|
||||
}
|
||||
|
||||
#[sel(allocReturningNull)]
|
||||
fn alloc_returning_null() -> *mut Self {
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[sel(init)]
|
||||
fn init(&mut self) -> *mut Self {
|
||||
TEST_DATA.with(|data| data.borrow_mut().init += 1);
|
||||
unsafe { msg_send![super(self), init] }
|
||||
}
|
||||
|
||||
#[sel(initReturningNull)]
|
||||
fn init_returning_null(&mut self) -> *mut Self {
|
||||
let _: () = unsafe { msg_send![self, release] };
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[sel(retain)]
|
||||
fn retain(&self) -> *mut Self {
|
||||
TEST_DATA.with(|data| data.borrow_mut().retain += 1);
|
||||
unsafe { msg_send![super(self), retain] }
|
||||
}
|
||||
|
||||
#[sel(release)]
|
||||
fn release(&self) {
|
||||
TEST_DATA.with(|data| data.borrow_mut().release += 1);
|
||||
unsafe { msg_send![super(self), release] }
|
||||
}
|
||||
|
||||
#[sel(autorelease)]
|
||||
fn autorelease(&self) -> *mut Self {
|
||||
TEST_DATA.with(|data| data.borrow_mut().autorelease += 1);
|
||||
unsafe { msg_send![super(self), autorelease] }
|
||||
}
|
||||
|
||||
#[sel(dealloc)]
|
||||
unsafe fn dealloc(&mut self) {
|
||||
TEST_DATA.with(|data| data.borrow_mut().dealloc += 1);
|
||||
unsafe { msg_send![super(self), dealloc] }
|
||||
}
|
||||
|
||||
#[sel(_tryRetain)]
|
||||
unsafe fn try_retain(&self) -> bool {
|
||||
TEST_DATA.with(|data| data.borrow_mut().try_retain += 1);
|
||||
let res: bool = unsafe { msg_send![super(self), _tryRetain] };
|
||||
if !res {
|
||||
TEST_DATA.with(|data| data.borrow_mut().try_retain -= 1);
|
||||
TEST_DATA.with(|data| data.borrow_mut().try_retain_fail += 1);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
#[sel(copyWithZone:)]
|
||||
fn copy_with_zone(&self, _zone: *const NSZone) -> *const Self {
|
||||
TEST_DATA.with(|data| data.borrow_mut().copy += 1);
|
||||
Id::consume_as_ptr(ManuallyDrop::new(Self::new()))
|
||||
}
|
||||
|
||||
#[sel(mutableCopyWithZone:)]
|
||||
fn mutable_copy_with_zone(&self, _zone: *const NSZone) -> *const Self {
|
||||
TEST_DATA.with(|data| data.borrow_mut().mutable_copy += 1);
|
||||
Id::consume_as_ptr(ManuallyDrop::new(Self::new()))
|
||||
}
|
||||
|
||||
#[sel(copyReturningNull)]
|
||||
fn copy_returning_null(&self) -> *const Self {
|
||||
ptr::null()
|
||||
}
|
||||
|
||||
#[sel(methodReturningNull)]
|
||||
fn method_returning_null(&self) -> *const Self {
|
||||
ptr::null()
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
unsafe impl Send for RcTestObject {}
|
||||
unsafe impl Sync for RcTestObject {}
|
||||
|
||||
impl RcTestObject {
|
||||
pub(crate) fn new() -> Id<Self, Owned> {
|
||||
// Use msg_send! - msg_send_id! is tested elsewhere!
|
||||
unsafe { Id::new(msg_send![Self::class(), new]) }.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn ensure_declared_name() {
|
||||
assert_eq!(RcTestObject::class().name(), RcTestObject::NAME);
|
||||
}
|
||||
}
|
||||
221
third-party/vendor/objc2/src/rc/weak_id.rs
vendored
Normal file
221
third-party/vendor/objc2/src/rc/weak_id.rs
vendored
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
use alloc::boxed::Box;
|
||||
use core::cell::UnsafeCell;
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
use core::ptr;
|
||||
use std::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
use super::{Id, Shared};
|
||||
use crate::ffi;
|
||||
use crate::Message;
|
||||
|
||||
/// A pointer type for a weak reference to an Objective-C reference counted
|
||||
/// object.
|
||||
///
|
||||
/// Allows breaking reference cycles and safely checking whether the object
|
||||
/// has been deallocated.
|
||||
#[repr(transparent)]
|
||||
pub struct WeakId<T: ?Sized> {
|
||||
/// We give the runtime the address to this box, so that it can modify it
|
||||
/// even if the `WeakId` is moved.
|
||||
///
|
||||
/// Loading may modify the pointer through a shared reference, so we use
|
||||
/// an UnsafeCell to get a *mut without self being mutable.
|
||||
///
|
||||
/// Remember that any thread may actually modify the inner value
|
||||
/// concurrently, but as long as we only use it through the `objc_XXXWeak`
|
||||
/// methods, all access is behind a lock.
|
||||
///
|
||||
/// TODO: Verify the need for UnsafeCell?
|
||||
/// TODO: Investigate if we can avoid some allocations using `Pin`.
|
||||
inner: Box<UnsafeCell<*mut ffi::objc_object>>,
|
||||
/// WeakId inherits variance, dropck and various marker traits from
|
||||
/// `Id<T, Shared>` because it can be loaded as a shared Id.
|
||||
item: PhantomData<Id<T, Shared>>,
|
||||
}
|
||||
|
||||
impl<T: Message> WeakId<T> {
|
||||
/// Construct a new [`WeakId`] referencing the given shared [`Id`].
|
||||
#[doc(alias = "objc_initWeak")]
|
||||
#[inline]
|
||||
pub fn new(obj: &Id<T, Shared>) -> Self {
|
||||
// Note that taking `&Id<T, Owned>` would not be safe since that would
|
||||
// allow loading an `Id<T, Shared>` later on.
|
||||
|
||||
// SAFETY: `obj` is valid
|
||||
unsafe { Self::new_inner(Id::as_ptr(obj)) }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The object must be valid or null.
|
||||
unsafe fn new_inner(obj: *const T) -> Self {
|
||||
let inner = Box::new(UnsafeCell::new(ptr::null_mut()));
|
||||
// SAFETY: `ptr` will never move, and the caller verifies `obj`
|
||||
let _ = unsafe { ffi::objc_initWeak(inner.get(), (obj as *mut T).cast()) };
|
||||
Self {
|
||||
inner,
|
||||
item: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Load a shared (and retained) [`Id`] if the object still exists.
|
||||
///
|
||||
/// Returns [`None`] if the object has been deallocated or was created
|
||||
/// with [`Default::default`].
|
||||
#[doc(alias = "retain")]
|
||||
#[doc(alias = "objc_loadWeak")]
|
||||
#[doc(alias = "objc_loadWeakRetained")]
|
||||
#[inline]
|
||||
pub fn load(&self) -> Option<Id<T, Shared>> {
|
||||
let ptr = self.inner.get();
|
||||
let obj = unsafe { ffi::objc_loadWeakRetained(ptr) }.cast();
|
||||
unsafe { Id::new(obj) }
|
||||
}
|
||||
|
||||
// TODO: Add `autorelease(&self) -> Option<&T>` using `objc_loadWeak`?
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for WeakId<T> {
|
||||
/// Drops the `WeakId` pointer.
|
||||
#[doc(alias = "objc_destroyWeak")]
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::objc_destroyWeak(self.inner.get()) }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add ?Sized
|
||||
impl<T> Clone for WeakId<T> {
|
||||
/// Makes a clone of the `WeakId` that points to the same object.
|
||||
#[doc(alias = "objc_copyWeak")]
|
||||
fn clone(&self) -> Self {
|
||||
let ptr = Box::new(UnsafeCell::new(ptr::null_mut()));
|
||||
unsafe { ffi::objc_copyWeak(ptr.get(), self.inner.get()) };
|
||||
Self {
|
||||
inner: ptr,
|
||||
item: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add ?Sized
|
||||
impl<T: Message> Default for WeakId<T> {
|
||||
/// Constructs a new `WeakId<T>` that doesn't reference any object.
|
||||
///
|
||||
/// Calling [`Self::load`] on the return value always gives [`None`].
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
// SAFETY: The pointer is null
|
||||
unsafe { Self::new_inner(ptr::null()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// This implementation follows the same reasoning as `Id<T, Shared>`.
|
||||
unsafe impl<T: Sync + Send + ?Sized> Sync for WeakId<T> {}
|
||||
|
||||
/// This implementation follows the same reasoning as `Id<T, Shared>`.
|
||||
unsafe impl<T: Sync + Send + ?Sized> Send for WeakId<T> {}
|
||||
|
||||
// Unsure about the Debug bound on T, see std::sync::Weak
|
||||
impl<T: fmt::Debug + ?Sized> fmt::Debug for WeakId<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "(WeakId)")
|
||||
}
|
||||
}
|
||||
|
||||
// Underneath this is just a `Box`
|
||||
impl<T: ?Sized> Unpin for WeakId<T> {}
|
||||
|
||||
// Same as `Id<T, Shared>`.
|
||||
impl<T: RefUnwindSafe + ?Sized> RefUnwindSafe for WeakId<T> {}
|
||||
|
||||
// Same as `Id<T, Shared>`.
|
||||
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for WeakId<T> {}
|
||||
|
||||
impl<T: Message> From<Id<T, Shared>> for WeakId<T> {
|
||||
#[inline]
|
||||
fn from(obj: Id<T, Shared>) -> Self {
|
||||
WeakId::new(&obj)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Message> TryFrom<WeakId<T>> for Id<T, Shared> {
|
||||
type Error = ();
|
||||
fn try_from(weak: WeakId<T>) -> Result<Self, ()> {
|
||||
weak.load().ok_or(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::rc::{RcTestObject, ThreadTestData};
|
||||
use crate::runtime::Object;
|
||||
|
||||
#[test]
|
||||
fn test_weak() {
|
||||
let obj: Id<_, Shared> = RcTestObject::new().into();
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
let weak = WeakId::new(&obj);
|
||||
expected.assert_current();
|
||||
|
||||
let strong = weak.load().unwrap();
|
||||
expected.try_retain += 1;
|
||||
expected.assert_current();
|
||||
assert!(ptr::eq(&*strong, &*obj));
|
||||
|
||||
drop(obj);
|
||||
drop(strong);
|
||||
expected.release += 2;
|
||||
expected.dealloc += 1;
|
||||
expected.assert_current();
|
||||
|
||||
if cfg!(not(feature = "gnustep-1-7")) {
|
||||
// This loads the object on GNUStep for some reason??
|
||||
assert!(weak.load().is_none());
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
drop(weak);
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_weak_clone() {
|
||||
let obj: Id<_, Shared> = RcTestObject::new().into();
|
||||
let mut expected = ThreadTestData::current();
|
||||
|
||||
let weak = WeakId::new(&obj);
|
||||
expected.assert_current();
|
||||
|
||||
let weak2 = weak.clone();
|
||||
if cfg!(feature = "apple") {
|
||||
expected.try_retain += 1;
|
||||
expected.release += 1;
|
||||
}
|
||||
expected.assert_current();
|
||||
|
||||
let strong = weak.load().unwrap();
|
||||
expected.try_retain += 1;
|
||||
expected.assert_current();
|
||||
assert!(ptr::eq(&*strong, &*obj));
|
||||
|
||||
let strong2 = weak2.load().unwrap();
|
||||
expected.try_retain += 1;
|
||||
expected.assert_current();
|
||||
assert!(ptr::eq(&*strong, &*strong2));
|
||||
|
||||
drop(weak);
|
||||
drop(weak2);
|
||||
expected.assert_current();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_weak_default() {
|
||||
let weak: WeakId<Object> = WeakId::default();
|
||||
assert!(weak.load().is_none());
|
||||
drop(weak);
|
||||
}
|
||||
}
|
||||
1037
third-party/vendor/objc2/src/runtime.rs
vendored
Normal file
1037
third-party/vendor/objc2/src/runtime.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
247
third-party/vendor/objc2/src/test_utils.rs
vendored
Normal file
247
third-party/vendor/objc2/src/test_utils.rs
vendored
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
use core::mem::ManuallyDrop;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_char;
|
||||
use std::sync::Once;
|
||||
|
||||
use crate::declare::{ClassBuilder, ProtocolBuilder};
|
||||
use crate::runtime::{Class, Object, Protocol, Sel};
|
||||
use crate::{ffi, Encode, Encoding, MessageReceiver};
|
||||
use crate::{msg_send, sel};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CustomObject {
|
||||
obj: *mut Object,
|
||||
}
|
||||
|
||||
impl CustomObject {
|
||||
fn new(class: &Class) -> Self {
|
||||
let ptr: *const Class = class;
|
||||
let obj = unsafe { ffi::class_createInstance(ptr.cast(), 0) }.cast();
|
||||
CustomObject { obj }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove the need for this hack
|
||||
impl crate::message::private::Sealed for &CustomObject {}
|
||||
impl crate::message::private::Sealed for &mut CustomObject {}
|
||||
impl crate::message::private::Sealed for ManuallyDrop<CustomObject> {}
|
||||
|
||||
unsafe impl MessageReceiver for &CustomObject {
|
||||
type __Inner = Object;
|
||||
|
||||
#[inline]
|
||||
fn __as_raw_receiver(self) -> *mut Object {
|
||||
self.obj
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl MessageReceiver for &mut CustomObject {
|
||||
type __Inner = Object;
|
||||
|
||||
#[inline]
|
||||
fn __as_raw_receiver(self) -> *mut Object {
|
||||
self.obj
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl MessageReceiver for ManuallyDrop<CustomObject> {
|
||||
type __Inner = Object;
|
||||
|
||||
#[inline]
|
||||
fn __as_raw_receiver(self) -> *mut Object {
|
||||
self.obj
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for CustomObject {
|
||||
type Target = Object;
|
||||
|
||||
fn deref(&self) -> &Object {
|
||||
unsafe { self.obj.as_ref().unwrap_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for CustomObject {
|
||||
fn deref_mut(&mut self) -> &mut Object {
|
||||
unsafe { self.obj.as_mut().unwrap_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for CustomObject {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
#[allow(deprecated)]
|
||||
ffi::object_dispose(self.obj.cast());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct CustomStruct {
|
||||
pub(crate) a: u64,
|
||||
pub(crate) b: u64,
|
||||
pub(crate) c: u64,
|
||||
pub(crate) d: u64,
|
||||
}
|
||||
|
||||
unsafe impl Encode for CustomStruct {
|
||||
const ENCODING: Encoding = Encoding::Struct(
|
||||
"CustomStruct",
|
||||
&[u64::ENCODING, u64::ENCODING, u64::ENCODING, u64::ENCODING],
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn custom_class() -> &'static Class {
|
||||
static REGISTER_CUSTOM_CLASS: Once = Once::new();
|
||||
|
||||
REGISTER_CUSTOM_CLASS.call_once(|| {
|
||||
// The runtime will call this method, so it has to be implemented
|
||||
extern "C" fn custom_obj_class_initialize(_this: &Class, _cmd: Sel) {}
|
||||
|
||||
let mut builder = ClassBuilder::root(
|
||||
"CustomObject",
|
||||
custom_obj_class_initialize as extern "C" fn(_, _),
|
||||
)
|
||||
.unwrap();
|
||||
let proto = custom_protocol();
|
||||
|
||||
builder.add_protocol(proto);
|
||||
builder.add_ivar::<u32>("_foo");
|
||||
|
||||
unsafe extern "C" fn custom_obj_release(this: *mut Object, _cmd: Sel) {
|
||||
// Drop the value
|
||||
let _ = CustomObject { obj: this };
|
||||
}
|
||||
|
||||
extern "C" fn custom_obj_set_foo(this: &mut Object, _cmd: Sel, foo: u32) {
|
||||
unsafe { this.set_ivar::<u32>("_foo", foo) }
|
||||
}
|
||||
|
||||
extern "C" fn custom_obj_get_foo(this: &Object, _cmd: Sel) -> u32 {
|
||||
unsafe { *this.ivar::<u32>("_foo") }
|
||||
}
|
||||
|
||||
extern "C" fn custom_obj_get_foo_reference(this: &Object, _cmd: Sel) -> &u32 {
|
||||
unsafe { this.ivar::<u32>("_foo") }
|
||||
}
|
||||
|
||||
extern "C" fn custom_obj_get_struct(_this: &Object, _cmd: Sel) -> CustomStruct {
|
||||
CustomStruct {
|
||||
a: 1,
|
||||
b: 2,
|
||||
c: 3,
|
||||
d: 4,
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn custom_obj_class_method(_this: &Class, _cmd: Sel) -> u32 {
|
||||
7
|
||||
}
|
||||
|
||||
extern "C" fn custom_obj_set_bar(this: &mut Object, _cmd: Sel, bar: u32) {
|
||||
unsafe {
|
||||
this.set_ivar::<u32>("_foo", bar);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn custom_obj_add_number_to_number(
|
||||
_this: &Class,
|
||||
_cmd: Sel,
|
||||
fst: i32,
|
||||
snd: i32,
|
||||
) -> i32 {
|
||||
fst + snd
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let release: unsafe extern "C" fn(_, _) = custom_obj_release;
|
||||
builder.add_method(sel!(release), release);
|
||||
|
||||
let set_foo: extern "C" fn(_, _, _) = custom_obj_set_foo;
|
||||
builder.add_method(sel!(setFoo:), set_foo);
|
||||
let get_foo: extern "C" fn(_, _) -> _ = custom_obj_get_foo;
|
||||
builder.add_method(sel!(foo), get_foo);
|
||||
let get_foo_reference: extern "C" fn(_, _) -> _ = custom_obj_get_foo_reference;
|
||||
builder.add_method(sel!(fooReference), get_foo_reference);
|
||||
let get_struct: extern "C" fn(_, _) -> CustomStruct = custom_obj_get_struct;
|
||||
builder.add_method(sel!(customStruct), get_struct);
|
||||
let class_method: extern "C" fn(_, _) -> _ = custom_obj_class_method;
|
||||
builder.add_class_method(sel!(classFoo), class_method);
|
||||
|
||||
let protocol_instance_method: extern "C" fn(_, _, _) = custom_obj_set_bar;
|
||||
builder.add_method(sel!(setBar:), protocol_instance_method);
|
||||
let protocol_class_method: extern "C" fn(_, _, _, _) -> _ =
|
||||
custom_obj_add_number_to_number;
|
||||
builder.add_class_method(sel!(addNumber:toNumber:), protocol_class_method);
|
||||
}
|
||||
|
||||
builder.register();
|
||||
});
|
||||
|
||||
// Can't use `class!` here since `CustomObject` is dynamically created.
|
||||
Class::get("CustomObject").unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn custom_protocol() -> &'static Protocol {
|
||||
static REGISTER_CUSTOM_PROTOCOL: Once = Once::new();
|
||||
|
||||
REGISTER_CUSTOM_PROTOCOL.call_once(|| {
|
||||
let mut builder = ProtocolBuilder::new("CustomProtocol").unwrap();
|
||||
|
||||
builder.add_method_description::<(i32,), ()>(sel!(setBar:), true);
|
||||
builder.add_method_description::<(), *const c_char>(sel!(getName), false);
|
||||
builder.add_class_method_description::<(i32, i32), i32>(sel!(addNumber:toNumber:), true);
|
||||
|
||||
builder.register();
|
||||
});
|
||||
|
||||
Protocol::get("CustomProtocol").unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn custom_subprotocol() -> &'static Protocol {
|
||||
static REGISTER_CUSTOM_SUBPROTOCOL: Once = Once::new();
|
||||
|
||||
REGISTER_CUSTOM_SUBPROTOCOL.call_once(|| {
|
||||
let super_proto = custom_protocol();
|
||||
let mut builder = ProtocolBuilder::new("CustomSubProtocol").unwrap();
|
||||
|
||||
builder.add_protocol(super_proto);
|
||||
builder.add_method_description::<(u32,), u32>(sel!(calculateFoo:), true);
|
||||
|
||||
builder.register();
|
||||
});
|
||||
|
||||
Protocol::get("CustomSubProtocol").unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn custom_object() -> CustomObject {
|
||||
CustomObject::new(custom_class())
|
||||
}
|
||||
|
||||
pub(crate) fn custom_subclass() -> &'static Class {
|
||||
static REGISTER_CUSTOM_SUBCLASS: Once = Once::new();
|
||||
|
||||
REGISTER_CUSTOM_SUBCLASS.call_once(|| {
|
||||
let superclass = custom_class();
|
||||
let mut builder = ClassBuilder::new("CustomSubclassObject", superclass).unwrap();
|
||||
|
||||
extern "C" fn custom_subclass_get_foo(this: &Object, _cmd: Sel) -> u32 {
|
||||
let foo: u32 = unsafe { msg_send![super(this, custom_class()), foo] };
|
||||
foo + 2
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let get_foo: extern "C" fn(_, _) -> _ = custom_subclass_get_foo;
|
||||
builder.add_method(sel!(foo), get_foo);
|
||||
}
|
||||
|
||||
builder.register();
|
||||
});
|
||||
|
||||
Class::get("CustomSubclassObject").unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn custom_subclass_object() -> CustomObject {
|
||||
CustomObject::new(custom_subclass())
|
||||
}
|
||||
216
third-party/vendor/objc2/src/verify.rs
vendored
Normal file
216
third-party/vendor/objc2/src/verify.rs
vendored
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
use core::fmt;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use malloc_buf::Malloc;
|
||||
use std::error::Error;
|
||||
|
||||
use crate::encode::{Encode, EncodeArguments, EncodeConvert, Encoding};
|
||||
use crate::runtime::{Class, Object, Sel};
|
||||
|
||||
/// Workaround for `Malloc<str>` not implementing common traits
|
||||
#[derive(Debug)]
|
||||
struct MallocEncoding(Malloc<str>);
|
||||
|
||||
// SAFETY: `char*` strings can safely be free'd on other threads.
|
||||
unsafe impl Send for MallocEncoding {}
|
||||
unsafe impl Sync for MallocEncoding {}
|
||||
|
||||
impl PartialEq for MallocEncoding {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
*self.0 == *other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for MallocEncoding {}
|
||||
|
||||
impl Hash for MallocEncoding {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
Hash::hash(&*self.0, state)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MallocEncoding {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&*self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
enum Inner {
|
||||
MethodNotFound,
|
||||
MismatchedReturn(MallocEncoding, Encoding),
|
||||
MismatchedArgumentsCount(usize, usize),
|
||||
MismatchedArgument(usize, MallocEncoding, Encoding),
|
||||
}
|
||||
|
||||
impl fmt::Display for Inner {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::MethodNotFound => {
|
||||
write!(f, "method not found")
|
||||
}
|
||||
Self::MismatchedReturn(expected, actual) => {
|
||||
write!(
|
||||
f,
|
||||
"expected return to have type code {}, but found {}",
|
||||
expected, actual
|
||||
)
|
||||
}
|
||||
Self::MismatchedArgumentsCount(expected, actual) => {
|
||||
write!(
|
||||
f,
|
||||
"expected {} arguments, but {} were given",
|
||||
expected, actual
|
||||
)
|
||||
}
|
||||
Self::MismatchedArgument(i, expected, actual) => {
|
||||
write!(
|
||||
f,
|
||||
"expected argument at index {} to have type code {}, but found {}",
|
||||
i, expected, actual
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Failed verifying selector on a class.
|
||||
///
|
||||
/// This is returned in the error case of [`Class::verify_sel`], see that for
|
||||
/// details.
|
||||
///
|
||||
/// This implements [`Error`], and a description of the error can be retrieved
|
||||
/// using [`fmt::Display`].
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct VerificationError(Inner);
|
||||
|
||||
impl From<Inner> for VerificationError {
|
||||
fn from(inner: Inner) -> Self {
|
||||
Self(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for VerificationError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Delegate to inner
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for VerificationError {}
|
||||
|
||||
pub(crate) fn verify_message_signature<A, R>(cls: &Class, sel: Sel) -> Result<(), VerificationError>
|
||||
where
|
||||
A: EncodeArguments,
|
||||
R: EncodeConvert,
|
||||
{
|
||||
let method = match cls.instance_method(sel) {
|
||||
Some(method) => method,
|
||||
None => return Err(Inner::MethodNotFound.into()),
|
||||
};
|
||||
|
||||
let actual = R::__Inner::ENCODING;
|
||||
let expected = method.return_type();
|
||||
if !actual.equivalent_to_str(&*expected) {
|
||||
return Err(Inner::MismatchedReturn(MallocEncoding(expected), actual).into());
|
||||
}
|
||||
|
||||
let self_and_cmd = [<*mut Object>::ENCODING, Sel::ENCODING];
|
||||
let args = A::ENCODINGS;
|
||||
|
||||
let actual = self_and_cmd.len() + args.len();
|
||||
let expected = method.arguments_count();
|
||||
if actual != expected {
|
||||
return Err(Inner::MismatchedArgumentsCount(expected, actual).into());
|
||||
}
|
||||
|
||||
for (i, actual) in self_and_cmd.iter().chain(args).copied().enumerate() {
|
||||
let expected = method.argument_type(i).unwrap();
|
||||
if !actual.equivalent_to_str(&*expected) {
|
||||
return Err(Inner::MismatchedArgument(i, MallocEncoding(expected), actual).into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::sel;
|
||||
use crate::test_utils;
|
||||
use alloc::string::ToString;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
#[test]
|
||||
fn test_verify_message() {
|
||||
let cls = test_utils::custom_class();
|
||||
|
||||
assert!(cls.verify_sel::<(), u32>(sel!(foo)).is_ok());
|
||||
assert!(cls.verify_sel::<(u32,), ()>(sel!(setFoo:)).is_ok());
|
||||
|
||||
let metaclass = cls.metaclass();
|
||||
metaclass
|
||||
.verify_sel::<(i32, i32), i32>(sel!(addNumber:toNumber:))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_message_errors() {
|
||||
let cls = test_utils::custom_class();
|
||||
|
||||
// Unimplemented selector (missing colon)
|
||||
let err = cls.verify_sel::<(), ()>(sel!(setFoo)).unwrap_err();
|
||||
assert_eq!(err.to_string(), "method not found");
|
||||
|
||||
// Incorrect return type
|
||||
let err = cls.verify_sel::<(u32,), u64>(sel!(setFoo:)).unwrap_err();
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"expected return to have type code v, but found Q"
|
||||
);
|
||||
|
||||
// Too many arguments
|
||||
let err = cls.verify_sel::<(u32, i8), ()>(sel!(setFoo:)).unwrap_err();
|
||||
assert_eq!(err.to_string(), "expected 3 arguments, but 4 were given");
|
||||
|
||||
// Too few arguments
|
||||
let err = cls.verify_sel::<(), ()>(sel!(setFoo:)).unwrap_err();
|
||||
assert_eq!(err.to_string(), "expected 3 arguments, but 2 were given");
|
||||
|
||||
// Incorrect argument type
|
||||
let err = cls.verify_sel::<(Sel,), ()>(sel!(setFoo:)).unwrap_err();
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"expected argument at index 2 to have type code I, but found :"
|
||||
);
|
||||
|
||||
// Metaclass
|
||||
let metaclass = cls.metaclass();
|
||||
let err = metaclass
|
||||
.verify_sel::<(i32, i32, i32), i32>(sel!(addNumber:toNumber:))
|
||||
.unwrap_err();
|
||||
assert_eq!(err.to_string(), "expected 4 arguments, but 5 were given");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "verify_message")]
|
||||
#[should_panic = "invalid message send to -[CustomObject foo]: expected return to have type code I, but found i"]
|
||||
fn test_send_message_verified() {
|
||||
let obj = test_utils::custom_object();
|
||||
let _: i32 = unsafe { crate::msg_send![&obj, foo] };
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "verify_message")]
|
||||
#[should_panic = "invalid message send to +[CustomObject abcDef]: method not found"]
|
||||
fn test_send_message_verified_to_class() {
|
||||
let cls = test_utils::custom_class();
|
||||
let _: i32 = unsafe { crate::msg_send![cls, abcDef] };
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_marker_traits() {
|
||||
fn assert_marker_traits<T: Send + Sync + UnwindSafe + RefUnwindSafe + Unpin>() {}
|
||||
assert_marker_traits::<VerificationError>();
|
||||
}
|
||||
}
|
||||
76
third-party/vendor/objc2/tests/id_retain_autoreleased.rs
vendored
Normal file
76
third-party/vendor/objc2/tests/id_retain_autoreleased.rs
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
use std::ffi::c_void;
|
||||
|
||||
use objc2::rc::{autoreleasepool, Id, Shared};
|
||||
use objc2::runtime::Object;
|
||||
use objc2::{class, msg_send};
|
||||
|
||||
#[cfg(feature = "gnustep-1-7")]
|
||||
#[test]
|
||||
fn ensure_linkage() {
|
||||
unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
}
|
||||
|
||||
fn retain_count(obj: &Object) -> usize {
|
||||
unsafe { msg_send![obj, retainCount] }
|
||||
}
|
||||
|
||||
fn create_data(bytes: &[u8]) -> Id<Object, Shared> {
|
||||
let bytes_ptr: *const c_void = bytes.as_ptr().cast();
|
||||
unsafe {
|
||||
// let obj: *mut Object = msg_send![
|
||||
// class!(NSMutableData),
|
||||
// dataWithBytes: bytes_ptr,
|
||||
// length: bytes.len(),
|
||||
// ];
|
||||
//
|
||||
// On x86 (and perhaps others), dataWithBytes does not tail call
|
||||
// `autorelease` and hence the return address points into that instead
|
||||
// of our code, making the fast autorelease scheme fail.
|
||||
//
|
||||
// So instead, we call `autorelease` manually here.
|
||||
let obj: *mut Object = msg_send![class!(NSMutableData), alloc];
|
||||
let obj: *mut Object = msg_send![
|
||||
obj,
|
||||
initWithBytes: bytes_ptr,
|
||||
length: bytes.len(),
|
||||
];
|
||||
let obj: *mut Object = msg_send![obj, autorelease];
|
||||
// All code between the `msg_send!` and the `retain_autoreleased` must
|
||||
// be able to be optimized away for this to work.
|
||||
Id::retain_autoreleased(obj).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_retain_autoreleased() {
|
||||
autoreleasepool(|_| {
|
||||
// Run once to allow DYLD to resolve the symbol stubs.
|
||||
// Required for making `retain_autoreleased` work on x86_64.
|
||||
let _data = create_data(b"12");
|
||||
|
||||
// When compiled in release mode / with optimizations enabled,
|
||||
// subsequent usage of `retain_autoreleased` will succeed in retaining
|
||||
// the autoreleased value!
|
||||
let expected = if cfg!(feature = "gnustep-1-7") {
|
||||
1
|
||||
} else if cfg!(any(
|
||||
debug_assertions,
|
||||
feature = "exception",
|
||||
feature = "verify_message"
|
||||
)) {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
let data = create_data(b"34");
|
||||
assert_eq!(retain_count(&data), expected);
|
||||
|
||||
let data = create_data(b"56");
|
||||
assert_eq!(retain_count(&data), expected);
|
||||
|
||||
// Here we manually clean up the autorelease, so it will always be 1.
|
||||
let data = autoreleasepool(|_| create_data(b"78"));
|
||||
assert_eq!(retain_count(&data), 1);
|
||||
});
|
||||
}
|
||||
116
third-party/vendor/objc2/tests/no_prelude.rs
vendored
Normal file
116
third-party/vendor/objc2/tests/no_prelude.rs
vendored
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
//! Tests macros in a hostile environment (`#![no_implicit_prelude]` and all
|
||||
//! functions, modules, traits, and types replaced with custom bogus user
|
||||
//! replacements).
|
||||
//!
|
||||
//! Heavy inspiration for this file taken from `objrs`:
|
||||
//! https://gitlab.com/objrs/objrs/-/blob/b4f6598696b3fa622e6fddce7aff281770b0a8c2/test/src/no_prelude.rs
|
||||
#![no_implicit_prelude]
|
||||
#![allow(dead_code, non_camel_case_types)]
|
||||
|
||||
extern crate objc2 as new_objc2;
|
||||
|
||||
#[cfg(feature = "gnustep-1-7")]
|
||||
#[test]
|
||||
fn ensure_linkage() {
|
||||
unsafe { new_objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
}
|
||||
|
||||
mod core {}
|
||||
mod std {}
|
||||
mod libc {}
|
||||
mod objc2 {}
|
||||
|
||||
enum BogusType {}
|
||||
type u8 = BogusType;
|
||||
type u16 = BogusType;
|
||||
type u32 = BogusType;
|
||||
type u64 = BogusType;
|
||||
type u128 = BogusType;
|
||||
type usize = BogusType;
|
||||
|
||||
type i8 = BogusType;
|
||||
type i16 = BogusType;
|
||||
type i32 = BogusType;
|
||||
type i64 = BogusType;
|
||||
type i128 = BogusType;
|
||||
type isize = BogusType;
|
||||
|
||||
type bool = BogusType;
|
||||
type char = BogusType;
|
||||
type str = BogusType;
|
||||
|
||||
type f32 = BogusType;
|
||||
type f64 = BogusType;
|
||||
|
||||
type Option = BogusType;
|
||||
type Some = BogusType;
|
||||
type None = BogusType;
|
||||
|
||||
type Result = BogusType;
|
||||
type Ok = BogusType;
|
||||
type Err = BogusType;
|
||||
|
||||
type Box = BogusType;
|
||||
type String = BogusType;
|
||||
type Vec = BogusType;
|
||||
|
||||
type drop = BogusType;
|
||||
|
||||
type Copy = BogusType;
|
||||
type Send = BogusType;
|
||||
type Sized = BogusType;
|
||||
type Sync = BogusType;
|
||||
type Drop = BogusType;
|
||||
type Fn = BogusType;
|
||||
type FnMut = BogusType;
|
||||
type FnOnce = BogusType;
|
||||
|
||||
type ToOwned = BogusType;
|
||||
type Clone = BogusType;
|
||||
type PartialEq = BogusType;
|
||||
type PartialOrd = BogusType;
|
||||
type Eq = BogusType;
|
||||
type Ord = BogusType;
|
||||
type AsRef = BogusType;
|
||||
type AsMut = BogusType;
|
||||
type Into = BogusType;
|
||||
type From = BogusType;
|
||||
type Default = BogusType;
|
||||
type Hash = BogusType;
|
||||
type Debug = BogusType;
|
||||
type Iterator = BogusType;
|
||||
type Extend = BogusType;
|
||||
type IntoIterator = BogusType;
|
||||
type DoubleEndedIterator = BogusType;
|
||||
type ExactSizeIterator = BogusType;
|
||||
type SliceConcatExt = BogusType;
|
||||
type ToString = BogusType;
|
||||
|
||||
pub fn test_selector() {
|
||||
let _sel = new_objc2::sel!(abc);
|
||||
let _sel = new_objc2::sel!(abc:def:);
|
||||
}
|
||||
|
||||
pub fn test_class() {
|
||||
let _class = new_objc2::class!(NSObject);
|
||||
}
|
||||
|
||||
pub fn test_msg_send(obj: &new_objc2::foundation::NSString) {
|
||||
let superclass = obj.class().superclass().unwrap();
|
||||
let _: () = unsafe { new_objc2::msg_send![obj, a] };
|
||||
let _: () = unsafe { new_objc2::msg_send![obj, a: obj, b: obj] };
|
||||
let _: () = unsafe { new_objc2::msg_send![super(obj), a] };
|
||||
let _: () = unsafe { new_objc2::msg_send![super(obj), a: obj, b: obj] };
|
||||
let _: () = unsafe { new_objc2::msg_send![super(obj, superclass), a] };
|
||||
let _: () = unsafe { new_objc2::msg_send![super(obj, superclass), a: obj, b: obj] };
|
||||
}
|
||||
|
||||
pub fn test_msg_send_id(obj: &new_objc2::runtime::Object) {
|
||||
let _: new_objc2::rc::Id<new_objc2::runtime::Object, new_objc2::rc::Shared> =
|
||||
unsafe { new_objc2::msg_send_id![obj, a] };
|
||||
let _: new_objc2::__macro_helpers::Option<
|
||||
new_objc2::rc::Id<new_objc2::runtime::Object, new_objc2::rc::Shared>,
|
||||
> = unsafe { new_objc2::msg_send_id![obj, a] };
|
||||
let _: new_objc2::rc::Id<new_objc2::runtime::Object, new_objc2::rc::Shared> =
|
||||
unsafe { new_objc2::msg_send_id![obj, a: obj, b: obj] };
|
||||
}
|
||||
59
third-party/vendor/objc2/tests/use_macros.rs
vendored
Normal file
59
third-party/vendor/objc2/tests/use_macros.rs
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
use objc2::foundation::NSString;
|
||||
use objc2::runtime::{Class, Object};
|
||||
use objc2::{class, msg_send, sel};
|
||||
|
||||
#[cfg(feature = "gnustep-1-7")]
|
||||
#[test]
|
||||
fn ensure_linkage() {
|
||||
unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn use_class_and_msg_send() {
|
||||
unsafe {
|
||||
let cls = class!(NSObject);
|
||||
let obj: *mut Object = msg_send![cls, new];
|
||||
let _hash: usize = msg_send![obj, hash];
|
||||
let _: () = msg_send![obj, release];
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn use_sel() {
|
||||
let _sel = sel!(description);
|
||||
let _sel = sel!(setObject:forKey:);
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn test_msg_send_comma_handling(obj: &NSString, superclass: &Class) {
|
||||
unsafe {
|
||||
let _: () = msg_send![obj, a];
|
||||
let _: () = msg_send![obj, a,];
|
||||
let _: () = msg_send![obj, a: 32i32];
|
||||
let _: () = msg_send![obj, a: 32i32,];
|
||||
let _: () = msg_send![obj, a: 32i32 b: 32i32];
|
||||
let _: () = msg_send![obj, a: 32i32 b: 32i32,];
|
||||
let _: () = msg_send![obj, a: 32i32, b: 32i32];
|
||||
let _: () = msg_send![obj, a: 32i32, b: 32i32,];
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![super(obj, superclass), a];
|
||||
let _: () = msg_send![super(obj, superclass), a,];
|
||||
let _: () = msg_send![super(obj, superclass), a: 32i32];
|
||||
let _: () = msg_send![super(obj, superclass), a: 32i32,];
|
||||
let _: () = msg_send![super(obj, superclass), a: 32i32 b: 32i32];
|
||||
let _: () = msg_send![super(obj, superclass), a: 32i32 b: 32i32,];
|
||||
let _: () = msg_send![super(obj, superclass), a: 32i32, b: 32i32];
|
||||
let _: () = msg_send![super(obj, superclass), a: 32i32, b: 32i32,];
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![super(obj), a];
|
||||
let _: () = msg_send![super(obj), a,];
|
||||
let _: () = msg_send![super(obj), a: 32i32];
|
||||
let _: () = msg_send![super(obj), a: 32i32,];
|
||||
let _: () = msg_send![super(obj), a: 32i32, b: 32i32];
|
||||
let _: () = msg_send![super(obj), a: 32i32, b: 32i32,];
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue