Generate a random u64 to append to subclass names (Issue #63)

This commit is contained in:
Ryan McGrath 2022-10-14 15:34:41 -04:00
parent 65578f06fe
commit dd0786bdab
No known key found for this signature in database
GPG key ID: DA6CBD9233593DEA

View file

@ -1,38 +1,34 @@
use std::cell::Cell;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::ffi::CString; use std::ffi::CString;
use std::thread;
use std::time::Instant;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use objc::{class, msg_send, sel, sel_impl};
use objc::declare::ClassDecl; use objc::declare::ClassDecl;
use objc::runtime::{objc_getClass, Class, Object}; use objc::runtime::{objc_getClass, Class};
lazy_static! { lazy_static! {
static ref CLASSES: ClassMap = ClassMap::new(); static ref CLASSES: ClassMap = ClassMap::new();
} }
/// A temporary method for testing; this will get cleaned up if it's worth bringing in permanently. thread_local! {
/// /// A very simple RNG seed that we use in constructing unique subclass names.
/// (and probably not repeatedly queried...) ///
/// /// Why are we doing this? Mainly because I just don't want to bring in another
/// This accounts for code not running in a standard bundle, and returns `None` if the bundle /// crate for something that can be done like this; we don't need cryptographically
/// identifier is nil. /// secure generation or anything fancy, as we're just after a unique dangling bit
fn get_bundle_id() -> Option<String> { /// for class names.
let identifier: *mut Object = unsafe { static RNG_SEED: Cell<u64> = Cell::new({
let bundle: *mut Object = msg_send![class!(NSBundle), mainBundle]; let mut hasher = DefaultHasher::new();
msg_send![bundle, bundleIdentifier] Instant::now().hash(&mut hasher);
}; thread::current().id().hash(&mut hasher);
let hash = hasher.finish();
if identifier == crate::foundation::nil { (hash << 1) | 1
return None; });
}
let identifier = crate::foundation::NSString::retain(identifier).to_string()
.replace(".", "_")
.replace("-", "_");
Some(identifier)
} }
/// Represents an entry in a `ClassMap`. We store an optional superclass_name for debugging /// Represents an entry in a `ClassMap`. We store an optional superclass_name for debugging
@ -143,10 +139,20 @@ where
// If we can't find the class anywhere, then we'll attempt to load the superclass and register // If we can't find the class anywhere, then we'll attempt to load the superclass and register
// our new class type. // our new class type.
if let Some(superclass) = CLASSES.load(superclass_name, None) { if let Some(superclass) = CLASSES.load(superclass_name, None) {
let objc_subclass_name = match get_bundle_id() { // When we're generating a new Subclass name, we need to append a random-ish component
Some(bundle_id) => format!("{}_{}_{}", subclass_name, superclass_name, bundle_id), // due to some oddities that can come up in certain scenarios (e.g, various bundler
None => format!("{}_{}", subclass_name, superclass_name) // situations appear to have odd rules about subclass name usage/registration, this simply
}; // guarantees that we almost always have a unique name to register with the ObjC runtime).
//
// For more context, see: https://github.com/ryanmcgrath/cacao/issues/63
let objc_subclass_name = format!("{}_{}_{}", subclass_name, superclass_name, RNG_SEED.with(|rng| {
rng.set(rng.get().wrapping_add(0xa0761d6478bd642f));
let s = rng.get();
let t = u128::from(s) * (u128::from(s ^ 0xe7037ed1a0b428db));
((t >> 64) as u64) ^ (t as u64)
}));
println!("{}", objc_subclass_name);
match ClassDecl::new(&objc_subclass_name, unsafe { &*superclass }) { match ClassDecl::new(&objc_subclass_name, unsafe { &*superclass }) {
Some(mut decl) => { Some(mut decl) => {