Skip to content

Commit 4ec5b64

Browse files
authored
Merge pull request #526 from madsmtm/future-proof-struct-encodings
Future-proof struct encodings
2 parents aa67e05 + d49a190 commit 4ec5b64

File tree

8 files changed

+233
-214
lines changed

8 files changed

+233
-214
lines changed

crates/objc2-encode/CHANGELOG.md

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

77
## Unreleased - YYYY-MM-DD
88

9+
### Changed
10+
* **BREAKING**: Changed the type of `EncodingBox::Struct` and
11+
`EncodingBox::Union` to no longer contain an `Option`.
12+
13+
### Fixed
14+
* Fixed encoding equivalence between empty structs and unions.
15+
916

1017
## 3.0.0 - 2023-07-31
1118

crates/objc2-encode/src/encoding.rs

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ pub enum Encoding {
104104
/// are possible for the type.
105105
///
106106
/// A `BitField(_, Some(_))` and a `BitField(_, None)` do _not_ compare
107-
/// equal; instead, you should set the bitfield depending on the target
108-
/// platform.
107+
/// equal; instead, you should set the bitfield correctly depending on the
108+
/// target platform.
109109
BitField(u8, Option<&'static (u64, Encoding)>),
110110
/// A pointer to the given type.
111111
///
@@ -129,12 +129,18 @@ pub enum Encoding {
129129
/// It is not uncommon for the name to be `"?"`.
130130
///
131131
/// Corresponds to the `"{" name "=" fields... "}"` code.
132+
///
133+
/// Note that the `=` may be omitted in some situations; this is
134+
/// considered equal to the case where there are no fields.
132135
Struct(&'static str, &'static [Encoding]),
133-
/// A union with the given name and fields.
136+
/// A union with the given name and members.
134137
///
135-
/// The order of the fields must match the order of the order in this.
138+
/// The order of the members must match the order of the order in this.
139+
///
140+
/// Corresponds to the `"(" name "=" members... ")"` code.
136141
///
137-
/// Corresponds to the `"(" name "=" fields... ")"` code.
142+
/// Note that the `=` may be omitted in some situations; this is
143+
/// considered equal to the case where there are no members.
138144
Union(&'static str, &'static [Encoding]),
139145
// TODO: "Vector" types have the '!' encoding, but are not implemented in
140146
// clang
@@ -189,6 +195,9 @@ impl Encoding {
189195
/// - Structs or unions behind multiple pointers are considered
190196
/// equivalent, since Objective-C compilers strip this information to
191197
/// avoid unnecessary nesting.
198+
/// - Structs or unions with no fields/members are considered to represent
199+
/// "opqaue" types, and will therefore be equivalent to all other
200+
/// structs / unions.
192201
///
193202
/// The comparison may be changed in the future to e.g. ignore struct
194203
/// names or similar changes that may be required because of limitations
@@ -197,7 +206,7 @@ impl Encoding {
197206
/// For example, you should not rely on two equivalent encodings to have
198207
/// the same size or ABI - that is provided on a best-effort basis.
199208
pub fn equivalent_to(&self, other: &Self) -> bool {
200-
compare_encodings(self, NestingLevel::new(), other, NestingLevel::new(), false)
209+
compare_encodings(self, other, NestingLevel::new(), false)
201210
}
202211

203212
/// Check if an encoding is equivalent to the given string representation.
@@ -223,7 +232,7 @@ impl Encoding {
223232
/// See [`Encoding::equivalent_to`] for details about the meaning of
224233
/// "equivalence".
225234
pub fn equivalent_to_box(&self, other: &EncodingBox) -> bool {
226-
compare_encodings(self, NestingLevel::new(), other, NestingLevel::new(), false)
235+
compare_encodings(self, other, NestingLevel::new(), false)
227236
}
228237
}
229238

@@ -235,7 +244,7 @@ impl Encoding {
235244
/// Objective-C compilers.
236245
impl fmt::Display for Encoding {
237246
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238-
write!(f, "{}", Helper::new(self, NestingLevel::new()))
247+
Helper::new(self).fmt(f, NestingLevel::new())
239248
}
240249
}
241250

@@ -303,16 +312,16 @@ mod tests {
303312

304313
// Check equivalence comparisons
305314
assert!(E.equivalent_to(&E), "equivalent self");
306-
assert!(E.equivalent_to_str($string), "equivalent self string");
315+
assert!(E.equivalent_to_str($string), "equivalent self string {}", $string);
307316
assert!(E.equivalent_to_box(&boxed), "equivalent self boxed");
308317
$(
309-
assert!(E.equivalent_to(&$equivalent_encoding), "equivalent encoding");
318+
assert!(E.equivalent_to(&$equivalent_encoding), "equivalent encoding {}", $equivalent_encoding);
310319
assert!(E.equivalent_to_str(&$equivalent_encoding.to_string()), "equivalent encoding string");
311320
let boxed = EncodingBox::from_str(&$equivalent_encoding.to_string()).expect("parse equivalent encoding");
312321
assert!(E.equivalent_to_box(&boxed), "equivalent encoding boxed");
313322
)*
314323
$(
315-
assert!(E.equivalent_to_str($equivalent_string), "equivalent string");
324+
assert!(E.equivalent_to_str($equivalent_string), "equivalent string {}", $equivalent_string);
316325
let boxed = EncodingBox::from_str($equivalent_string).expect("parse equivalent string");
317326
assert!(E.equivalent_to_box(&boxed), "equivalent string boxed");
318327
)*
@@ -447,52 +456,61 @@ mod tests {
447456
448457
fn struct_() {
449458
Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int]);
459+
~Encoding::Struct("SomeStruct", &[]);
450460
!Encoding::Union("SomeStruct", &[Encoding::Char, Encoding::Int]);
451461
!Encoding::Int;
452462
!Encoding::Struct("SomeStruct", &[Encoding::Int]);
453463
!Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int, Encoding::Int]);
454464
!Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char]);
455465
!Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int]);
456466
"{SomeStruct=ci}";
457-
!"{SomeStruct=ci";
467+
~"{SomeStruct=}";
458468
!"{SomeStruct}";
459-
!"{SomeStruct=}";
469+
!"{SomeStruct=ic}";
470+
!"{SomeStruct=malformed";
460471
}
461472
462473
fn pointer_struct() {
463474
Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int]));
475+
~Encoding::Pointer(&Encoding::Struct("SomeStruct", &[]));
464476
!Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char]));
465477
!Encoding::Pointer(&Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int]));
466478
"^{SomeStruct=ci}";
467-
!"^{SomeStruct=ci";
479+
~"^{SomeStruct=}";
468480
!"^{SomeStruct}";
469-
!"^{SomeStruct=}";
481+
!"^{SomeStruct=ic}";
482+
!"^{SomeStruct=malformed";
470483
}
471484
472485
fn pointer_pointer_struct() {
473486
Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int])));
487+
~Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("SomeStruct", &[])));
474488
~Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char])));
475489
!Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int])));
476490
"^^{SomeStruct}";
477-
!"^^{SomeStruct=ci}";
478-
!"^^{SomeStruct=ii}";
479-
!"^^{SomeStruct=ci";
480491
!"^^{SomeStruct=}";
492+
!"^^{SomeStruct=ci}";
493+
!"^^{SomeStruct=ic}";
494+
!"^^{AnotherName=ic}";
495+
!"^^{SomeStruct=malformed";
481496
}
482497
483498
fn atomic_struct() {
484499
Encoding::Atomic(&Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int]));
500+
~Encoding::Atomic(&Encoding::Struct("SomeStruct", &[]));
485501
~Encoding::Atomic(&Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char]));
486502
!Encoding::Atomic(&Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int]));
487503
"A{SomeStruct}";
488-
!"A{SomeStruct=ci}";
489-
!"A{SomeStruct=ci";
490504
!"A{SomeStruct=}";
505+
!"A{SomeStruct=ci}";
506+
!"A{SomeStruct=ic}";
507+
!"A{SomeStruct=malformed";
491508
}
492509
493510
fn empty_struct() {
494511
Encoding::Struct("SomeStruct", &[]);
495512
"{SomeStruct=}";
513+
~"{SomeStruct=ci}";
496514
!"{SomeStruct}";
497515
}
498516
@@ -554,12 +572,25 @@ mod tests {
554572
]
555573
);
556574
"{abc=^[8B](def=@?)^^b255c?}";
575+
~"{abc=}";
576+
!"{abc}";
557577
}
558578
559579
fn identifier() {
560580
Encoding::Struct("_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", &[]);
561581
"{_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=}";
562582
}
583+
584+
// Regression test. The encoding of the `CGLContextObj` object changed
585+
// between versions of macOS. As such, this is something that we must
586+
// be prepared to handle.
587+
fn cgl_context_obj() {
588+
Encoding::Pointer(&Encoding::Struct("_CGLContextObject", &[]));
589+
"^{_CGLContextObject=}";
590+
~"^{_CGLContextObject=^{__GLIContextRec}{__GLIFunctionDispatchRec=^?^?^?^?^?}^{_CGLPrivateObject}^v}";
591+
!"^{_CGLContextObject}";
592+
!"^{SomeOtherStruct=}";
593+
}
563594
}
564595
565596
#[test]
@@ -592,7 +623,7 @@ mod tests {
592623
let parsed = EncodingBox::from_str(s).unwrap();
593624
let expected = EncodingBox::Struct(
594625
"S".to_string(),
595-
Some(vec![EncodingBox::Block, EncodingBox::Block]),
626+
vec![EncodingBox::Block, EncodingBox::Block],
596627
);
597628
assert_eq!(parsed, expected);
598629

crates/objc2-encode/src/encoding_box.rs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,9 @@ pub enum EncodingBox {
8585
/// Same as [`Encoding::Array`].
8686
Array(u64, Box<Self>),
8787
/// Same as [`Encoding::Struct`].
88-
Struct(String, Option<Vec<Self>>),
88+
Struct(String, Vec<Self>),
8989
/// Same as [`Encoding::Union`].
90-
Union(String, Option<Vec<Self>>),
90+
Union(String, Vec<Self>),
9191
}
9292

9393
impl EncodingBox {
@@ -135,13 +135,13 @@ impl EncodingBox {
135135
/// Same formatting as [`Encoding`]'s `Display` implementation.
136136
impl fmt::Display for EncodingBox {
137137
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138-
write!(f, "{}", Helper::from_box(self, NestingLevel::new()))
138+
Helper::from_box(self).fmt(f, NestingLevel::new())
139139
}
140140
}
141141

142142
impl PartialEq<Encoding> for EncodingBox {
143143
fn eq(&self, other: &Encoding) -> bool {
144-
compare_encodings(self, NestingLevel::new(), other, NestingLevel::new(), true)
144+
compare_encodings(self, other, NestingLevel::new(), true)
145145
}
146146
}
147147

@@ -188,20 +188,20 @@ mod tests {
188188
));
189189
let enc2 = EncodingBox::Atomic(Box::new(EncodingBox::Struct(
190190
"test".to_string(),
191-
Some(vec![EncodingBox::Array(2, Box::new(EncodingBox::Int))]),
191+
vec![EncodingBox::Array(2, Box::new(EncodingBox::Int))],
192192
)));
193193
let enc3 = EncodingBox::Atomic(Box::new(EncodingBox::Struct(
194194
"test".to_string(),
195-
Some(vec![EncodingBox::Array(2, Box::new(EncodingBox::Char))]),
195+
vec![EncodingBox::Array(2, Box::new(EncodingBox::Char))],
196196
)));
197197
assert_eq!(enc1, enc2);
198198
assert_ne!(enc1, enc3);
199199
}
200200

201201
#[test]
202-
fn ne_struct_with_without_fields() {
203-
let enc1 = EncodingBox::Struct("test".to_string(), Some(vec![EncodingBox::Char]));
204-
let enc2 = EncodingBox::Struct("test".to_string(), None);
202+
fn struct_nested_in_pointer() {
203+
let enc1 = EncodingBox::Struct("test".to_string(), vec![EncodingBox::Char]);
204+
let enc2 = EncodingBox::Struct("test".to_string(), vec![EncodingBox::Int]);
205205
const ENC3A: Encoding = Encoding::Struct("test", &[Encoding::Char]);
206206
assert_ne!(enc1, enc2);
207207
assert!(ENC3A.equivalent_to_box(&enc1));
@@ -225,15 +225,12 @@ mod tests {
225225
#[test]
226226
fn parse_atomic_struct() {
227227
let expected = EncodingBox::Atomic(Box::new(EncodingBox::Atomic(Box::new(
228-
EncodingBox::Struct("a".into(), Some(Vec::new())),
228+
EncodingBox::Struct("a".into(), vec![]),
229229
))));
230230
let actual = EncodingBox::from_str("AA{a=}").unwrap();
231231
assert_eq!(expected, actual);
232232
assert_eq!(expected.to_string(), "AA{a}");
233233

234-
let expected = EncodingBox::Atomic(Box::new(EncodingBox::Atomic(Box::new(
235-
EncodingBox::Struct("a".into(), None),
236-
))));
237234
let actual = EncodingBox::from_str("AA{a}").unwrap();
238235
assert_eq!(expected, actual);
239236
assert_eq!(expected.to_string(), "AA{a}");
@@ -243,7 +240,7 @@ mod tests {
243240
fn parse_part_of_string() {
244241
let mut s = "{a}cb0i16";
245242

246-
let expected = EncodingBox::Struct("a".into(), None);
243+
let expected = EncodingBox::Struct("a".into(), vec![]);
247244
let actual = EncodingBox::from_start_of_str(&mut s).unwrap();
248245
assert_eq!(expected, actual);
249246

0 commit comments

Comments
 (0)