1use std::ffi::{CStr, CString};
8use std::ops::Deref;
9use std::ptr::NonNull;
10
11use mozjs::conversions::jsstr_to_string;
12use mozjs::gc::{RootableVec, RootedVec};
13use mozjs::jsapi::{
14 HandleValueArray, JS_CallFunction, JS_DecompileFunction, JS_GetFunctionArity, JS_GetFunctionDisplayId,
15 JS_GetFunctionId, JS_GetFunctionLength, JS_GetFunctionObject, JS_GetObjectFunction, JS_IsBuiltinEvalFunction,
16 JS_IsBuiltinFunctionConstructor, JS_IsConstructor, JS_NewFunction, JS_ObjectIsFunction, JSContext, JSFunction,
17 JSFunctionSpec, JSObject, NewFunctionFromSpec1, NewFunctionWithReserved, SetFunctionNativeReserved,
18};
19use mozjs::jsval::{JSVal, ObjectValue};
20
21use crate::flags::PropertyFlags;
22use crate::function::closure::{
23 Closure, ClosureOnce, call_closure, call_closure_once, create_closure_object, create_closure_once_object,
24};
25use crate::{Context, Error, ErrorReport, Local, Object, Value};
26
27pub type NativeFunction = unsafe extern "C" fn(*mut JSContext, u32, *mut JSVal) -> bool;
29
30#[derive(Debug)]
33pub struct Function<'f> {
34 function: Local<'f, *mut JSFunction>,
35}
36
37impl<'f> Function<'f> {
38 pub fn new(
40 cx: &'f Context, name: &str, func: Option<NativeFunction>, nargs: u32, flags: PropertyFlags,
41 ) -> Function<'f> {
42 let name = CString::new(name).unwrap();
43 Function {
44 function: cx
45 .root(unsafe { JS_NewFunction(cx.as_ptr(), func, nargs, u32::from(flags.bits()), name.as_ptr()) }),
46 }
47 }
48
49 pub fn from_spec(cx: &'f Context, spec: &JSFunctionSpec) -> Function<'f> {
51 Function {
52 function: cx.root(unsafe { NewFunctionFromSpec1(cx.as_ptr(), spec) }),
53 }
54 }
55
56 pub fn from_closure_once(
60 cx: &'f Context, name: &CStr, closure: Box<ClosureOnce>, nargs: u32, flags: PropertyFlags,
61 ) -> Function<'f> {
62 let closure = create_closure_once_object(cx, closure);
63 Function::create_with_closure(cx, call_closure_once, name, &closure, nargs, flags)
64 }
65
66 pub fn from_closure(
68 cx: &'f Context, name: &CStr, closure: Box<Closure>, nargs: u32, flags: PropertyFlags,
69 ) -> Function<'f> {
70 let closure = create_closure_object(cx, closure);
71 Function::create_with_closure(cx, call_closure, name, &closure, nargs, flags)
72 }
73
74 fn create_with_closure(
75 cx: &'f Context, call: NativeFunction, name: &CStr, closure: &Object, nargs: u32, flags: PropertyFlags,
76 ) -> Function<'f> {
77 unsafe {
78 let function = Function {
79 function: cx.root(NewFunctionWithReserved(
80 cx.as_ptr(),
81 Some(call),
82 nargs,
83 u32::from(flags.bits()),
84 name.as_ptr().cast(),
85 )),
86 };
87 SetFunctionNativeReserved(
88 JS_GetFunctionObject(function.get()),
89 0,
90 &ObjectValue(closure.handle().get()),
91 );
92 function
93 }
94 }
95
96 pub fn from_object(cx: &'f Context, obj: &Local<'_, *mut JSObject>) -> Option<Function<'f>> {
99 (unsafe { Function::is_function_raw(obj.get()) }).then(|| Function {
100 function: cx.root(unsafe { JS_GetObjectFunction(obj.get()) }),
101 })
102 }
103
104 pub fn to_object(&self, cx: &'f Context) -> Object<'f> {
106 cx.root(unsafe { JS_GetFunctionObject(self.get()) }).into()
107 }
108
109 pub fn to_string(&self, cx: &Context) -> Option<String> {
111 unsafe {
112 NonNull::new(JS_DecompileFunction(cx.as_ptr(), self.handle().into()))
113 .map(|str| jsstr_to_string(cx.as_ptr(), str))
114 }
115 }
116
117 pub fn name(&self, cx: &Context) -> crate::Result<String> {
119 let mut name = crate::String::new(cx);
120 if unsafe { JS_GetFunctionId(cx.as_ptr(), self.handle().into(), name.handle_mut().into()) } {
121 name.to_owned(cx)
122 } else {
123 Err(Error::none())
124 }
125 }
126
127 pub fn display_name(&self, cx: &Context) -> crate::Result<String> {
131 let mut name = crate::String::new(cx);
132 if unsafe { JS_GetFunctionDisplayId(cx.as_ptr(), self.handle().into(), name.handle_mut().into()) } {
133 name.to_owned(cx)
134 } else {
135 Err(Error::none())
136 }
137 }
138
139 pub fn nargs(&self) -> u16 {
141 unsafe { JS_GetFunctionArity(self.get()) }
142 }
143
144 pub fn length(&self, cx: &Context) -> Option<u16> {
146 let mut length = 0;
147 unsafe { JS_GetFunctionLength(cx.as_ptr(), self.handle().into(), &raw mut length) }.then_some(length)
148 }
149
150 pub fn call<'cx>(
154 &self, cx: &'cx Context, this: &Object, args: &[Value],
155 ) -> Result<Value<'cx>, Option<ErrorReport>> {
156 let mut root = RootableVec::new_unrooted();
157 let args = RootedVec::from_iter(&mut root, args.iter().map(|a| a.get()));
158 self.call_with_handle(cx, this, HandleValueArray::from(&args))
159 }
160
161 pub fn call_with_handle<'cx>(
165 &self, cx: &'cx Context, this: &Object, args: HandleValueArray,
166 ) -> Result<Value<'cx>, Option<ErrorReport>> {
167 let mut rval = Value::undefined(cx);
168 if unsafe {
169 JS_CallFunction(
170 cx.as_ptr(),
171 this.handle().into(),
172 self.handle().into(),
173 &raw const args,
174 rval.handle_mut().into(),
175 )
176 } {
177 Ok(rval)
178 } else {
179 Err(ErrorReport::new_with_exception_stack(cx).unwrap())
180 }
181 }
182
183 pub fn is_eval(&self) -> bool {
185 unsafe { JS_IsBuiltinEvalFunction(self.get()) }
186 }
187
188 pub fn is_constructor(&self) -> bool {
190 unsafe { JS_IsConstructor(self.get()) }
191 }
192
193 pub fn is_function_constructor(&self) -> bool {
195 unsafe { JS_IsBuiltinFunctionConstructor(self.get()) }
196 }
197
198 pub unsafe fn is_function_raw(obj: *mut JSObject) -> bool {
200 unsafe { JS_ObjectIsFunction(obj) }
201 }
202}
203
204impl<'f> From<Local<'f, *mut JSFunction>> for Function<'f> {
205 fn from(function: Local<'f, *mut JSFunction>) -> Function<'f> {
206 Function { function }
207 }
208}
209
210impl<'f> Deref for Function<'f> {
211 type Target = Local<'f, *mut JSFunction>;
212
213 fn deref(&self) -> &Self::Target {
214 &self.function
215 }
216}