Skip to content

Instantly share code, notes, and snippets.

@jdm
Forked from pcwalton/gist:9280996
Last active August 29, 2015 13:56
Show Gist options
  • Save jdm/9332689 to your computer and use it in GitHub Desktop.
Save jdm/9332689 to your computer and use it in GitHub Desktop.
#[feature(macro_rules)];
use std::cast;
trait RootedReference<T> {
fn root_ref<'a>(&'a self) -> Option<JSRef<'a, T>>;
}
impl<T> RootedReference<T> for Option<Rooted<T>> {
fn root_ref<'a>(&'a self) -> Option<JSRef<'a, T>> {
self.as_ref().map(|js| js.root_ref())
}
}
macro_rules! root(
($($name:ident = $js:expr),+ in $rest:expr) => {
{
$(let $name;)+
unsafe {
$($name = Rooted::new(&$js);)+
}
$(let _you_cannot_move_a_rooted_javascript_object = &$name;)+
$rest
}
}
)
macro_rules! maybe_root(
($($name:ident = $js:expr),+ in $rest:expr) => {
{
$(let $name;)+
unsafe {
$($name = ($js).map(|val| Rooted::new(&val));)+
}
$(let _you_cannot_move_a_rooted_javascript_object = &$name;)+
$rest
}
}
)
pub struct JS<T> {
priv ptr: *mut T,
}
impl<T> JS<T> {
pub fn get<'a>(&'a self) -> &'a mut T {
unsafe {
cast::transmute(self.ptr)
}
}
pub fn set(&mut self, other: JSRef<T>) {
self.ptr = other.ptr;
}
}
#[unsafe_destructor]
impl<T> Drop for JS<T> {
fn drop(&mut self) {}
}
pub struct Rooted<T> {
priv ptr: *mut T,
}
impl<T> Rooted<T> {
pub unsafe fn new(js_object: &JS<T>) -> Rooted<T> {
println!("rooting!");
Rooted {
ptr: js_object.ptr,
}
}
pub fn get<'a>(&'a self) -> &'a mut T {
unsafe {
cast::transmute(self.ptr)
}
}
pub fn root_ref<'a>(&'a self) -> JSRef<'a,T> {
unsafe {
JSRef {
ptr: self.ptr,
chain: ::std::cast::transmute_region(&()),
}
}
}
}
#[unsafe_destructor]
impl<T> Drop for Rooted<T> {
fn drop(&mut self) {
println!("dropping root!");
}
}
/// Encapsulates a reference to something that is guaranteed to be alive. This is freely copyable.
pub struct JSRef<'a,T> {
priv ptr: *mut T,
priv chain: &'a (),
}
impl<'a,T> JSRef<'a,T> {
pub fn get<'a>(&'a self) -> &'a mut T {
unsafe {
cast::transmute(self.ptr)
}
}
}
pub struct IntContainer {
value: JS<int>,
}
pub fn main() {
// setup
let mut value = 3;
let three: JS<int> = JS {
ptr: &mut value,
};
let mut value = 5;
let five: JS<int> = JS {
ptr: &mut value,
};
let mut value = IntContainer {
value: five,
};
let int_container: JS<IntContainer> = JS {
ptr: &mut value,
};
// rooting!
root!(rooted_three = three,
rooted_int_container = int_container in {
println!("rooted object has value {}", *rooted_three.get());
// let _ = you_cannot_move_a_rooted_javascript_object; -- prohibited; inaccessible
// drop(rooted_three); -- prohibited; cannot move out because borrowed
other_function(rooted_int_container.root_ref(), rooted_three.root_ref());
});
let mut value = 6;
let six: JS<int> = JS {
ptr: &mut value,
};
maybe_root!(rooted_possible_six = Some(six),
rooted_possible_seven = None in {
another_function(rooted_possible_six.root_ref());
another_function(rooted_possible_seven.root_ref());
});
let mut value = 7;
root!(rooted_seven = return_value(&mut value) in {
another_function(Some(rooted_seven.root_ref()))
});
}
fn other_function(int_container: JSRef<IntContainer>, value: JSRef<int>) {
println!("I had {}", *int_container.get().value.get());
int_container.get().value.set(value);
println!("I now have {}", *int_container.get().value.get());
}
fn another_function(maybe_value: Option<JSRef<int>>) {
match maybe_value {
Some(val) => println!("I had {}", *val.get()),
None => println!("didn't have anything"),
}
}
fn return_value(unboxed: &mut int) -> JS<int> {
JS {
ptr: unboxed
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment