1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use syn::{parse_macro_input, FnArg, ItemFn, PatType, Type, TypePath};
4
5#[proc_macro_attribute]
6pub fn callback(_attr: TokenStream, item: TokenStream) -> TokenStream {
7 let input = parse_macro_input!(item as ItemFn);
8 let fn_name = &input.sig.ident;
9 let visibility = &input.vis;
10 let generics = &input.sig.generics;
11 let generic_params = &generics.params;
12 let where_clause = &generics.where_clause;
13
14 let params: Vec<_> = input.sig.inputs.iter().skip(3).collect();
17
18 if params.len() == 1 {
20 if let FnArg::Typed(PatType { ty, .. }) = ¶ms[0] {
21 if let Type::Reference(_) = &**ty {
22 return generate_legacy_callback(&input, fn_name, visibility, generics);
24 }
25 }
26 }
27
28 let mut parse_stmts = Vec::new();
30 for (i, param) in params.iter().enumerate() {
31 if let FnArg::Typed(PatType { pat, ty, .. }) = param {
32 let idx = syn::Index::from(i);
33 let var_ident = format_ident!("arg_{}", i);
34 let param_name = quote!(#pat).to_string();
35
36 parse_stmts.push(match &**ty {
37 Type::Path(TypePath { path, .. }) => {
38 let is_optional = path
39 .segments
40 .last()
41 .map(|s| s.ident == "Option")
42 .unwrap_or(false);
43
44 if is_optional {
45 generate_optional_param_parsing(idx, var_ident)
46 } else {
47 generate_required_param_parsing(
48 idx,
49 var_ident,
50 fn_name.to_string().as_str(),
51 param_name.as_str(),
52 )
53 }
54 }
55 _ => quote! {
56 panic!("[callback] Unsupported parameter type for {}", #param_name);
57 },
58 });
59 }
60 }
61
62 let call_args: Vec<_> = params
63 .iter()
64 .enumerate()
65 .map(|(i, _)| {
66 let var_ident = format_ident!("arg_{}", i);
67 quote!(#var_ident)
68 })
69 .collect();
70
71 let func_call = quote! {
72 #fn_name ::<#generic_params>(ctx, function, this_object, #(#call_args),*)
73 };
74
75 let expanded = quote! {
76 #visibility unsafe extern "C" fn #fn_name <#generic_params> (
77 __ctx_ref: rust_jsc::internal::JSContextRef,
78 __function: rust_jsc::internal::JSObjectRef,
79 __this_object: rust_jsc::internal::JSObjectRef,
80 __argument_count: usize,
81 __arguments: *const rust_jsc::internal::JSValueRef,
82 __exception: *mut rust_jsc::internal::JSValueRef,
83 ) -> *const rust_jsc::internal::OpaqueJSValue
84 #where_clause {
85 let ctx = rust_jsc::JSContext::from(__ctx_ref);
86 let function = rust_jsc::JSObject::from_ref(__function, __ctx_ref);
87 let this_object = rust_jsc::JSObject::from_ref(__this_object, __ctx_ref);
88 let arguments = if __arguments.is_null() || __argument_count == 0 {
89 vec![]
90 } else {
91 unsafe { std::slice::from_raw_parts(__arguments, __argument_count) }
92 .iter()
93 .map(|__inner_value| rust_jsc::JSValue::new(*__inner_value, __ctx_ref))
94 .collect::<Vec<_>>()
95 };
96
97 #(#parse_stmts)*
98
99 let result = (|| {
100 #input
101 #func_call
102 })();
103
104 match result {
105 Ok(value) => {
106 *__exception = std::ptr::null_mut();
107 value.into()
108 }
109 Err(exception) => {
110 *__exception = rust_jsc::internal::JSValueRef::from(exception) as *mut _;
111 std::ptr::null_mut()
112 }
113 }
114 }
115 };
116
117 TokenStream::from(expanded)
118}
119
120fn generate_optional_param_parsing(
121 idx: syn::Index,
122 var_ident: syn::Ident,
123) -> proc_macro2::TokenStream {
124 quote! {
125 let #var_ident = match arguments.get(#idx).map(|value| value.try_into()) {
126 Some(Ok(value)) => Some(value),
127 Some(Err(err)) => {
128 *__exception = rust_jsc::internal::JSValueRef::from(err) as *mut _;
129 return std::ptr::null_mut();
130 },
131 None => None,
132 };
133 }
134}
135
136fn generate_required_param_parsing(
137 idx: syn::Index,
138 var_ident: syn::Ident,
139 fn_name: &str,
140 param_name: &str,
141) -> proc_macro2::TokenStream {
142 quote! {
143 let #var_ident = match arguments.get(#idx).map(|value| value.try_into()) {
144 Some(Ok(value)) => value,
145 Some(Err(err)) => {
146 *__exception = rust_jsc::internal::JSValueRef::from(err) as *mut _;
147 return std::ptr::null_mut();
148 },
149 None => {
150 *__exception = rust_jsc::JSError::new_typ_raw(&ctx, format!("[{}] Missing argument {}", #fn_name, #param_name)) as *mut _;
151 return std::ptr::null_mut();
152 },
153 };
154 }
155}
156
157fn generate_legacy_callback(
158 input: &ItemFn,
159 fn_name: &syn::Ident,
160 visibility: &syn::Visibility,
161 generics: &syn::Generics,
162) -> TokenStream {
163 let generic_params = &generics.params;
164 let where_clause = &generics.where_clause;
165
166 let expanded = quote! {
167 #visibility unsafe extern "C" fn #fn_name <#generic_params> (
168 __ctx_ref: rust_jsc::internal::JSContextRef,
169 __function: rust_jsc::internal::JSObjectRef,
170 __this_object: rust_jsc::internal::JSObjectRef,
171 __argument_count: usize,
172 __arguments: *const rust_jsc::internal::JSValueRef,
173 __exception: *mut rust_jsc::internal::JSValueRef,
174 ) -> *const rust_jsc::internal::OpaqueJSValue
175 #where_clause {
176 let ctx = rust_jsc::JSContext::from(__ctx_ref);
177 let function = rust_jsc::JSObject::from_ref(__function, __ctx_ref);
178 let this_object = rust_jsc::JSObject::from_ref(__this_object, __ctx_ref);
179 let arguments = if __arguments.is_null() || __argument_count == 0 {
180 vec![]
181 } else {
182 unsafe { std::slice::from_raw_parts(__arguments, __argument_count) }
183 .iter()
184 .map(|__inner_value| rust_jsc::JSValue::new(*__inner_value, __ctx_ref))
185 .collect::<Vec<_>>()
186 };
187
188 let func: fn(
189 rust_jsc::JSContext,
190 rust_jsc::JSObject,
191 rust_jsc::JSObject,
192 &[rust_jsc::JSValue],
193 ) -> rust_jsc::JSResult<rust_jsc::JSValue> = {
194 #input
195
196 #fn_name ::<#generic_params>
197 };
198
199 let result = func(ctx, function, this_object, arguments.as_slice());
200
201 match result {
202 Ok(value) => {
203 *__exception = std::ptr::null_mut();
204 value.into()
205 }
206 Err(exception) => {
207 *__exception = rust_jsc::internal::JSValueRef::from(exception) as *mut _;
208 std::ptr::null_mut()
209 }
210 }
211 }
212 };
213
214 TokenStream::from(expanded)
215}
216
217#[proc_macro_attribute]
218pub fn constructor(_attr: TokenStream, item: TokenStream) -> TokenStream {
219 let input = parse_macro_input!(item as ItemFn);
220 let fn_name = &input.sig.ident;
221 let visibility = &input.vis;
222 let generics = &input.sig.generics;
223 let generic_params = &generics.params;
224 let where_clause = &generics.where_clause;
225
226 let expanded = quote! {
227 #visibility unsafe extern "C" fn #fn_name <#generic_params> (
228 __ctx_ref: rust_jsc::internal::JSContextRef,
229 __constructor: rust_jsc::internal::JSObjectRef,
230 __argument_count: usize,
231 __arguments: *const rust_jsc::internal::JSValueRef,
232 __exception: *mut rust_jsc::internal::JSValueRef,
233 ) -> *mut rust_jsc::internal::OpaqueJSValue
234 #where_clause {
235 let ctx = rust_jsc::JSContext::from(__ctx_ref);
236 let constructor = rust_jsc::JSObject::from_ref(__constructor, __ctx_ref);
237 let arguments = if __arguments.is_null() || __argument_count == 0 {
238 vec![]
239 } else {
240 unsafe { std::slice::from_raw_parts(__arguments, __argument_count) }
241 .iter()
242 .map(|__inner_value| rust_jsc::JSValue::new(*__inner_value, __ctx_ref))
243 .collect::<Vec<_>>()
244 };
245
246 let func: fn(
247 rust_jsc::JSContext,
248 rust_jsc::JSObject,
249 &[rust_jsc::JSValue],
250 ) -> rust_jsc::JSResult<rust_jsc::JSValue> = {
251 #input
252
253 #fn_name ::<#generic_params>
254 };
255
256 let result = func(ctx, constructor, arguments.as_slice());
257
258 match result {
259 Ok(value) => {
260 *__exception = std::ptr::null_mut();
261 value.into()
262 }
263 Err(exception) => {
264 *__exception = rust_jsc::internal::JSValueRef::from(exception) as *mut _;
265 std::ptr::null_mut()
266 }
267 }
268 }
269 };
270
271 TokenStream::from(expanded)
272}
273
274#[proc_macro_attribute]
275pub fn initialize(_attr: TokenStream, item: TokenStream) -> TokenStream {
276 let input = parse_macro_input!(item as ItemFn);
277 let fn_name = &input.sig.ident;
278 let visibility = &input.vis;
279 let generics = &input.sig.generics;
280 let generic_params = &generics.params;
281 let where_clause = &generics.where_clause;
282
283 let expanded = quote! {
284 #visibility unsafe extern "C" fn #fn_name <#generic_params> (
285 __ctx_ref: rust_jsc::internal::JSContextRef,
286 __object: rust_jsc::internal::JSObjectRef,
287 )
288 #where_clause {
289 let ctx = rust_jsc::JSContext::from(__ctx_ref);
290 let object = rust_jsc::JSObject::from_ref(__object, __ctx_ref);
291
292 let func: fn(
293 rust_jsc::JSContext,
294 rust_jsc::JSObject,
295 ) = {
296 #input
297
298 #fn_name ::<#generic_params>
299 };
300
301 func(ctx, object);
302 }
303 };
304
305 TokenStream::from(expanded)
306}
307
308#[proc_macro_attribute]
309pub fn finalize(_attr: TokenStream, item: TokenStream) -> TokenStream {
310 let input = parse_macro_input!(item as ItemFn);
311 let fn_name = &input.sig.ident;
312 let visibility = &input.vis;
313 let generics = &input.sig.generics;
314 let generic_params = &generics.params;
315 let where_clause = &generics.where_clause;
316
317 let expanded = quote! {
318 #visibility unsafe extern "C" fn #fn_name <#generic_params> (
319 __object: rust_jsc::internal::JSObjectRef,
320 )
321 #where_clause {
322 let data_ptr = rust_jsc::internal::JSObjectGetPrivate(__object);
323
324 let func: fn(
325 rust_jsc::PrivateData
326 ) = {
327 #input
328
329 #fn_name ::<#generic_params>
330 };
331
332 func(data_ptr);
333 }
334 };
335
336 TokenStream::from(expanded)
337}
338
339#[proc_macro_attribute]
340pub fn has_instance(_attr: TokenStream, item: TokenStream) -> TokenStream {
341 let input = parse_macro_input!(item as ItemFn);
342 let fn_name = &input.sig.ident;
343 let visibility = &input.vis;
344 let generics = &input.sig.generics;
345 let generic_params = &generics.params;
346 let where_clause = &generics.where_clause;
347
348 let expanded = quote! {
349 #visibility unsafe extern "C" fn #fn_name <#generic_params> (
350 __ctx_ref: rust_jsc::internal::JSContextRef,
351 __constructor: rust_jsc::internal::JSObjectRef,
352 __possible_instance: rust_jsc::internal::JSValueRef,
353 __exception: *mut rust_jsc::internal::JSValueRef,
354 ) -> bool
355 #where_clause {
356 let ctx = rust_jsc::JSContext::from(__ctx_ref);
357 let constructor = rust_jsc::JSObject::from_ref(__constructor, __ctx_ref);
358 let possible_instance = rust_jsc::JSValue::new(__possible_instance, __ctx_ref);
359
360 let func: fn(
361 rust_jsc::JSContext,
362 rust_jsc::JSObject,
363 rust_jsc::JSValue,
364 ) -> rust_jsc::JSResult<bool> = {
365 #input
366
367 #fn_name ::<#generic_params>
368 };
369
370 let result = func(ctx, constructor, possible_instance);
371
372 match result {
373 Ok(value) => {
374 *__exception = std::ptr::null_mut();
375 value
376 }
377 Err(exception) => {
378 *__exception = rust_jsc::internal::JSValueRef::from(exception) as *mut _;
379 false
380 }
381 }
382 }
383 };
384
385 TokenStream::from(expanded)
386}
387
388#[proc_macro_attribute]
389pub fn module_resolve(_attr: TokenStream, item: TokenStream) -> TokenStream {
390 let input = parse_macro_input!(item as ItemFn);
391 let fn_name = &input.sig.ident;
392 let visibility = &input.vis;
393 let generics = &input.sig.generics;
394 let generic_params = &generics.params;
395 let where_clause = &generics.where_clause;
396
397 let expanded = quote! {
398 #visibility unsafe extern "C" fn #fn_name <#generic_params> (
399 __ctx_ref: rust_jsc::internal::JSContextRef,
400 __key_value: rust_jsc::internal::JSValueRef,
401 __referrer: rust_jsc::internal::JSValueRef,
402 __script_fetcher: rust_jsc::internal::JSValueRef,
403 ) -> *mut rust_jsc::internal::OpaqueJSString
404 #where_clause {
405 let ctx = rust_jsc::JSContext::from(__ctx_ref);
406 let key_value = rust_jsc::JSValue::new(__key_value, __ctx_ref);
407 let referrer = rust_jsc::JSValue::new(__referrer, __ctx_ref);
408 let script_fetcher = rust_jsc::JSValue::new(__script_fetcher, __ctx_ref);
409
410 let func: fn(
411 rust_jsc::JSContext,
412 rust_jsc::JSValue,
413 rust_jsc::JSValue,
414 rust_jsc::JSValue,
415 ) -> rust_jsc::JSStringProctected = {
416 #input
417
418 #fn_name ::<#generic_params>
419 };
420
421 let result = func(ctx, key_value, referrer, script_fetcher);
422 rust_jsc::internal::JSStringRef::from(result)
423 }
424 };
425
426 TokenStream::from(expanded)
427}
428
429#[proc_macro_attribute]
430pub fn module_evaluate(_attr: TokenStream, item: TokenStream) -> TokenStream {
431 let input = parse_macro_input!(item as ItemFn);
432 let fn_name = &input.sig.ident;
433 let visibility = &input.vis;
434 let generics = &input.sig.generics;
435 let generic_params = &generics.params;
436 let where_clause = &generics.where_clause;
437
438 let expanded = quote! {
439 #visibility unsafe extern "C" fn #fn_name <#generic_params> (
440 __ctx_ref: rust_jsc::internal::JSContextRef,
441 __key_value: rust_jsc::internal::JSValueRef,
442 ) -> *const rust_jsc::internal::OpaqueJSValue
443 #where_clause {
444 let ctx = rust_jsc::JSContext::from(__ctx_ref);
445 let key_value = rust_jsc::JSValue::new(__key_value, __ctx_ref);
446
447 let func: fn(
448 rust_jsc::JSContext,
449 rust_jsc::JSValue,
450 ) -> rust_jsc::JSValue = {
451 #input
452
453 #fn_name ::<#generic_params>
454 };
455
456 let result = func(ctx, key_value);
457 result.into()
458 }
459 };
460
461 TokenStream::from(expanded)
462}
463
464#[proc_macro_attribute]
465pub fn module_fetch(_attr: TokenStream, item: TokenStream) -> TokenStream {
466 let input = parse_macro_input!(item as ItemFn);
467 let fn_name = &input.sig.ident;
468 let visibility = &input.vis;
469 let generics = &input.sig.generics;
470 let generic_params = &generics.params;
471 let where_clause = &generics.where_clause;
472
473 let expanded = quote! {
474 #visibility unsafe extern "C" fn #fn_name <#generic_params> (
475 __ctx_ref: rust_jsc::internal::JSContextRef,
476 __key_value: rust_jsc::internal::JSValueRef,
477 __attributes_value: rust_jsc::internal::JSValueRef,
478 __script_fetcher: rust_jsc::internal::JSValueRef,
479 ) -> *mut rust_jsc::internal::OpaqueJSString
480 #where_clause {
481 let ctx = rust_jsc::JSContext::from(__ctx_ref);
482 let key_value = rust_jsc::JSValue::new(__key_value, __ctx_ref);
483 let attributes_value = rust_jsc::JSValue::new(__attributes_value, __ctx_ref);
484 let script_fetcher = rust_jsc::JSValue::new(__script_fetcher, __ctx_ref);
485
486 let func: fn(
487 rust_jsc::JSContext,
488 rust_jsc::JSValue,
489 rust_jsc::JSValue,
490 rust_jsc::JSValue,
491 ) -> rust_jsc::JSStringProctected = {
492 #input
493
494 #fn_name ::<#generic_params>
495 };
496
497 let result = func(ctx, key_value, attributes_value, script_fetcher);
498 rust_jsc::internal::JSStringRef::from(result)
499 }
500 };
501
502 TokenStream::from(expanded)
503}
504
505#[proc_macro_attribute]
506pub fn module_import_meta(_attr: TokenStream, item: TokenStream) -> TokenStream {
507 let input = parse_macro_input!(item as ItemFn);
508 let fn_name = &input.sig.ident;
509 let visibility = &input.vis;
510 let generics = &input.sig.generics;
511 let generic_params = &generics.params;
512 let where_clause = &generics.where_clause;
513
514 let expanded = quote! {
515 #visibility unsafe extern "C" fn #fn_name <#generic_params> (
516 __ctx_ref: rust_jsc::internal::JSContextRef,
517 __key_value: rust_jsc::internal::JSValueRef,
518 __script_fetcher: rust_jsc::internal::JSValueRef,
519 ) -> *mut rust_jsc::internal::OpaqueJSValue
520 #where_clause {
521 let ctx = rust_jsc::JSContext::from(__ctx_ref);
522 let key_value = rust_jsc::JSValue::new(__key_value, __ctx_ref);
523 let script_fetcher = rust_jsc::JSValue::new(__script_fetcher, __ctx_ref);
524
525 let func: fn(
526 rust_jsc::JSContext,
527 rust_jsc::JSValue,
528 rust_jsc::JSValue,
529 ) -> rust_jsc::JSObject = {
530 #input
531
532 #fn_name ::<#generic_params>
533 };
534
535 let result = func(ctx, key_value, script_fetcher);
536 rust_jsc::internal::JSObjectRef::from(result)
537 }
538 };
539
540 TokenStream::from(expanded)
541}
542
543#[proc_macro_attribute]
544pub fn uncaught_exception(_attr: TokenStream, item: TokenStream) -> TokenStream {
545 let input = parse_macro_input!(item as ItemFn);
546 let fn_name = &input.sig.ident;
547 let visibility = &input.vis;
548 let generics = &input.sig.generics;
549 let generic_params = &generics.params;
550 let where_clause = &generics.where_clause;
551
552 let expanded = quote! {
553 #visibility unsafe extern "C" fn #fn_name <#generic_params> (
554 __ctx_ref: rust_jsc::internal::JSContextRef,
555 __filename: rust_jsc::internal::JSStringRef,
556 __exception: rust_jsc::internal::JSValueRef,
557 ) #where_clause {
558 let ctx = rust_jsc::JSContext::from(__ctx_ref);
559 let filename = rust_jsc::JSString::from(__filename);
560 let exception = rust_jsc::JSValue::new(__exception, __ctx_ref);
561
562 let func: fn(
563 rust_jsc::JSContext,
564 rust_jsc::JSString,
565 rust_jsc::JSValue,
566 ) = {
567 #input
568
569 #fn_name ::<#generic_params>
570 };
571
572 func(ctx, filename, exception);
573 }
574 };
575
576 TokenStream::from(expanded)
577}
578
579#[proc_macro_attribute]
580pub fn uncaught_exception_event_loop(
581 _attr: TokenStream,
582 item: TokenStream,
583) -> TokenStream {
584 let input = parse_macro_input!(item as ItemFn);
585 let fn_name = &input.sig.ident;
586 let visibility = &input.vis;
587 let generics = &input.sig.generics;
588 let generic_params = &generics.params;
589 let where_clause = &generics.where_clause;
590
591 let expanded = quote! {
592 #visibility unsafe extern "C" fn #fn_name <#generic_params> (
593 __ctx_ref: rust_jsc::internal::JSContextRef,
594 __exception: rust_jsc::internal::JSValueRef,
595 ) #where_clause {
596 let ctx = rust_jsc::JSContext::from(__ctx_ref);
597 let exception = rust_jsc::JSValue::new(__exception, __ctx_ref);
598
599 let func: fn(
600 rust_jsc::JSContext,
601 rust_jsc::JSValue,
602 ) = {
603 #input
604
605 #fn_name ::<#generic_params>
606 };
607
608 func(ctx, exception);
609 }
610 };
611
612 TokenStream::from(expanded)
613}