Skip to content

Commit 4fafe4a

Browse files
authored
feat: Add JSObject::call_as_function (#13)
* feat: Add `JSObject::call_as_function`. This patch implements `call_as_function` on `JSObject`. Behind the scene, it uses `sys::JSObjectCallAsFunction`.
1 parent 4c8dd5a commit 4fafe4a

File tree

1 file changed

+91
-2
lines changed

1 file changed

+91
-2
lines changed

Diff for: src/object.rs

+91-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
// option. This file may not be copied, modified, or distributed
55
// except according to those terms.
66

7-
use super::{JSObject, JSString, JSValue};
8-
use crate::sys;
7+
use super::{JSObject, JSString};
8+
use crate::{sys, JSContext, JSException, JSValue};
99
use std::ops::Deref;
1010
use std::ptr;
1111

@@ -133,6 +133,70 @@ impl JSObject {
133133
ctx: self.value.ctx,
134134
}
135135
}
136+
137+
/// Call this object considering it is a valid function.
138+
///
139+
/// ```rust
140+
/// # use javascriptcore::{JSContext, JSValue};
141+
/// let ctx = JSContext::default();
142+
/// let global = ctx.global_object().unwrap();
143+
/// let math = global.get_property("Math").as_object().unwrap();
144+
/// let pow = math.get_property("pow").as_object().unwrap();
145+
///
146+
/// let result = pow.call_as_function(
147+
/// &ctx,
148+
/// None,
149+
/// &[JSValue::new_number(&ctx, 2.), JSValue::new_number(&ctx, 3.)],
150+
/// ).unwrap();
151+
///
152+
/// assert_eq!(result.as_number().unwrap(), 8.);
153+
/// ```
154+
pub fn call_as_function(
155+
&self,
156+
context: &JSContext,
157+
this: Option<&JSObject>,
158+
arguments: &[JSValue],
159+
) -> Result<JSValue, JSException> {
160+
let arguments = arguments
161+
.iter()
162+
.map(|argument| argument.raw)
163+
.collect::<Vec<_>>();
164+
let mut exception: sys::JSValueRef = ptr::null_mut();
165+
166+
let result = unsafe {
167+
sys::JSObjectCallAsFunction(
168+
context.raw,
169+
self.raw,
170+
this.map(|this| this.raw).unwrap_or_else(ptr::null_mut),
171+
arguments.len(),
172+
arguments.as_slice().as_ptr(),
173+
&mut exception,
174+
)
175+
};
176+
177+
if !exception.is_null() {
178+
return Err(JSException {
179+
value: JSValue {
180+
raw: exception,
181+
ctx: context.raw,
182+
},
183+
});
184+
}
185+
186+
if result.is_null() {
187+
return Err(JSException {
188+
value: JSValue::new_string(
189+
context,
190+
"Cannot call this object as a function: it is not a valid function",
191+
),
192+
});
193+
}
194+
195+
Ok(JSValue {
196+
raw: result,
197+
ctx: context.raw,
198+
})
199+
}
136200
}
137201

138202
/// A `JSObject` can be dereferenced to return the underlying `JSValue`.
@@ -173,6 +237,8 @@ impl Iterator for JSObjectPropertyNameIter {
173237

174238
#[cfg(test)]
175239
mod tests {
240+
use crate::JSException;
241+
176242
use super::super::{JSContext, JSValue};
177243

178244
#[test]
@@ -222,4 +288,27 @@ mod tests {
222288
assert!(v.is_object());
223289
assert!(o.is_object());
224290
}
291+
292+
#[test]
293+
fn call_as_function() -> Result<(), JSException> {
294+
let ctx = JSContext::default();
295+
let global = ctx.global_object()?;
296+
let math = global.get_property("Math").as_object()?;
297+
let pow = math.get_property("pow").as_object()?;
298+
299+
let result = pow.call_as_function(
300+
&ctx,
301+
None,
302+
&[JSValue::new_number(&ctx, 2.), JSValue::new_number(&ctx, 3.)],
303+
)?;
304+
305+
assert_eq!(result.as_number()?, 8.);
306+
307+
// Not a function, it's a constant.
308+
let e = math.get_property("E").as_object()?;
309+
310+
assert!(e.call_as_function(&ctx, None, &[]).is_err());
311+
312+
Ok(())
313+
}
225314
}

0 commit comments

Comments
 (0)