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