1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
use std::any::Any;
use std::fmt;
use std::sync::Mutex;
use std::marker::PhantomData;

use base::types::{Type, ArcType};
use Result;
use gc::{Gc, GcPtr, Move, Traverseable};
use vm::Thread;
use thread::ThreadInternal;
use value::{Value, Cloner};
use api::{RuntimeResult, Generic, Userdata, VmType, WithVM};
use api::generic::A;

pub struct Reference<T> {
    value: Mutex<Value>,
    thread: GcPtr<Thread>,
    _marker: PhantomData<T>,
}

impl<T> Userdata for Reference<T>
    where T: Any + Send + Sync,
{
    fn deep_clone(&self, deep_cloner: &mut Cloner) -> Result<GcPtr<Box<Userdata>>> {
        let value = self.value.lock().unwrap();
        let cloned_value = deep_cloner.deep_clone(*value)?;
        let data: Box<Userdata> = Box::new(Reference {
            value: Mutex::new(cloned_value),
            thread: unsafe { GcPtr::from_raw(deep_cloner.thread()) },
            _marker: PhantomData::<A>,
        });
        deep_cloner.gc().alloc(Move(data))
    }
}

impl<T> fmt::Debug for Reference<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Ref({:?})", *self.value.lock().unwrap())
    }
}

impl<T> Traverseable for Reference<T> {
    fn traverse(&self, gc: &mut Gc) {
        self.value.lock().unwrap().traverse(gc)
    }
}

impl<T> VmType for Reference<T>
    where T: VmType,
          T::Type: Sized,
{
    type Type = Reference<T::Type>;

    fn make_type(vm: &Thread) -> ArcType {
        let env = vm.global_env().get_env();
        let symbol = env.find_type_info("Ref").unwrap().name.clone();
        let ctor = Type::ident(symbol);
        Type::app(ctor, collect![T::make_type(vm)])
    }
}

fn set(r: &Reference<A>, a: Generic<A>) -> RuntimeResult<(), String> {
    match r.thread.deep_clone_value(&r.thread, a.0) {
        Ok(a) => {
            *r.value.lock().unwrap() = a;
            RuntimeResult::Return(())
        }
        Err(err) => RuntimeResult::Panic(format!("{}", err)),
    }
}

fn get(r: &Reference<A>) -> Generic<A> {
    Generic::from(*r.value.lock().unwrap())
}

fn make_ref(a: WithVM<Generic<A>>) -> Reference<A> {
    Reference {
        value: Mutex::new(a.value.0),
        thread: unsafe { GcPtr::from_raw(a.vm) },
        _marker: PhantomData,
    }
}

pub fn load(vm: &Thread) -> Result<()> {
    let _ = vm.register_type::<Reference<A>>("Ref", &["a"]);
    vm.define_global("<-", primitive!(2 set))?;
    vm.define_global("load", primitive!(1 get))?;
    vm.define_global("ref", primitive!(1 make_ref))?;
    Ok(())
}