Skip to content

Commit d857e90

Browse files
authored
Merge pull request #14 from madsmtm/encode-bounds
Add `RefEncode`, and make `Encode` bounds required
2 parents 66c7ddf + 1600f6e commit d857e90

File tree

17 files changed

+602
-301
lines changed

17 files changed

+602
-301
lines changed

.github/workflows/ci.yml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ jobs:
7575
uses: actions-rs/cargo@v1
7676
with:
7777
command: check
78-
args: --verbose
78+
args: --verbose --no-default-features
7979

8080
- name: Install GNUStep libobjc2
8181
if: contains(matrix.platform.os, 'ubuntu')
@@ -116,10 +116,16 @@ jobs:
116116
echo "LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH" >> $GITHUB_ENV
117117
echo "CPATH=/usr/local/include:$CPATH" >> $GITHUB_ENV
118118
119-
- name: Build and run tests
119+
- name: Test
120120
uses: actions-rs/cargo@v1
121121
with:
122122
command: test
123-
# TODO: `objc/exception` feature is broken in objc_foundation
124-
# TODO: `objc_foundation/block` feature doesn't work on GNUStep
123+
# TODO: `objc_foundation/block` feature doesn't work
125124
args: --verbose --no-fail-fast --no-default-features
125+
126+
- name: Test w. exception and verify_message features
127+
uses: actions-rs/cargo@v1
128+
with:
129+
command: test
130+
# TODO: `objc_foundation/block` feature doesn't work
131+
args: --verbose --no-fail-fast --no-default-features --features exception,verify_message

objc/src/encode.rs

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,23 @@
11
use crate::runtime::{Class, Object, Sel};
2-
use crate::{Encode, Encoding};
2+
use crate::{Encode, Encoding, RefEncode};
33

44
unsafe impl Encode for Sel {
55
const ENCODING: Encoding<'static> = Encoding::Sel;
66
}
77

8-
unsafe impl<'a> Encode for &'a Object {
9-
const ENCODING: Encoding<'static> = Encoding::Object;
8+
unsafe impl RefEncode for Object {
9+
const ENCODING_REF: Encoding<'static> = Encoding::Object;
1010
}
1111

12-
unsafe impl<'a> Encode for &'a mut Object {
13-
const ENCODING: Encoding<'static> = Encoding::Object;
12+
unsafe impl RefEncode for Class {
13+
const ENCODING_REF: Encoding<'static> = Encoding::Class;
1414
}
1515

16-
unsafe impl<'a> Encode for &'a Class {
17-
const ENCODING: Encoding<'static> = Encoding::Class;
18-
}
19-
20-
unsafe impl<'a> Encode for &'a mut Class {
21-
const ENCODING: Encoding<'static> = Encoding::Class;
22-
}
23-
24-
/// Types that represent a group of arguments, where each has an Objective-C
25-
/// type-encoding.
26-
pub trait EncodeArguments {
27-
/// The type as which the encodings for Self will be returned.
28-
const ENCODINGS: &'static [Encoding<'static>];
29-
}
30-
31-
macro_rules! encode_args_impl {
32-
($($t:ident),*) => (
33-
impl<$($t: Encode),*> EncodeArguments for ($($t,)*) {
34-
const ENCODINGS: &'static [Encoding<'static>] = &[
35-
$($t::ENCODING),*
36-
];
37-
}
38-
);
39-
}
40-
41-
encode_args_impl!();
42-
encode_args_impl!(A);
43-
encode_args_impl!(A, B);
44-
encode_args_impl!(A, B, C);
45-
encode_args_impl!(A, B, C, D);
46-
encode_args_impl!(A, B, C, D, E);
47-
encode_args_impl!(A, B, C, D, E, F);
48-
encode_args_impl!(A, B, C, D, E, F, G);
49-
encode_args_impl!(A, B, C, D, E, F, G, H);
50-
encode_args_impl!(A, B, C, D, E, F, G, H, I);
51-
encode_args_impl!(A, B, C, D, E, F, G, H, I, J);
52-
encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K);
53-
encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K, L);
54-
5516
#[cfg(test)]
5617
mod tests {
5718
use crate::runtime::{Class, Object, Sel};
19+
use crate::Encode;
5820
use alloc::string::ToString;
59-
use objc_encode::Encode;
6021

6122
#[test]
6223
fn test_encode() {

objc/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,8 @@ extern "C" {}
7575
#[doc = include_str!("../../README.md")]
7676
extern "C" {}
7777

78-
pub use objc_encode::{Encode, Encoding};
78+
pub use objc_encode::{Encode, EncodeArguments, Encoding, RefEncode};
7979

80-
pub use crate::encode::EncodeArguments;
8180
pub use crate::message::{Message, MessageArguments, MessageError};
8281

8382
pub use crate::cache::CachedClass as __CachedClass;

objc/src/message/mod.rs

Lines changed: 26 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@ use core::mem;
55
use std::error::Error;
66

77
use crate::runtime::{Class, Imp, Object, Sel};
8-
use crate::{Encode, EncodeArguments};
8+
use crate::{Encode, EncodeArguments, RefEncode};
99

1010
#[cfg(feature = "exception")]
1111
macro_rules! objc_try {
1212
($b:block) => {
1313
$crate::exception::catch_exception(|| $b).map_err(|exception| {
14+
use alloc::borrow::ToOwned;
1415
if exception.is_null() {
1516
MessageError("Uncaught exception nil".to_owned())
1617
} else {
17-
MessageError(format!("Uncaught exception {:?}", &**exception))
18+
MessageError(alloc::format!("Uncaught exception {:?}", &**exception))
1819
}
1920
})
2021
};
@@ -52,18 +53,19 @@ struct Super {
5253
///
5354
/// Examples include objects, classes, and blocks.
5455
///
55-
/// The type should also implement [`Encode`] for `&Self` and `&mut Self`.
56+
/// Implementing this allows using pointers and references to the type as the
57+
/// receiver (first argument) in the [`msg_send!`][`crate::msg_send`] macro.
5658
///
5759
/// # Safety
5860
///
61+
/// The type must implement [`RefEncode`] and adhere to the safety guidelines
62+
/// therein.
63+
///
5964
/// A pointer to the type must be able to be the receiver of an Objective-C
6065
/// message sent with [`objc_msgSend`] or similar.
6166
///
62-
/// The type must also have a C-compatible `repr` (`repr(C)`, `repr(u8)`,
63-
/// `repr(transparent)` where the inner types are C-compatible, and so on).
64-
///
6567
/// [`objc_msgSend`]: https://developer.apple.com/documentation/objectivec/1456712-objc_msgsend
66-
pub unsafe trait Message {
68+
pub unsafe trait Message: RefEncode {
6769
/**
6870
Sends a message to self with the given selector and arguments.
6971
@@ -74,18 +76,6 @@ pub unsafe trait Message {
7476
If the selector is known at compile-time, it is recommended to use the
7577
`msg_send!` macro rather than this method.
7678
*/
77-
#[cfg(not(feature = "verify_message"))]
78-
unsafe fn send_message<A, R>(&self, sel: Sel, args: A) -> Result<R, MessageError>
79-
where
80-
Self: Sized,
81-
A: MessageArguments,
82-
R: Any,
83-
{
84-
send_message(self, sel, args)
85-
}
86-
87-
#[allow(missing_docs)]
88-
#[cfg(feature = "verify_message")]
8979
unsafe fn send_message<A, R>(&self, sel: Sel, args: A) -> Result<R, MessageError>
9080
where
9181
Self: Sized,
@@ -226,56 +216,28 @@ impl<'a> From<VerificationError<'a>> for MessageError {
226216
}
227217

228218
#[doc(hidden)]
229-
#[inline(always)]
230-
#[cfg(not(feature = "verify_message"))]
231-
pub unsafe fn send_message<T, A, R>(obj: *const T, sel: Sel, args: A) -> Result<R, MessageError>
232-
where
233-
T: Message,
234-
A: MessageArguments,
235-
R: Any,
236-
{
237-
send_unverified(obj, sel, args)
238-
}
239-
240-
#[doc(hidden)]
241-
#[inline(always)]
242-
#[cfg(feature = "verify_message")]
219+
#[cfg_attr(feature = "verify_message", inline(always))]
243220
pub unsafe fn send_message<T, A, R>(obj: *const T, sel: Sel, args: A) -> Result<R, MessageError>
244221
where
245222
T: Message,
246223
A: MessageArguments + EncodeArguments,
247224
R: Any + Encode,
248225
{
249-
let cls = if obj.is_null() {
250-
return Err(VerificationError::NilReceiver(sel).into());
251-
} else {
252-
(*(obj as *const Object)).class()
253-
};
226+
#[cfg(feature = "verify_message")]
227+
{
228+
let cls = if obj.is_null() {
229+
return Err(VerificationError::NilReceiver(sel).into());
230+
} else {
231+
(*(obj as *const Object)).class()
232+
};
254233

255-
verify_message_signature::<A, R>(cls, sel)?;
234+
verify_message_signature::<A, R>(cls, sel)?;
235+
}
256236
send_unverified(obj, sel, args)
257237
}
258238

259239
#[doc(hidden)]
260-
#[inline(always)]
261-
#[cfg(not(feature = "verify_message"))]
262-
pub unsafe fn send_super_message<T, A, R>(
263-
obj: *const T,
264-
superclass: &Class,
265-
sel: Sel,
266-
args: A,
267-
) -> Result<R, MessageError>
268-
where
269-
T: Message,
270-
A: MessageArguments,
271-
R: Any,
272-
{
273-
send_super_unverified(obj, superclass, sel, args)
274-
}
275-
276-
#[doc(hidden)]
277-
#[inline(always)]
278-
#[cfg(feature = "verify_message")]
240+
#[cfg_attr(feature = "verify_message", inline(always))]
279241
pub unsafe fn send_super_message<T, A, R>(
280242
obj: *const T,
281243
superclass: &Class,
@@ -287,11 +249,13 @@ where
287249
A: MessageArguments + EncodeArguments,
288250
R: Any + Encode,
289251
{
290-
if obj.is_null() {
291-
return Err(VerificationError::NilReceiver(sel).into());
252+
#[cfg(feature = "verify_message")]
253+
{
254+
if obj.is_null() {
255+
return Err(VerificationError::NilReceiver(sel).into());
256+
}
257+
verify_message_signature::<A, R>(superclass, sel)?;
292258
}
293-
294-
verify_message_signature::<A, R>(superclass, sel)?;
295259
send_super_unverified(obj, superclass, sel, args)
296260
}
297261

objc_encode/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
[package]
22
name = "objc-encode"
3-
version = "1.1.0" # Remember to update html_root_url in lib.rs
3+
# Remember to update html_root_url in lib.rs and README.md
4+
version = "1.1.0"
45
authors = ["Steven Sheldon", "Mads Marquart <[email protected]>"]
56
edition = "2018"
67

7-
description = "Objective-C type encoding creation and parsing."
8+
description = "Objective-C type-encodings"
89
keywords = ["objective-c", "macos", "ios", "encode"]
910
categories = [
1011
"development-tools::ffi",

objc_encode/README.md

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,83 @@
1-
# `objc-encode`
1+
# `objc-encode` - Objective-C type-encoding in Rust
22

33
[![Latest version](https://badgen.net/crates/v/objc-encode)](https://crates.io/crates/objc-encode)
44
[![License](https://badgen.net/badge/license/MIT/blue)](../LICENSE.txt)
55
[![Documentation](https://docs.rs/objc-encode/badge.svg)](https://docs.rs/objc-encode/)
66
[![CI Status](https://github.com/madsmtm/objc/workflows/CI/badge.svg)](https://github.com/madsmtm/objc/actions)
77

8-
Objective-C type encoding creation and parsing in Rust.
8+
The Objective-C directive `@encode` encodes types as strings for usage in
9+
various places in the runtime.
910

10-
The Objective-C compiler encodes types as strings for usage in the runtime.
11-
This crate aims to provide a strongly-typed (rather than stringly-typed) way
12-
to create and describe these type encodings without memory allocation in Rust.
11+
This crate provides the `Encoding` type to describe and compare these
12+
type-encodings without memory allocation.
1313

14+
Additionally it provides traits for annotating types that has a corresponding
15+
Objective-C encoding, respectively `Encode` for structs and `RefEncode` for
16+
references (and `EncodeArguments` for function arguments).
1417

15-
## Implementing Encode
18+
These types are exported under the `objc` crate as well, so usually you would
19+
just use that crate.
1620

17-
This crate declares an `Encode` trait that can be implemented for types that
18-
the Objective-C compiler can encode. Implementing this trait looks like:
21+
# Examples
1922

20-
```rust
21-
use objc::{Encode, Encoding};
22-
23-
#[cfg(target_pointer_width = "32")]
24-
type CGFloat = f32;
23+
Implementing `Encode` and `RefEncode`:
2524

26-
#[cfg(target_pointer_width = "64")]
27-
type CGFloat = f64;
25+
```rust
26+
use objc_encode::{Encode, Encoding, RefEncode};
2827

2928
#[repr(C)]
30-
struct CGPoint {
31-
x: CGFloat,
32-
y: CGFloat,
29+
struct MyObject {
30+
a: f32,
31+
b: bool,
3332
}
3433

35-
unsafe impl Encode for CGPoint {
36-
const ENCODING: Encoding<'static> =
37-
Encoding::Struct("CGPoint", &[CGFloat::ENCODING, CGFloat::ENCODING]);
34+
unsafe impl Encode for MyObject {
35+
const ENCODING: Encoding<'static> = Encoding::Struct(
36+
"MyObject",
37+
&[f32::ENCODING, bool::ENCODING
38+
]);
3839
}
39-
```
4040

41-
For an example of how this works with more complex types, like structs
42-
containing structs, see the `core_graphics` example.
41+
assert_eq!(&MyObject::ENCODING, "{MyObject=fB}");
4342

44-
## Comparing with encoding strings
43+
unsafe impl RefEncode for MyObject {
44+
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
45+
}
46+
47+
assert_eq!(&MyObject::ENCODING_REF, "^{MyObject=fB}");
48+
```
4549

4650
An `Encoding` can be compared with an encoding string from the Objective-C
4751
runtime:
4852

4953
```rust
5054
use objc::Encode;
51-
5255
assert!(&i32::ENCODING == "i");
5356
```
5457

55-
## Generating encoding strings
56-
57-
Every `Encoding` implements `Display` as its string representation.
58-
This can be generated conveniently through the `to_string` method:
58+
`Encoding` implements `Display` as its string representation. This can be
59+
generated conveniently through the `to_string` method:
5960

6061
```rust
6162
use objc::Encode;
62-
6363
assert_eq!(i32::ENCODING.to_string(), "i");
6464
```
65+
66+
See the [`examples`] folder for more complex usage.
67+
68+
# Installation
69+
70+
```toml
71+
[dependencies]
72+
objc-encode = "1.1.0"
73+
```
74+
75+
# License
76+
77+
This project is licensed under the MIT license, see [`../LICENSE.txt`].
78+
79+
Work is in progress to make it dual-licensed under the Apache License
80+
(Version 2.0) as well.
81+
82+
[`examples`]: https://github.com/madsmtm/objc/tree/master/objc_encode/examples
83+
[`../LICENSE.txt`]: https://github.com/madsmtm/objc/blob/master/LICENSE.txt

objc_encode/examples/core_graphics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use objc_encode::{Encode, Encoding};
1+
use objc::{Encode, Encoding};
22

33
#[cfg(target_pointer_width = "32")]
44
type CGFloat = f32;

0 commit comments

Comments
 (0)