neon/types_impl/
private.rs

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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use std::{ffi::c_void, mem::MaybeUninit};

use crate::{
    context::{
        internal::{ContextInternal, Env},
        Context, Cx,
    },
    handle::{internal::TransparentNoCopyWrapper, Handle},
    result::{JsResult, NeonResult, Throw},
    sys::{self, bindings as napi, raw},
    types::Value,
};

use super::JsValue;

// Maximum number of function arguments in V8.
const V8_ARGC_LIMIT: usize = 65535;

pub(crate) unsafe fn prepare_call<'a, 'b, C: Context<'a>>(
    cx: &mut C,
    args: &[Handle<'b, JsValue>],
) -> NeonResult<(usize, *const c_void)> {
    // Note: This cast is only save because `Handle<'_, JsValue>` is
    // guaranteed to have the same layout as a pointer because `Handle`
    // and `JsValue` are both `repr(C)` newtypes.
    let argv = args.as_ptr().cast();
    let argc = args.len();
    if argc > V8_ARGC_LIMIT {
        return cx.throw_range_error("too many arguments");
    }
    Ok((argc, argv))
}

pub trait ValueInternal: TransparentNoCopyWrapper + 'static {
    fn name() -> &'static str;

    fn is_typeof<Other: Value>(cx: &mut Cx, other: &Other) -> bool;

    fn downcast<Other: Value>(cx: &mut Cx, other: &Other) -> Option<Self> {
        if Self::is_typeof(cx, other) {
            // # Safety
            // `is_typeof` check ensures this is the correct JavaScript type
            Some(unsafe { Self::from_local(cx.env(), other.to_local()) })
        } else {
            None
        }
    }

    fn cast<'a, T: Value, F: FnOnce(raw::Local) -> T>(self, f: F) -> Handle<'a, T> {
        Handle::new_internal(f(self.to_local()))
    }

    fn to_local(&self) -> raw::Local;

    // # Safety
    // JavaScript value must be of type `Self`
    unsafe fn from_local(env: Env, h: raw::Local) -> Self;

    unsafe fn try_call<'a, 'b, C: Context<'a>, T, AS>(
        &self,
        cx: &mut C,
        this: Handle<'b, T>,
        args: AS,
    ) -> JsResult<'a, JsValue>
    where
        T: Value,
        AS: AsRef<[Handle<'b, JsValue>]>,
    {
        let callee = self.to_local();
        let (argc, argv) = unsafe { prepare_call(cx, args.as_ref()) }?;
        let env = cx.env();
        let mut result: MaybeUninit<raw::Local> = MaybeUninit::zeroed();

        let status = napi::call_function(
            env.to_raw(),
            this.to_local(),
            callee,
            argc,
            argv.cast(),
            result.as_mut_ptr(),
        );

        check_call_status(cx, callee, status)?;

        Ok(Handle::new_internal(JsValue::from_local(
            env,
            result.assume_init(),
        )))
    }

    unsafe fn try_construct<'a, 'b, C: Context<'a>, AS>(
        &self,
        cx: &mut C,
        args: AS,
    ) -> JsResult<'a, JsValue>
    where
        AS: AsRef<[Handle<'b, JsValue>]>,
    {
        let callee = self.to_local();
        let (argc, argv) = unsafe { prepare_call(cx, args.as_ref()) }?;
        let env = cx.env();
        let mut result: MaybeUninit<raw::Local> = MaybeUninit::zeroed();
        let status =
            napi::new_instance(env.to_raw(), callee, argc, argv.cast(), result.as_mut_ptr());

        check_call_status(cx, callee, status)?;

        Ok(Handle::new_internal(JsValue::from_local(
            env,
            result.assume_init(),
        )))
    }
}

unsafe fn check_call_status<'a, C: Context<'a>>(
    cx: &mut C,
    callee: raw::Local,
    status: Result<(), sys::Status>,
) -> NeonResult<()> {
    match status {
        Err(sys::Status::InvalidArg) if !sys::tag::is_function(cx.env().to_raw(), callee) => {
            return cx.throw_error("not a function");
        }
        Err(sys::Status::PendingException) => {
            return Err(Throw::new());
        }
        status => status.unwrap(),
    }

    Ok(())
}