rquickjs_core/value/function/
args.rs1use crate::{
2 function::{Flat, Opt, Rest, This},
3 qjs, Ctx, FromJs, Function, IntoJs, Result, Value,
4};
5use alloc::vec::Vec;
6
7use super::{ffi::defer_call_job, Constructor};
8
9const ARGS_ON_STACK: usize = 4;
10
11pub enum ArgsSlice {
12 Stack {
13 slice: [qjs::JSValue; ARGS_ON_STACK],
14 offset: u8,
15 },
16 Heap(Vec<qjs::JSValue>),
17}
18
19pub struct Args<'js> {
23 ctx: Ctx<'js>,
24 pub(crate) this: qjs::JSValue,
25 pub(crate) args: ArgsSlice,
26}
27
28impl<'js> Args<'js> {
29 pub fn new(ctx: Ctx<'js>, args: usize) -> Args<'js> {
31 Args {
32 ctx,
33 this: qjs::JS_UNDEFINED,
34 args: if args <= ARGS_ON_STACK {
35 ArgsSlice::Stack {
36 slice: [qjs::JS_UNDEFINED; ARGS_ON_STACK],
37 offset: 0,
38 }
39 } else {
40 ArgsSlice::Heap(Vec::with_capacity(args))
41 },
42 }
43 }
44
45 pub fn new_unsized(ctx: Ctx<'js>) -> Args<'js> {
47 Args {
48 ctx,
49 this: qjs::JS_UNDEFINED,
50 args: ArgsSlice::Heap(Vec::new()),
51 }
52 }
53
54 pub fn ctx(&self) -> &Ctx<'js> {
56 &self.ctx
57 }
58
59 pub fn push_arg<T: IntoJs<'js>>(&mut self, arg: T) -> Result<()> {
61 let v = arg.into_js(&self.ctx)?;
62
63 match self.args {
64 ArgsSlice::Stack {
65 ref mut slice,
66 ref mut offset,
67 } => {
68 if *offset >= 8 {
69 panic!("pushed more arguments than num_args returned");
70 }
71 slice[*offset as usize] = v.into_js_value();
72 *offset += 1;
73 }
74 ArgsSlice::Heap(ref mut h) => h.push(v.into_js_value()),
75 }
76
77 Ok(())
78 }
79
80 pub fn push_args<T, I>(&mut self, iter: I) -> Result<()>
82 where
83 T: IntoJs<'js>,
84 I: IntoIterator<Item = T>,
85 {
86 for a in iter.into_iter() {
87 self.push_arg(a)?
88 }
89 Ok(())
90 }
91
92 pub fn this<T>(&mut self, this: T) -> Result<()>
94 where
95 T: IntoJs<'js>,
96 {
97 let v = this.into_js(&self.ctx)?;
98 let v = core::mem::replace(&mut self.this, v.into_js_value());
99 unsafe { qjs::JS_FreeValue(self.ctx.as_ptr(), v) };
100 Ok(())
101 }
102
103 pub fn take_this(&mut self) -> Value<'js> {
105 let value = core::mem::replace(&mut self.this, qjs::JS_UNDEFINED);
106 Value {
107 ctx: self.ctx().clone(),
108 value,
109 }
110 }
111
112 fn len(&self) -> usize {
114 match self.args {
115 ArgsSlice::Stack { offset, .. } => offset as usize,
116 ArgsSlice::Heap(ref h) => h.len(),
117 }
118 }
119
120 fn as_ptr(&self) -> *const qjs::JSValue {
121 match self.args {
122 ArgsSlice::Stack { ref slice, .. } => slice.as_ptr(),
123 ArgsSlice::Heap(ref h) => h.as_ptr(),
124 }
125 }
126
127 pub fn apply<R>(self, func: &Function<'js>) -> Result<R>
129 where
130 R: FromJs<'js>,
131 {
132 let val = unsafe {
133 #[cfg(feature = "parallel")]
134 qjs::JS_UpdateStackTop(qjs::JS_GetRuntime(self.ctx.as_ptr()));
135
136 let val = qjs::JS_Call(
137 self.ctx.as_ptr(),
138 func.as_js_value(),
139 self.this,
140 self.len() as _,
141 self.as_ptr() as _,
142 );
143 let val = self.ctx.handle_exception(val)?;
144 Value::from_js_value(self.ctx.clone(), val)
145 };
146 R::from_js(&self.ctx, val)
147 }
148
149 pub fn defer(mut self, func: Function<'js>) -> Result<()> {
150 let this = self.take_this();
151 self.push_arg(this)?;
152 self.push_arg(func)?;
153 let ctx = self.ctx();
154 unsafe {
155 if qjs::JS_EnqueueJob(
156 ctx.as_ptr(),
157 Some(defer_call_job),
158 self.len() as _,
159 self.as_ptr() as _,
160 ) < 0
161 {
162 return Err(ctx.raise_exception());
163 }
164 }
165 Ok(())
166 }
167
168 pub fn construct<R>(self, constructor: &Constructor<'js>) -> Result<R>
169 where
170 R: FromJs<'js>,
171 {
172 let value = if unsafe { qjs::JS_VALUE_GET_TAG(self.this) != qjs::JS_TAG_UNDEFINED } {
173 unsafe {
174 #[cfg(feature = "parallel")]
175 qjs::JS_UpdateStackTop(qjs::JS_GetRuntime(self.ctx.as_ptr()));
176
177 qjs::JS_CallConstructor2(
178 self.ctx.as_ptr(),
179 constructor.as_js_value(),
180 self.this,
181 self.len() as _,
182 self.as_ptr() as _,
183 )
184 }
185 } else {
186 unsafe {
187 #[cfg(feature = "parallel")]
188 qjs::JS_UpdateStackTop(qjs::JS_GetRuntime(self.ctx.as_ptr()));
189
190 qjs::JS_CallConstructor(
191 self.ctx.as_ptr(),
192 constructor.as_js_value(),
193 self.len() as _,
194 self.as_ptr() as _,
195 )
196 }
197 };
198 let value = unsafe { self.ctx.handle_exception(value)? };
199 let v = unsafe { Value::from_js_value(self.ctx.clone(), value) };
200 R::from_js(&self.ctx, v)
201 }
202}
203
204impl Drop for Args<'_> {
205 fn drop(&mut self) {
206 match self.args {
207 ArgsSlice::Heap(ref h) => h.iter().for_each(|v| {
208 unsafe { qjs::JS_FreeValue(self.ctx.as_ptr(), *v) };
209 }),
210 ArgsSlice::Stack { ref slice, offset } => {
211 slice[..(offset as usize)].iter().for_each(|v| {
212 unsafe { qjs::JS_FreeValue(self.ctx.as_ptr(), *v) };
213 })
214 }
215 }
216 unsafe { qjs::JS_FreeValue(self.ctx.as_ptr(), self.this) };
217 }
218}
219
220pub trait IntoArg<'js> {
222 fn num_args(&self) -> usize;
224
225 fn into_arg(self, args: &mut Args<'js>) -> Result<()>;
227}
228
229pub trait IntoArgs<'js> {
231 fn num_args(&self) -> usize;
233
234 fn into_args(self, args: &mut Args<'js>) -> Result<()>;
236
237 fn apply<R>(self, function: &Function<'js>) -> Result<R>
238 where
239 R: FromJs<'js>,
240 Self: Sized,
241 {
242 let mut args = Args::new(function.ctx().clone(), self.num_args());
243 self.into_args(&mut args)?;
244 args.apply(function)
245 }
246
247 fn defer<R>(self, function: Function<'js>) -> Result<()>
248 where
249 Self: Sized,
250 {
251 let mut args = Args::new(function.ctx().clone(), self.num_args());
252 self.into_args(&mut args)?;
253 args.defer(function)
254 }
255
256 fn construct<R>(self, function: &Constructor<'js>) -> Result<()>
257 where
258 Self: Sized,
259 {
260 let mut args = Args::new(function.ctx().clone(), self.num_args());
261 self.into_args(&mut args)?;
262 args.construct(function)
263 }
264}
265
266impl<'js, T: IntoJs<'js>> IntoArg<'js> for T {
267 fn num_args(&self) -> usize {
268 1
269 }
270
271 fn into_arg(self, args: &mut Args<'js>) -> Result<()> {
272 args.push_arg(self)
273 }
274}
275
276impl<'js, T: IntoJs<'js>> IntoArg<'js> for This<T> {
277 fn num_args(&self) -> usize {
278 0
279 }
280
281 fn into_arg(self, args: &mut Args<'js>) -> Result<()> {
282 args.this(self.0)
283 }
284}
285
286impl<'js, T: IntoJs<'js>> IntoArg<'js> for Opt<T> {
287 fn num_args(&self) -> usize {
288 self.0.is_some() as usize
289 }
290
291 fn into_arg(self, args: &mut Args<'js>) -> Result<()> {
292 if let Some(x) = self.0 {
293 args.push_arg(x)?
294 }
295 Ok(())
296 }
297}
298
299impl<'js, T: IntoJs<'js>> IntoArg<'js> for Rest<T> {
300 fn num_args(&self) -> usize {
301 self.0.len()
302 }
303
304 fn into_arg(self, args: &mut Args<'js>) -> Result<()> {
305 args.push_args(self.0)
306 }
307}
308
309impl<'js, T: IntoArgs<'js>> IntoArg<'js> for Flat<T> {
310 fn num_args(&self) -> usize {
311 self.0.num_args()
312 }
313
314 fn into_arg(self, args: &mut Args<'js>) -> Result<()> {
315 self.0.into_args(args)
316 }
317}
318
319macro_rules! impl_into_args {
320 ($($t:ident),*) => {
321 #[allow(non_snake_case)]
322 impl<'js $(,$t)*> IntoArgs<'js> for ($($t,)*)
323 where
324 $($t : IntoArg<'js>,)*
325 {
326 fn num_args(&self) -> usize{
327 let ($(ref $t,)*) = *self;
328 0 $(+ $t.num_args())*
329 }
330
331 fn into_args(self, _args: &mut Args<'js>) -> Result<()>{
332 let ($($t,)*) = self;
333 $($t.into_arg(_args)?;)*
334 Ok(())
335 }
336 }
337 };
338}
339
340impl_into_args!();
341impl_into_args!(A);
342impl_into_args!(A, B);
343impl_into_args!(A, B, C);
344impl_into_args!(A, B, C, D);
345impl_into_args!(A, B, C, D, E);
346impl_into_args!(A, B, C, D, E, F);
347impl_into_args!(A, B, C, D, E, F, G);