Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unstable: &'static str from Encoding #70

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions crates/icrate/src/additions/Foundation/value.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
#![cfg(feature = "Foundation_NSValue")]
use alloc::string::ToString;
use core::fmt;
use core::hash;
use core::mem::MaybeUninit;
use core::str;
use std::ffi::{CStr, CString};
use std::ffi::CStr;

use objc2::encode::Encode;

Expand Down Expand Up @@ -35,7 +34,7 @@ impl NSValue {
/// [`NSPoint`]: crate::Foundation::NSPoint
pub fn new<T: 'static + Copy + Encode>(value: T) -> Id<Self> {
let bytes: NonNull<T> = NonNull::from(&value);
let encoding = CString::new(T::ENCODING.to_string()).unwrap();
let encoding = T::ENCODING_CSTR;
unsafe {
Self::initWithBytes_objCType(
Self::alloc(),
Expand Down
3 changes: 3 additions & 0 deletions crates/objc2-encode/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## Unreleased - YYYY-MM-DD

### Added
* Added `Encode::ENCODING_CSTR` for statically generating an encoding string.


## 3.0.0 - 2023-07-31

Expand Down
11 changes: 11 additions & 0 deletions crates/objc2-encode/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use core::fmt;

use crate::helper::{compare_encodings, Helper, NestingLevel};
use crate::parse::Parser;
use crate::static_str::{static_encoding_str_array, static_encoding_str_len};
use crate::EncodingBox;

/// An Objective-C type-encoding.
Expand Down Expand Up @@ -181,6 +182,16 @@ impl Encoding {
}
};

/// TODO
pub const fn static_encoding_str_len(&self) -> usize {
static_encoding_str_len(self, NestingLevel::new())
}

/// TODO
pub const fn static_encoding_str_array<const LEN: usize>(&self) -> [u8; LEN] {
static_encoding_str_array(self, NestingLevel::new())
}

/// Check if one encoding is equivalent to another.
///
/// Currently, equivalence testing mostly requires that the encodings are
Expand Down
3 changes: 0 additions & 3 deletions crates/objc2-encode/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,6 @@ mod encoding;
mod encoding_box;
mod helper;
mod parse;

// Will be used at some point when generic constants are available
#[allow(dead_code)]
mod static_str;

pub use self::encoding::Encoding;
Expand Down
17 changes: 5 additions & 12 deletions crates/objc2/src/declare/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ mod ivar_encode;
mod ivar_forwarding_impls;

use alloc::format;
use alloc::string::ToString;
use core::ffi::CStr;
use core::mem;
use core::mem::ManuallyDrop;
use core::ptr;
Expand Down Expand Up @@ -550,17 +550,11 @@ impl ClassBuilder {
/// 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 { self.add_ivar_inner::<T>(name, &T::ENCODING_CSTR) }
}

// Monomorphized version
unsafe fn add_ivar_inner_mono(
&mut self,
name: &str,
size: usize,
align: u8,
encoding: &Encoding,
) {
unsafe fn add_ivar_inner_mono(&mut self, name: &str, size: usize, align: u8, encoding: &CStr) {
// `class_addIvar` sadly doesn't check this for us.
//
// We must _always_ do the check, since there is no way for the user
Expand All @@ -575,7 +569,6 @@ impl ClassBuilder {
}

let c_name = CString::new(name).unwrap();
let encoding = CString::new(encoding.to_string()).unwrap();
let success = Bool::from_raw(unsafe {
ffi::class_addIvar(
self.as_mut_ptr(),
Expand All @@ -588,7 +581,7 @@ impl ClassBuilder {
assert!(success.as_bool(), "failed to add ivar {name}");
}

unsafe fn add_ivar_inner<T>(&mut self, name: &str, encoding: &Encoding) {
unsafe fn add_ivar_inner<T>(&mut self, name: &str, encoding: &CStr) {
unsafe { self.add_ivar_inner_mono(name, mem::size_of::<T>(), T::LOG2_ALIGNMENT, encoding) }
}

Expand All @@ -600,7 +593,7 @@ impl ClassBuilder {
/// 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>(T::NAME, &T::Type::ENCODING) }
unsafe { self.add_ivar_inner::<T::Type>(T::NAME, &T::Type::ENCODING_CSTR) }
}

/// Adds the given protocol to self.
Expand Down
75 changes: 75 additions & 0 deletions crates/objc2/src/encode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ use core::num::{
};
use core::ptr::NonNull;
use core::sync::atomic;
use std::ffi::CStr;

// Intentionally not `#[doc(hidden)]`
pub mod __unstable;
Expand Down Expand Up @@ -112,6 +113,8 @@ pub use objc2_encode::{Encoding, EncodingBox, ParseError};
/// passed to Objective-C via. `objc2::msg_send!` their destructor will not be
/// called!
///
/// Finally, you must not override [`ENCODING_CSTR`][Self::ENCODING_CSTR].
///
///
/// # Examples
///
Expand Down Expand Up @@ -149,6 +152,43 @@ pub use objc2_encode::{Encoding, EncodingBox, ParseError};
pub unsafe trait Encode {
/// The Objective-C type-encoding for this type.
const ENCODING: Encoding;

#[doc(hidden)]
const __ENCODING_CSTR_LEN: usize = Self::ENCODING.static_encoding_str_len();

#[doc(hidden)]
const __ENCODING_CSTR_ARRAY: [u8; 128] = {
if Self::__ENCODING_CSTR_LEN >= 127 {
panic!("encoding string was too long! The maximum supported length is 1023.");
}

Self::ENCODING.static_encoding_str_array()
};

/// The encoding as a static [`CStr`].
///
/// This has the same output as `Encoding::to_string`, but it is created
/// at compile-time instead.
///
/// The encoding is guaranteed to be a pure ASCII string.
#[cfg(feature = "std")]
const ENCODING_CSTR: &'static CStr = {
let mut slice: &[u8] = &Self::__ENCODING_CSTR_ARRAY;
// Cut down to desired size (length + 1 for NUL byte)
// Equivalent to:
// slice[0..Self::__ENCODING_CSTR_LEN + 1]
while slice.len() > Self::__ENCODING_CSTR_LEN + 1 {
if let Some(res) = slice.split_last() {
slice = res.1;
} else {
unreachable!();
}
}
// SAFETY: `static_encoding_str_array` is guaranteed to not contain
// any NULL bytes (the only place those could appear would be in a
// struct or union name, and that case is checked).
unsafe { CStr::from_bytes_with_nul_unchecked(slice) }
};
}

/// Types whoose references has an Objective-C type-encoding.
Expand Down Expand Up @@ -768,4 +808,39 @@ mod tests {
impls_encode(my_fn3 as extern "C" fn(_) -> _);
impls_encode(my_fn4 as extern "C" fn(_, _) -> _);
}

#[test]
#[cfg(feature = "std")]
fn test_cstr_simple() {
assert_eq!(i8::__ENCODING_CSTR_LEN, 1);

let mut array = [0; 128];
array[0] = b'c';
assert_eq!(i8::__ENCODING_CSTR_ARRAY, array);

let cstr = CStr::from_bytes_with_nul(b"c\0").unwrap();
assert_eq!(i8::ENCODING_CSTR, cstr);
}

#[test]
#[cfg(feature = "std")]
fn test_cstr() {
struct X;

unsafe impl Encode for X {
const ENCODING: Encoding = Encoding::Struct(
"abc",
&[
Encoding::Union("def", &[Encoding::Char]),
<*const *const i8>::ENCODING,
<AtomicPtr<AtomicI16>>::ENCODING,
<extern "C" fn()>::ENCODING,
],
);
}

let s = b"{abc=(def=c)^*A^As^?}\0";

assert_eq!(X::ENCODING_CSTR.to_bytes_with_nul(), s);
}
}
6 changes: 6 additions & 0 deletions crates/test-ui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@

fn main() {
let t = trybuild::TestCases::new();

let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("ui")
.join("*.rs");
t.compile_fail(path);

let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("ui-ignore")
.join("*.rs");
t.pass(path);
}
24 changes: 20 additions & 4 deletions crates/tests/src/test_encode_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ unsafe fn assert_encoding(s: *const c_char, e: Encoding) {
assert_eq!(e.to_string(), s.trim_start_matches('r'));
}

unsafe fn assert_ty<T: Encode>(s: *const c_char) {
assert_encoding(s, T::ENCODING);
// To ensure ENCODING_CSTR is implemented correctly
assert_eq!(T::ENCODING_CSTR.to_str().unwrap(), T::ENCODING.to_string());
}

#[allow(unused)]
unsafe fn assert_str<T: Display>(s: *const c_char, expected: T) {
let s = CStr::from_ptr(s).to_str().unwrap();
Expand All @@ -38,6 +44,16 @@ macro_rules! assert_inner {
unsafe { assert_encoding($stat, $expected) };
}
};
(ty $(#[$m:meta])* $stat:ident => $expected:ty) => {
$(#[$m])*
#[test]
fn $stat() {
extern "C" {
static $stat: *const c_char;
}
unsafe { assert_ty::<$expected>($stat) };
}
};
(str $(#[$m:meta])* $stat:ident => $expected:expr) => {
$(#[$m])*
#[test]
Expand Down Expand Up @@ -77,10 +93,10 @@ macro_rules! assert_types {
$stat:ident $($should_atomic:ident)? => $type:ident
) => {
paste! {
assert_inner!(enc $(#[$m])* [<ENCODING_ $stat>] => <$type>::ENCODING);
assert_inner!(enc $(#[$m])* [<ENCODING_ $stat _POINTER>] => <*const $type>::ENCODING);
assert_inner!(enc $(#[$m])* [<ENCODING_ $stat _POINTER_POINTER>] => <*const *const $type>::ENCODING);
assert_inner!(enc $(#[$m])* [<ENCODING_ $stat _POINTER_POINTER_POINTER>] => <*const *const *const $type>::ENCODING);
assert_inner!(ty $(#[$m])* [<ENCODING_ $stat>] => $type);
assert_inner!(ty $(#[$m])* [<ENCODING_ $stat _POINTER>] => *const $type);
assert_inner!(ty $(#[$m])* [<ENCODING_ $stat _POINTER_POINTER>] => *const *const $type);
assert_inner!(ty $(#[$m])* [<ENCODING_ $stat _POINTER_POINTER_POINTER>] => *const *const *const $type);
$(assert_types!(#$should_atomic);)?
assert_inner!(enc $(#[$m])* $(#[cfg($should_atomic)])? [<ENCODING_ $stat _ATOMIC>] => Encoding::Atomic(&<$type>::ENCODING));
assert_inner!(enc $(#[$m])* $(#[cfg($should_atomic)])? [<ENCODING_ $stat _ATOMIC_POINTER>] => Encoding::Pointer(&Encoding::Atomic(&<$type>::ENCODING)));
Expand Down
26 changes: 26 additions & 0 deletions test-assembly/crates/test_encoding_cstr/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "test_encoding_cstr"
version = "0.1.0"
edition = "2021"
publish = false

[lib]
path = "lib.rs"

[dependencies]
objc2-encode = { path = "../../../objc2-encode", default-features = false }

[features]
default = ["apple", "std"]
std = ["objc2-encode/std"]

# Runtime
apple = []
gnustep-1-7 = []
gnustep-1-8 = ["gnustep-1-7"]
gnustep-1-9 = ["gnustep-1-8"]
gnustep-2-0 = ["gnustep-1-9"]
gnustep-2-1 = ["gnustep-2-0"]

# Hack
assembly-features = ["std"]
13 changes: 13 additions & 0 deletions test-assembly/crates/test_encoding_cstr/expected/apple-aarch64.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.section __TEXT,__text,regular,pure_instructions
.section __TEXT,__const
l_anon.a88231c846af3b75605317c1ca346ede.0:
.asciz "c\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"

.section __DATA,__const
.globl _ENC
.p2align 3
_ENC:
.quad l_anon.a88231c846af3b75605317c1ca346ede.0
.asciz "\002\000\000\000\000\000\000"

.subsections_via_symbols
14 changes: 14 additions & 0 deletions test-assembly/crates/test_encoding_cstr/expected/apple-armv7.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.section __TEXT,__text,regular,pure_instructions
.syntax unified
.section __TEXT,__const
l_anon.a88231c846af3b75605317c1ca346ede.0:
.asciz "c\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"

.section __DATA,__const
.globl _ENC
.p2align 2
_ENC:
.long l_anon.a88231c846af3b75605317c1ca346ede.0
.asciz "\002\000\000"

.subsections_via_symbols
14 changes: 14 additions & 0 deletions test-assembly/crates/test_encoding_cstr/expected/apple-armv7s.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.section __TEXT,__text,regular,pure_instructions
.syntax unified
.section __TEXT,__const
l_anon.a88231c846af3b75605317c1ca346ede.0:
.asciz "c\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"

.section __DATA,__const
.globl _ENC
.p2align 2
_ENC:
.long l_anon.a88231c846af3b75605317c1ca346ede.0
.asciz "\002\000\000"

.subsections_via_symbols
14 changes: 14 additions & 0 deletions test-assembly/crates/test_encoding_cstr/expected/apple-x86.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.section __TEXT,__text,regular,pure_instructions
.intel_syntax noprefix
.section __TEXT,__const
l_anon.a88231c846af3b75605317c1ca346ede.0:
.asciz "c\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"

.section __DATA,__const
.globl _ENC
.p2align 2
_ENC:
.long l_anon.a88231c846af3b75605317c1ca346ede.0
.asciz "\002\000\000"

.subsections_via_symbols
14 changes: 14 additions & 0 deletions test-assembly/crates/test_encoding_cstr/expected/apple-x86_64.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.section __TEXT,__text,regular,pure_instructions
.intel_syntax noprefix
.section __TEXT,__const
l_anon.a88231c846af3b75605317c1ca346ede.0:
.asciz "c\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"

.section __DATA,__const
.globl _ENC
.p2align 3
_ENC:
.quad l_anon.a88231c846af3b75605317c1ca346ede.0
.asciz "\002\000\000\000\000\000\000"

.subsections_via_symbols
Loading