Created
June 7, 2015 17:50
-
-
Save nwellnhof/299dd7315b8b54ac4e3d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extern crate libc; | |
use libc::types::common::c99::*; | |
use libc::types::os::arch::c95::*; | |
#[repr(C)] | |
pub struct ObjInternal { | |
refcount: size_t, | |
class: *mut ObjInternal, | |
} | |
#[link(name = "cfish")] | |
extern { | |
static CFISH_OBJ: *mut ObjInternal; | |
static CFISH_CLASS: *mut ObjInternal; | |
static CFISH_STRING: *mut ObjInternal; | |
fn cfish_bootstrap_parcel(); | |
fn cfish_inc_refcount(obj: *mut ObjInternal); | |
fn cfish_dec_refcount(obj: *mut ObjInternal) -> uint32_t; | |
// The libc crate doesn't support the C99 _Bool type. | |
// See https://github.com/rust-lang/rust/issues/14608 | |
fn cfish_Obj_is_a(obj: *mut ObjInternal, class: *mut ObjInternal) | |
-> c_char; | |
fn cfish_Str_new_from_trusted_utf8(utf8: *const u8, size: size_t) | |
-> *mut ObjInternal; | |
static CFISH_Str_Get_Size_OFFSET: size_t; | |
} | |
// Obj | |
pub struct Obj { | |
ptr: *mut ObjInternal, | |
} | |
#[allow(non_snake_case)] | |
pub trait ObjTrait { | |
fn PTR(&self) -> *mut ObjInternal; | |
} | |
#[allow(non_snake_case)] | |
pub trait ObjCore { | |
fn CLASS() -> Class; | |
fn NEW_FROM_PTR(ptr: *mut ObjInternal) -> Self; | |
} | |
pub trait ObjCopy { | |
fn copy(&self) -> Self; | |
} | |
impl<T:ObjTrait + ObjCore> ObjCopy for T { | |
fn copy(&self) -> T { | |
T::NEW_FROM_PTR(self.PTR()) | |
} | |
} | |
impl ObjTrait for Obj { | |
fn PTR(&self) -> *mut ObjInternal { self.ptr } | |
} | |
impl ObjCore for Obj { | |
fn CLASS() -> Class { Class { ptr: CFISH_OBJ } } | |
fn NEW_FROM_PTR(ptr: *mut ObjInternal) -> Self { | |
unsafe { cfish_inc_refcount(ptr); } | |
Obj { ptr: ptr } | |
} | |
} | |
impl Drop for Obj { | |
fn drop(&mut self) { | |
unsafe { cfish_dec_refcount(self.ptr); } | |
} | |
} | |
impl Clone for Obj { | |
fn clone(&self) -> Obj { | |
// Should invoke Obj_Clone. | |
unsafe { cfish_inc_refcount(self.ptr); } | |
Obj { ptr: self.ptr } | |
} | |
} | |
pub fn cfish_class<T: ObjCore>() -> Class { T::CLASS() } | |
pub fn is_a<T: ObjCore>(obj: &ObjTrait) -> bool { | |
unsafe { return cfish_Obj_is_a(obj.PTR(), T::CLASS().PTR()) != 0; } | |
} | |
pub fn cast<T: ObjCore>(obj: &ObjTrait) -> Option<T> { | |
if is_a::<T>(obj) { | |
return Some(T::NEW_FROM_PTR(obj.PTR())); | |
} | |
else { | |
return None; | |
} | |
} | |
// Class (no need for refcount handling) | |
pub struct Class { | |
ptr: *mut ObjInternal, | |
} | |
impl ObjTrait for Class { | |
fn PTR(&self) -> *mut ObjInternal { self.ptr } | |
} | |
impl ObjCore for Class { | |
fn CLASS() -> Class { Class { ptr: CFISH_CLASS } } | |
fn NEW_FROM_PTR(ptr: *mut ObjInternal) -> Self { Class { ptr: ptr } } | |
} | |
// String | |
pub struct String { | |
ptr: *mut ObjInternal, | |
} | |
impl ObjTrait for String { | |
fn PTR(&self) -> *mut ObjInternal { self.ptr } | |
} | |
impl ObjCore for String { | |
fn CLASS() -> Class { Class { ptr: CFISH_STRING } } | |
fn NEW_FROM_PTR(ptr: *mut ObjInternal) -> Self { | |
unsafe { cfish_inc_refcount(ptr); } | |
String { ptr: ptr } | |
} | |
} | |
pub trait StringTrait : ObjTrait { | |
fn get_size(&self) -> usize { | |
let ptr = self.PTR(); | |
unsafe { | |
let class = (*ptr).class as *const c_char; | |
let method_ptr = class.offset(CFISH_Str_Get_Size_OFFSET as isize) | |
as *const extern fn (*mut ObjInternal) -> size_t; | |
return (*method_ptr)(ptr) as usize; | |
} | |
} | |
} | |
impl StringTrait for String {} | |
impl Drop for String { | |
fn drop(&mut self) { | |
unsafe { cfish_dec_refcount(self.ptr); } | |
} | |
} | |
impl Clone for String { | |
fn clone(&self) -> String { | |
unsafe { cfish_inc_refcount(self.ptr); } | |
String { ptr: self.ptr } | |
} | |
fn clone_from(&mut self, source: &String) { | |
unsafe { cfish_inc_refcount(self.ptr); } | |
self.ptr = source.ptr; | |
} | |
} | |
impl String { | |
fn new(s: &str) -> String { | |
unsafe { | |
return String { | |
ptr: cfish_Str_new_from_trusted_utf8(s.as_ptr(), | |
s.len() as size_t), | |
} | |
} | |
} | |
} | |
// main | |
fn main() { | |
unsafe { cfish_bootstrap_parcel(); } | |
// Create a new String (Clownfish String, not Rust String). | |
let string: String = String::new("Hello, world!"); | |
{ | |
// Create String trait object. | |
let string_trait: &StringTrait = &string; | |
// Create Obj trait object. | |
let obj_trait: &ObjTrait = &string; | |
// Unfortunately, Rust doesn't allow to upcast trait objects. | |
//let new_obj_trait: &ObjTrait = string_trait; | |
// Obj trait knows it's a String. | |
assert!(is_a::<String>(obj_trait)); | |
// String is not a Class. | |
assert!(!is_a::<Class>(&string)); | |
// Method call works. | |
assert_eq!(string.get_size(), 13); | |
} | |
// We can't add the Copy trait to Clownfish objects, because we must | |
// call incref when creating a copy. Unfortunately, Rust doesn't allow | |
// to override the behavior of the Copy trait. This means that | |
// Clownfish object structs have "move" semantics. | |
// This invalidates the original string. This only works because the | |
// references we created earlier are out of scope. | |
let string_copy = string; | |
// This would fail now, for example. | |
//let string_ref: &String = &string; | |
// Custom "copy" method that creates a new String struct pointing | |
// to the original string, leaving the original struct intact. | |
let another_copy: String = string_copy.copy(); | |
// Upcast String to Object. It's easier to cast to an ObjTrait | |
// implicitly. | |
let obj: Obj = cast::<Obj>(&string_copy).expect("Invalid cast"); | |
assert_eq!(obj.PTR(), string_copy.PTR()); | |
// Downcast Obj back to String. | |
let cast_string: String = cast::<String>(&obj).expect("Invalid cast"); | |
// Invalid cast of String to Class. | |
let option: Option<Class> = cast::<Class>(&string_copy); | |
assert!(option.is_none()); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment