Skip to content

Commit 62686bc

Browse files
authored
Merge pull request #108 from madsmtm/encoding-integration-test
Properly test that encodings match the Objective-C `@encode` output
2 parents 3a53d3e + befc118 commit 62686bc

File tree

9 files changed

+336
-26
lines changed

9 files changed

+336
-26
lines changed

objc2-encode/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

77
## Unreleased - YYYY-MM-DD
88

9+
### Added
10+
* Added `Encoding::LONG` and `Encoding::U_LONG` to help with platform
11+
compatibility; use these instead of `c_long::ENCODING` and
12+
`c_ulong::ENCODING`.
13+
914

1015
## 2.0.0-beta.2 - 2022-01-03
1116

objc2-encode/src/encoding.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,40 @@ pub enum Encoding<'a> {
134134
}
135135

136136
impl Encoding<'_> {
137+
/// The encoding of [`c_long`](`std::os::raw::c_long`).
138+
///
139+
/// Ideally the encoding of `long` would just be the same as `int` when
140+
/// it's 32 bits wide and the same as `long long` when it is 64 bits wide;
141+
/// then `c_long::ENCODING` would just work.
142+
///
143+
/// Unfortunately, `long` have a different encoding than `int` when it is
144+
/// 32 bits wide; the 'l'/'L' encoding.
145+
pub const LONG: Self = {
146+
// Alternative: `mem::size_of::<c_long>() == 4`
147+
// That would exactly match what `clang` does:
148+
// https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/AST/ASTContext.cpp#L7245
149+
if cfg!(any(target_pointer_width = "32", windows)) {
150+
// @encode(long) = 'l'
151+
Self::Long
152+
} else {
153+
// @encode(long) = 'q'
154+
Self::LongLong
155+
}
156+
};
157+
158+
/// The encoding of [`c_ulong`](`std::os::raw::c_ulong`).
159+
///
160+
/// See [`Encoding::LONG`] for explanation.
161+
pub const U_LONG: Self = {
162+
if cfg!(any(target_pointer_width = "32", windows)) {
163+
// @encode(unsigned long) = 'L'
164+
Encoding::ULong
165+
} else {
166+
// @encode(unsigned long) = 'Q'
167+
Encoding::ULongLong
168+
}
169+
};
170+
137171
/// Check if one encoding is equivalent to another.
138172
pub fn equivalent_to(&self, other: &Self) -> bool {
139173
// For now, because we don't allow representing qualifiers

objc2-encode/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@
7878
#[doc = include_str!("../README.md")]
7979
extern "C" {}
8080

81+
#[cfg(doc)]
82+
extern crate std;
83+
8184
#[cfg(any(test, doc))]
8285
extern crate alloc;
8386

objc2-foundation/src/enumerator.rs

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use core::marker::PhantomData;
22
use core::mem;
3-
use core::mem::size_of;
43
use core::ptr;
54
use core::ptr::NonNull;
65
use core::slice;
@@ -57,35 +56,14 @@ struct NSFastEnumerationState<T: INSObject> {
5756
extra: [c_ulong; 5],
5857
}
5958

60-
/// Ideally the encoding of `long` would just be the same as `int` when it's
61-
/// 32 bits wide and the same as `long long` when it's 64 bits wide; then
62-
/// `c_long::ENCODING` would just work.
63-
///
64-
/// Unfortunately, `long` and `unsigned long` have a different encoding than
65-
/// `int` when it's 32 bits wide; the 'l'/'L' encoding.
66-
const U_LONG_ENCODING: Encoding<'static> = {
67-
// We could also just have used:
68-
// #[cfg(any(target_pointer_width = "32", windows))]
69-
//
70-
// But this way we exactly match what `clang` does:
71-
// https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/AST/ASTContext.cpp#L7245
72-
if size_of::<c_ulong>() == 4 {
73-
// @encode(unsigned long) = 'L'
74-
Encoding::ULong
75-
} else {
76-
// @encode(unsigned long) = 'Q'
77-
Encoding::ULongLong
78-
}
79-
};
80-
8159
unsafe impl<T: INSObject> Encode for NSFastEnumerationState<T> {
8260
const ENCODING: Encoding<'static> = Encoding::Struct(
8361
"?",
8462
&[
85-
U_LONG_ENCODING,
63+
Encoding::U_LONG,
8664
Encoding::Pointer(&Encoding::Object), // <*const *const T>::ENCODING
87-
Encoding::Pointer(&U_LONG_ENCODING),
88-
Encoding::Array(5, &U_LONG_ENCODING),
65+
Encoding::Pointer(&Encoding::U_LONG),
66+
Encoding::Array(5, &Encoding::U_LONG),
8967
],
9068
);
9169
}

tests/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ build = "build.rs"
1212
[dependencies]
1313
block2 = { path = "../block2" }
1414
block-sys = { path = "../block-sys" }
15+
objc-sys = { path = "../objc-sys" }
16+
objc2 = { path = "../objc2" }
1517
objc2-encode = { path = "../objc2-encode" }
1618

1719
[build-dependencies]
1820
cc = "1.0"
21+
22+
[dev-dependencies]
23+
paste = "1.0"

tests/build.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::env;
22

33
fn main() {
44
println!("cargo:rerun-if-changed=extern/block_utils.c");
5+
println!("cargo:rerun-if-changed=extern/encode_utils.m");
56

67
let mut builder = cc::Build::new();
78
builder.compiler("clang");
@@ -12,4 +13,14 @@ fn main() {
1213
}
1314

1415
builder.compile("libblock_utils.a");
16+
17+
let mut builder = cc::Build::new();
18+
builder.compiler("clang");
19+
builder.file("extern/encode_utils.m");
20+
21+
for flag in env::var("DEP_OBJC_CC_ARGS").unwrap().split(' ') {
22+
builder.flag(flag);
23+
}
24+
25+
builder.compile("libencode_utils.a");
1526
}

tests/extern/encode_utils.m

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#include <objc/objc.h>
2+
#include <stdint.h>
3+
#include <stddef.h>
4+
// For NSInteger / NSUInteger. Linking is not required.
5+
#include <Foundation/NSObject.h>
6+
7+
#define ENCODING_INNER(name, type) char* ENCODING_ ## name = @encode(type);
8+
9+
#define ENCODING(name, type) \
10+
ENCODING_INNER(name, type); \
11+
ENCODING_INNER(name ## _POINTER, type*); \
12+
ENCODING_INNER(name ## _ATOMIC, _Atomic type);
13+
14+
// C types
15+
16+
ENCODING(C99_BOOL, _Bool);
17+
ENCODING(CHAR, char);
18+
ENCODING(SIGNED_CHAR, signed char);
19+
ENCODING(UNSIGNED_CHAR, unsigned char);
20+
ENCODING(SHORT, short);
21+
ENCODING(UNSIGNED_SHORT, unsigned short);
22+
ENCODING(INT, int);
23+
ENCODING(UNSIGNED_INT, unsigned int);
24+
ENCODING(LONG, long);
25+
ENCODING(UNSIGNED_LONG, unsigned long);
26+
ENCODING(LONG_LONG, long long);
27+
ENCODING(UNSIGNED_LONG_LONG, unsigned long long);
28+
ENCODING(FLOAT, float);
29+
ENCODING(DOUBLE, double);
30+
ENCODING(LONG_DOUBLE, long double);
31+
32+
ENCODING(FLOAT_COMPLEX, float _Complex);
33+
ENCODING(DOUBLE_COMPLEX, double _Complex);
34+
ENCODING(LONG_DOUBLE_COMPLEX, long double _Complex);
35+
// TODO: Enable these:
36+
// ENCODING(FLOAT_IMAGINARY, float _Imaginary);
37+
// ENCODING(DOUBLE_IMAGINARY, double _Imaginary);
38+
// ENCODING(LONG_DOUBLE_IMAGINARY, long double _Imaginary);
39+
40+
ENCODING_INNER(VOID, void);
41+
ENCODING_INNER(VOID_POINTER, void*);
42+
ENCODING_INNER(VOID_POINTER_CONST, const void*);
43+
ENCODING_INNER(VOID_POINTER_POINTER, void**);
44+
45+
// Array
46+
47+
ENCODING_INNER(INT_ARRAY, int[10]);
48+
ENCODING_INNER(INT_ARRAY_POINTER, int*[10]);
49+
ENCODING_INNER(INT_ARRAY_ATOMIC, _Atomic int[10]);
50+
51+
// Struct
52+
53+
// TODO: Structs and such
54+
55+
// Objective-C
56+
57+
ENCODING(OBJC_BOOL, BOOL);
58+
ENCODING_INNER(ID, id);
59+
ENCODING_INNER(ID_POINTER, const id*);
60+
ENCODING_INNER(ID_ATOMIC, _Atomic id);
61+
ENCODING(CLASS, Class);
62+
ENCODING(SEL, SEL);
63+
ENCODING(NS_INTEGER, NSInteger);
64+
ENCODING(NS_UINTEGER, NSUInteger);
65+
66+
// stdint.h
67+
68+
ENCODING(INT8, int8_t);
69+
ENCODING(INT16, int16_t);
70+
ENCODING(INT32, int32_t);
71+
ENCODING(INT64, int64_t);
72+
ENCODING(INTPTR, intptr_t);
73+
ENCODING(UINT8, uint8_t);
74+
ENCODING(UINT16, uint16_t);
75+
ENCODING(UINT32, uint32_t);
76+
ENCODING(UINT64, uint64_t);
77+
ENCODING(UINTPTR, uintptr_t);
78+
79+
// stddef.h
80+
81+
ENCODING(SIZE_T, size_t);
82+
ENCODING(PTRDIFF_T, ptrdiff_t);
83+
84+
// Possible extras
85+
86+
#if __has_builtin(__int128_t)
87+
ENCODING(SIGNED_INT_128, __int128_t);
88+
ENCODING(UNSIGNED_INT_128, __uint128_t);
89+
#endif

tests/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
#![no_std]
55
use block2::{Block, RcBlock};
66

7-
#[cfg(test)]
87
extern crate alloc;
8+
extern crate std;
99

1010
pub mod ffi;
11+
#[cfg(test)]
12+
mod test_encode_utils;
1113
use crate::ffi::LargeStruct;
1214

1315
pub fn get_int_block_with(i: i32) -> RcBlock<(), i32> {

0 commit comments

Comments
 (0)