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
use std::mem;
use std::ptr::{null_mut, NonNull};

use foreign_types::ForeignTypeRef;

use crate::{ffi, ClassId, ContextRef, Local, Runtime, Value};

lazy_static! {
    static ref RUNTIME_USERDATA_CLASS_ID: ClassId = Runtime::new_class_id();
}

impl Runtime {
    pub fn userdata_class_id() -> ClassId {
        *RUNTIME_USERDATA_CLASS_ID
    }

    pub(crate) fn register_userdata_class(&self) -> bool {
        unsafe extern "C" fn userdata_finalizer(_rt: *mut ffi::JSRuntime, obj: ffi::JSValue) {
            let ptr = ffi::JS_GetOpaque(obj, Runtime::userdata_class_id());

            trace!("free userdata {:p} @ {:?}", ptr, obj.u.ptr);

            mem::drop(Box::from_raw(ptr));
        }

        self.new_class(
            Runtime::userdata_class_id(),
            &ffi::JSClassDef {
                class_name: cstr!(Userdata).as_ptr(),
                finalizer: Some(userdata_finalizer),
                gc_mark: None,
                call: None,
                exotic: null_mut(),
            },
        )
    }
}

impl ContextRef {
    pub fn new_userdata<T>(&self, v: T) -> Local<'_, Value> {
        let obj = self.new_object_class(Runtime::userdata_class_id());
        let ptr = Box::into_raw(Box::new(v));

        trace!("new userdata {:p} @ {:?}", ptr, obj.as_ptr::<()>());

        obj.set_opaque(ptr);

        self.bind(obj)
    }

    pub fn get_userdata_unchecked<T>(&self, obj: &Value) -> NonNull<T> {
        let ptr = self.get_opaque(obj, Runtime::userdata_class_id());

        trace!("got userdata {:p} @ {:?}", ptr, obj.as_ptr::<()>());

        unsafe { NonNull::new_unchecked(ptr) }
    }
}

impl Value {
    pub fn set_opaque<T>(&self, opaque: *mut T) {
        unsafe { ffi::JS_SetOpaque(self.raw(), opaque as *mut _) }
    }

    pub fn get_opaque<T>(&self, class_id: ClassId) -> *mut T {
        unsafe { ffi::JS_GetOpaque(self.raw(), class_id) as *mut _ }
    }
}

impl Local<'_, Value> {
    pub fn get_opaque<T>(&self, class_id: ClassId) -> *mut T {
        self.ctxt.get_opaque(self, class_id)
    }
}

impl ContextRef {
    pub fn get_opaque<T>(&self, obj: &Value, class_id: ClassId) -> *mut T {
        unsafe { ffi::JS_GetOpaque2(self.as_ptr(), obj.raw(), class_id) as *mut _ }
    }
}