1use std::{borrow::Cow, str::FromStr};
4
5use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
6
7mod swift;
8mod swift_mangle;
9
10enum Attr {
11 Optional,
12 MsgSend(String),
13 ApiAvailable(Versions),
14 DocAvailable,
15}
16
17impl Attr {
18 fn from_stream(stream: TokenStream) -> Option<Attr> {
19 let mut iter = stream.into_iter();
20 let Some(TokenTree::Ident(ident)) = iter.next() else {
21 return None;
22 };
23
24 let str = ident.to_string();
25 if str == "doc" {
26 let Some(TokenTree::Punct(p)) = iter.next() else {
27 return None;
28 };
29 assert_eq!(p, '=');
30 let Some(TokenTree::Literal(s)) = iter.next() else {
31 return None;
32 };
33 if s.to_string() == "\" # Availability\"" {
34 return Some(Attr::DocAvailable);
35 }
36 } else if str != "objc" && str != "api" {
37 return None;
38 }
39
40 let Some(TokenTree::Punct(p)) = iter.next() else {
41 return None;
42 };
43
44 assert_eq!(p, ':');
45
46 let Some(TokenTree::Punct(p)) = iter.next() else {
47 return None;
48 };
49
50 assert_eq!(p, ':');
51
52 if let Some(tt) = iter.next() {
53 match tt {
54 TokenTree::Group(v) => panic!("didn't expect group {v}"),
55 TokenTree::Ident(v) => {
56 let v = v.to_string();
57 return match v.as_str() {
58 "optional" => Some(Attr::Optional),
59 "msg_send" => {
60 let Some(TokenTree::Group(a)) = iter.next() else {
61 return None;
62 };
63 let sel = a.stream().to_string().replace([' ', '\n'], "");
64 Some(Attr::MsgSend(sel))
65 }
66 "available" => {
67 let Some(TokenTree::Group(a)) = iter.next() else {
68 return None;
69 };
70 Some(Attr::ApiAvailable(Versions::from_stream(a.stream())))
71 }
72 _ => None,
73 };
74 }
75 TokenTree::Punct(v) => panic!("didn't expect punct {v}"),
76 TokenTree::Literal(v) => panic!("didn't expect literal {v}"),
77 }
78 }
79
80 panic!("Unexpected attribute")
81 }
82}
83
84#[proc_macro_attribute]
87pub fn swift_call(decl: TokenStream, func: TokenStream) -> TokenStream {
88 swift::gen_swift_call(decl, func)
89}
90
91#[proc_macro]
93pub fn swift_symbol(args: TokenStream) -> TokenStream {
94 swift::gen_symbol(args)
95}
96
97#[proc_macro]
99pub fn swift_metadata_accessor(args: TokenStream) -> TokenStream {
100 swift::gen_metadata_accessor(args)
101}
102
103#[proc_macro_attribute]
106pub fn optional(_sel: TokenStream, func: TokenStream) -> TokenStream {
107 let mut iter = func.clone().into_iter();
108
109 let extern_name = loop {
110 let Some(TokenTree::Punct(p)) = iter.next() else {
111 panic!("expect #[objc::msg_send(...)]")
112 };
113 if p != '#' {
114 panic!("expect #[objc::msg_send(...)]")
115 }
116 let Some(TokenTree::Group(g)) = iter.next() else {
117 panic!("expect #[objc::msg_send(...)]")
118 };
119 if let Some(Attr::MsgSend(extern_name)) = Attr::from_stream(g.stream()) {
120 break extern_name;
121 }
122 };
123
124 let mut fn_name = None;
125
126 while let Some(tt) = iter.next() {
127 match tt {
128 TokenTree::Ident(i) if i.to_string().eq("fn") => {
129 let Some(TokenTree::Ident(name)) = iter.next() else {
130 panic!("expect function name");
131 };
132 fn_name = Some(name.to_string());
133 }
134 _ => {} }
136 }
137
138 let Some(fn_name) = fn_name else {
139 panic!("function name not found");
140 };
141
142 let getter: TokenStream = format!(
143 "
144 /// `@selector({extern_name})` but dynamic
145 /// use this function to check if object responds to selector
146 fn sel_{fn_name}() -> &'static objc::Sel {{
147 unsafe {{ objc::sel_reg_name(c\"{extern_name}\".as_ptr()) }}
148 }}
149 "
150 )
151 .parse()
152 .unwrap();
153
154 let mut func = func;
155 func.extend(getter);
156 func
157}
158
159#[proc_macro_attribute]
160pub fn protocol(args: TokenStream, ts: TokenStream) -> TokenStream {
161 let mut original_trait = ts.clone();
162 let error_msg = "objc::protocol expects protocol name as first argument";
163 let mut args = args.into_iter();
164 let Some(TokenTree::Ident(ident)) = args.next() else {
165 panic!("{}", error_msg);
166 };
167 assert!(args.next().is_none(), "{}", error_msg);
168 let protocol_name = ident.to_string();
169 let mut trait_name = String::new();
170
171 let mut pre_tokens = Vec::<TokenTree>::with_capacity(10);
172 let mut ts = ts.into_iter();
173 let mut group_stream = None;
174 while let Some(tt) = ts.next() {
175 match tt {
176 TokenTree::Group(ref g) if g.delimiter() == Delimiter::Brace => {
177 while let Some(tt) = pre_tokens.pop() {
178 let val = tt.to_string();
179 if val == "trait" {
180 pre_tokens.push(tt);
181 break;
182 }
183 trait_name = val;
184 }
185 group_stream = Some(g.stream());
186 break;
187 }
188 _ => pre_tokens.push(tt),
189 }
190 }
191 if trait_name != protocol_name {
192 let alias = format!("#[doc(alias = \"{protocol_name}\")]");
193 let stream = TokenStream::from_str(&alias).unwrap();
194 let mut alias_tokens = Vec::from_iter(stream.into_iter());
195 alias_tokens.append(&mut pre_tokens);
196 pre_tokens = alias_tokens;
197 }
198 let mut is_optional = false;
199 let mut skip = false;
200 let mut sel = String::new();
201 let mut fn_name; let mut generics = Vec::new();
203 let mut fn_args_str; let mut result = Vec::new();
206 let mut fn_body = Cow::Borrowed("");
207
208 let mut impl_trait_functions = vec![];
209 let mut has_optionals = false;
210 let mut fn_names = vec![];
211
212 let mut iter = group_stream.expect("should be group").into_iter();
213 while let Some(token) = iter.next() {
214 match token {
215 TokenTree::Group(g) => println!("group {g}"),
216 TokenTree::Ident(i) => {
217 let str = i.to_string();
218 if str == "fn" {
219 let Some(TokenTree::Ident(name)) = iter.next() else {
220 panic!("expect fn name");
221 };
222 fn_name = name.to_string();
223 let args = loop {
224 let Some(tt) = iter.next() else {
225 panic!("need more tokens");
226 };
227 match tt {
228 TokenTree::Group(args) => break args,
229 _ => generics.push(tt),
230 }
231 };
232 fn_args_str = args.to_string();
233 for tt in iter.by_ref() {
234 match tt {
235 TokenTree::Punct(ref p) if p.as_char() == ';' => {
236 result.push(tt);
237 break;
238 }
239 TokenTree::Group(ref g) if g.delimiter() == Delimiter::Brace => {
240 fn_body = Cow::Owned(g.to_string());
241 break;
242 }
243 _ => result.push(tt),
244 }
245 }
246
247 let mut ext = "";
248
249 let register_sel = if sel.is_empty() {
250 Cow::Borrowed("None")
251 } else {
252 ext = "extern \"C\" ";
253 fn_args_str = fn_args_str.replacen("(& self", "(&self", 1);
254 fn_args_str =
255 fn_args_str.replacen("(&self", "(&self, _cmd: Option<&objc::Sel>", 1);
256
257 fn_args_str = fn_args_str.replacen("(& mut self", "(&mut self", 1);
258 fn_args_str = fn_args_str.replacen(
259 "(&mut self",
260 "(&mut self, _cmd: Option<&objc::Sel>",
261 1,
262 );
263 Cow::Owned(format!(
264 "unsafe {{ objc::sel_reg_name(c\"{sel}\".as_ptr()) }}"
265 ))
266 };
267
268 if is_optional && !sel.is_empty() && fn_body.is_empty() {
269 result.pop(); fn_body = Cow::Borrowed("{ unimplemented!() }");
271 }
272
273 if !is_optional && sel.is_empty() {
274 skip = true;
275 }
276
277 let ret = if result.is_empty() {
278 Cow::Borrowed("")
279 } else {
280 Cow::Owned(TokenStream::from_iter(result.clone().into_iter()).to_string())
281 };
282
283 let gen = if generics.is_empty() {
284 Cow::Borrowed("")
285 } else {
286 Cow::Owned(TokenStream::from_iter(generics.clone().into_iter()).to_string())
287 };
288
289 let gen_rar_version = ret.contains("arc :: R <") && !returns_retained(&sel);
290
291 let impl_fn = if skip {
292 format!(
293 "
294 {ext}fn {fn_name}{gen}{fn_args_str}{ret} {fn_body}
295
296 "
297 )
298 } else {
299 fn_names.push((fn_name.clone(), gen_rar_version));
300 if gen_rar_version {
301 let ret = ret.replacen("arc :: R <", "arc :: Rar <", 1);
302 format!(
303 "
304 {ext}fn impl_{fn_name}_ar{gen}{fn_args_str}{ret} {fn_body}
305
306 "
307 )
308 } else {
309 format!(
310 "
311 {ext}fn impl_{fn_name}{gen}{fn_args_str}{ret} {fn_body}
312
313 "
314 )
315 }
316 };
317
318 if !skip {
319 impl_trait_functions.push(impl_fn);
320 }
321
322 if !is_optional && !skip {
323 let impl_sel = format!(
324 "
325 fn sel_{fn_name}() -> &'static objc::Sel {{ {register_sel} }}
326 "
327 );
328 impl_trait_functions.push(impl_sel);
329 }
330
331 is_optional = false;
332 sel.clear();
333 fn_name.clear();
334 fn_body = Cow::Borrowed("");
335 fn_args_str.clear();
336 generics.clear();
337 skip = false;
338 result.clear();
340 }
341 }
342 TokenTree::Punct(p) => match p.as_char() {
343 '#' => {
344 let TokenTree::Group(g) = iter.next().unwrap() else {
345 panic!("not a group");
346 };
347 match Attr::from_stream(g.stream()) {
348 Some(Attr::Optional) => {
349 has_optionals = true;
350 is_optional = true;
351 }
352 Some(Attr::MsgSend(s)) => sel = s,
353 Some(_) => continue,
354 None => continue,
355 }
356 }
357 _ => panic!("other char '{p}'"),
358 },
359 TokenTree::Literal(l) => println!("lit {l}"),
360 }
361 }
362 let pre = TokenStream::from_iter(pre_tokens).to_string();
363 let obj_trait_name = format!("{trait_name}Impl");
364 let fns = impl_trait_functions.join("\n");
365
366 let add_methods = if has_optionals {
367 Cow::Borrowed("fn cls_add_methods<O: objc::Obj>(cls: &objc::Class<O>);")
368 } else {
369 Cow::Owned(add_methods_fn(&fn_names))
370 };
371
372 let add_protocol = format!(
373 "
374 fn cls_add_protocol<O: objc::Obj>(cls: &objc::Class<O>) {{
375 unsafe {{
376 let cls: &objc::Class<objc::Id> = std::mem::transmute(cls);
377 if let Some(proto) = objc::objc_getProtocol(c\"{protocol_name}\".as_ptr()) {{
378 cls.add_protocol(proto);
379 }}
380 }}
381 }}
382 "
383 );
384
385 let code = format!(
386 "
387
388{pre} {obj_trait_name}: {trait_name} {{
389 {fns}
390 {add_methods}
391 {add_protocol}
392}}
393 "
394 );
395
396 let ts: TokenStream = code.parse().unwrap();
397
398 original_trait.extend(ts);
399 original_trait
400}
401
402fn add_methods_fn(fns: &[(String, bool)]) -> String {
403 let mut res = "
404 fn cls_add_methods<O: objc::Obj>(cls: &objc::Class<O>) {
405 let cls: &objc::Class<objc::Id> = unsafe { std::mem::transmute(cls) };
406 "
407 .to_string();
408 for (f, ar) in fns {
409 let suffix = if *ar { "_ar" } else { "" };
410 let add = format!(
411 "
412 let sel = Self::sel_{f}();
413 unsafe {{
414 let imp: extern \"C\" fn() = std::mem::transmute(Self::impl_{f}{suffix} as *const u8);
415 objc::class_addMethod(cls, sel, imp, std::ptr::null());
416 }}
417 ",
418 );
419 res.push_str(&add);
420 }
421 res.push_str("\n}");
422 res
423}
424#[proc_macro_attribute]
425pub fn add_methods(_args: TokenStream, tr_impl: TokenStream) -> TokenStream {
426 let mut tokens = vec![];
427
428 let iter = tr_impl.into_iter();
429 let mut fns = vec![];
430
431 for tt in iter {
432 match tt {
433 TokenTree::Group(g) => {
434 let mut body = g.stream().into_iter();
435 while let Some(tt) = body.next() {
436 match tt {
437 TokenTree::Ident(i) if i.to_string().eq("fn") => {
438 let Some(TokenTree::Ident(f)) = body.next() else {
439 panic!("expected function name");
440 };
441 let f = f.to_string().replacen("impl_", "", 1);
442 if let Some(f) = f.strip_suffix("_ar") {
443 fns.push((f.to_string(), true));
444 } else {
445 fns.push((f, false));
446 }
447 }
448 _ => continue,
449 }
450 }
451 let imp: TokenStream = add_methods_fn(&fns).parse().unwrap();
452 let mut stream = g.stream();
453 stream.extend(imp);
454 let g = Group::new(g.delimiter(), stream);
455 tokens.push(TokenTree::Group(g));
456 }
457 _ => tokens.push(tt),
458 }
459 }
460
461 TokenStream::from_iter(tokens)
464}
465#[proc_macro_attribute]
466pub fn msg_send_debug(sel: TokenStream, func: TokenStream) -> TokenStream {
467 let x86_64 = false;
468 gen_msg_send(sel, func, x86_64, true)
469}
470
471#[proc_macro_attribute]
472pub fn msg_send(sel: TokenStream, func: TokenStream) -> TokenStream {
473 let x86_64 = false;
474 gen_msg_send(sel, func, x86_64, false)
475}
476
477#[proc_macro_attribute]
478pub fn msg_send_x86_64(sel: TokenStream, func: TokenStream) -> TokenStream {
479 let x86_64 = true;
480 gen_msg_send(sel, func, x86_64, false)
481}
482
483fn gen_msg_send(sel: TokenStream, func: TokenStream, x86_64: bool, debug: bool) -> TokenStream {
484 let sel = sel.to_string().replace([' ', '\n'], "");
485 let sel_args_count = sel.matches(':').count();
486
487 let mut iter = func.into_iter();
488 let mut meta: Vec<TokenTree> = Vec::new();
489 let mut unsafe_already = false;
490 let mut optional_already = false;
491 let mut versions = Versions::default();
492
493 while let Some(tt) = iter.next() {
494 match tt {
495 TokenTree::Group(ref g) => {
496 if g.delimiter() == Delimiter::Bracket {
497 match Attr::from_stream(g.stream()) {
498 Some(Attr::Optional) => optional_already = true,
499 Some(Attr::ApiAvailable(v)) => {
500 versions = v;
501 meta.pop();
502 continue;
503 }
504 Some(Attr::DocAvailable) => {
505 iter.next(); let Some(TokenTree::Group(g)) = iter.next() else {
507 panic!("Expect doc with versions");
508 };
509 let mut doc_iter = g.stream().into_iter();
510 doc_iter.next(); doc_iter.next(); let Some(TokenTree::Literal(s)) = doc_iter.next() else {
513 panic!("Expect doc with versions");
514 };
515 let str = s.to_string();
516 versions = Versions::from_doc_str(&str[1..str.len() - 1]);
517 meta.push(tt.clone());
518 meta.push(TokenTree::Punct(Punct::new(
519 '#',
520 proc_macro::Spacing::Joint,
521 )));
522 meta.push(TokenTree::Group(g));
523 continue;
524 }
525 Some(Attr::MsgSend(_)) => panic!("only one msg_send is allowed"),
526 None => {}
527 }
528 }
529 }
530 TokenTree::Ident(ref i) => {
531 let i = i.to_string();
532 match i.as_str() {
533 "fn" => break,
534 "unsafe" => unsafe_already = true,
535 _ => {}
536 }
537 }
538 TokenTree::Punct(_) => {}
539 TokenTree::Literal(_) => {}
540 }
541 meta.push(tt);
542 }
543
544 let Some(TokenTree::Ident(fn_name)) = iter.next() else {
545 panic!("expected function name");
546 };
547
548 let fn_name = fn_name.to_string();
549 let mut generics = Vec::new();
550 let doc_alias = if fn_name != sel {
551 format!("#[doc(alias = \"{sel}\")]")
552 } else {
553 String::new()
554 };
555
556 let args = loop {
557 let Some(tt) = iter.next() else {
558 panic!("need more tokens");
559 };
560 match tt {
561 TokenTree::Group(args) => break args,
562 _ => generics.push(tt),
563 }
564 };
565
566 let gen = TokenStream::from_iter(generics).to_string();
567
568 let mut ret = TokenStream::from_iter(iter).to_string();
569 assert_eq!(ret.pop().expect(";"), ';');
570 let ret_full = ret.to_string();
571 if let Some((a, _)) = ret.split_once("where") {
572 ret = a.to_string();
573 }
574 let option = ret_full.contains("-> Option <");
575 if debug {
576 println!("{option}: {ret_full}");
577 }
578 let gen_rar_version = ret.contains("arc :: R <") && !returns_retained(&sel);
579
580 if debug {
581 println!("option: {option}, gen_rar_version {gen_rar_version} ret: {ret}");
582 }
583
584 let msg_send_fn = if x86_64 {
585 choose_msg_send_fn(&ret_full)
586 } else {
587 ""
588 };
589
590 let fn_args = args.to_string();
591
592 let (class, vars) = fn_args_from_stream(args.stream());
593 let fn_args_count = vars.len();
594
595 assert_eq!(
596 sel_args_count, fn_args_count,
597 "selector and function args don't match {vars:?}"
598 );
599
600 let (mut fn_args, mut call_args) = if x86_64 {
601 let fn_args = fn_args.replacen('(', "(id:", 1).replacen(
602 "self",
603 "Self, imp: *const std::ffi::c_void",
604 1,
605 );
606 (
607 fn_args,
608 format!("sig(self, x86_64_sel, {})", vars.join(", ")),
609 )
610 } else if fn_args_count == 0 {
611 let fn_args = fn_args
612 .replacen("( &", "(id: &", 1)
613 .replacen("self", "Self", 1);
614 (fn_args, "sig(self)".to_string())
615 } else {
616 let fn_args = fn_args
617 .replacen('(', "(id:", 1)
618 .replace("self", "Self, imp: *const std::ffi::c_void");
619 (
620 fn_args,
621 format!("sig(self, std::ptr::null(), {})", vars.join(", ")),
622 )
623 };
624
625 if class {
626 if x86_64 {
627 fn_args = fn_args.replacen(
628 "(id:",
629 "(cls: *const std::ffi::c_void, imp: *const std::ffi::c_void, ",
630 1,
631 );
632 call_args = call_args.replacen("sig(self", "sig(Self::cls_ptr()", 1);
633 } else if fn_args_count == 0 {
634 fn_args = fn_args.replacen('(', "(cls: *const std::ffi::c_void", 1);
635 call_args = call_args.replacen("sig(self", "sig(Self::cls_ptr()", 1);
636 } else {
637 fn_args = fn_args.replacen(
638 "(id:",
639 "(cls: *const std::ffi::c_void, imp: *const std::ffi::c_void, ",
640 1,
641 );
642 call_args = call_args.replacen("sig(self", "sig(Self::cls_ptr()", 1);
643 }
644 }
645
646 let available = versions.available_cfg();
647 let unavailable = versions.unavailable_cfg();
648
649 let mut flow = String::new();
650 let pre = TokenStream::from_iter(meta).to_string();
651 let self_ = if class { "Self::" } else { "self." };
652 let vars = vars.join(", ");
653 let mut impl_fn_name = fn_name.clone();
654 let impl_ret_full = if gen_rar_version {
655 ret_full.replacen("arc :: R <", "arc :: Rar <", 1)
656 } else {
657 ret_full.clone()
658 };
659 let impl_ret = if gen_rar_version {
660 ret.replacen("arc :: R <", "arc :: Rar <", 1)
661 } else {
662 ret
663 };
664
665 if gen_rar_version {
666 impl_fn_name.push_str("_ar");
667 }
668 if x86_64 {
669 flow.push_str(&format!(
670 "
671 {available}
672 {doc_alias}
673 #[inline]
674 {pre} fn {impl_fn_name}{gen}{args}{impl_ret_full} {{
675 extern \"C\" {{
676 #[link_name = \"{msg_send_fn}\"]
677 fn msg_send();
678 }}
679 extern \"C-unwind\" {{
680 fn sel_registerName(name: *const i8) -> *const std::ffi::c_void;
681 }}
682
683 unsafe {{
684 let x86_64_sel = sel_registerName(c\"{sel}\".as_ptr());
685 let fn_ptr = msg_send as *const std::ffi::c_void;
686 let sig: extern \"C\" fn{fn_args} {impl_ret} = std::mem::transmute(fn_ptr);
687
688 {call_args}
689 }}
690 }}
691 "
692 ));
693 if versions.any() {
694 let unsafe_str = if unsafe_already { "" } else { "unsafe" };
695 let optional = if optional_already {
696 String::new()
697 } else {
698 format!(
699 "
700 /// `@selector({sel})` but dynamic
701 /// use this function to check if object responds to selector
702 #[inline]
703 pub fn sel_{fn_name}() -> &'static objc::Sel {{
704 unsafe {{ objc::sel_reg_name(c\"{sel}\".as_ptr()) }}
705 }}
706 "
707 )
708 };
709
710 flow.push_str(&format!(
711 "
712 {optional}
713
714 {unavailable}
715 {doc_alias}
716 #[inline]
717 {pre} {unsafe_str} fn {impl_fn_name}{gen}{args}{impl_ret_full} {{
718 extern \"C\" {{
719 #[link_name = \"{msg_send_fn}\"]
720 fn msg_send();
721
722 }}
723 extern \"C-unwind\" {{
724 fn sel_registerName(name: *const i8) -> *const std::ffi::c_void;
725 }}
726
727 unsafe {{
728 let x86_64_sel = sel_registerName(c\"{sel}\".as_ptr());
729 let fn_ptr = msg_send as *const std::ffi::c_void;
730 let sig: extern \"C\" fn{fn_args} {impl_ret} = std::mem::transmute(fn_ptr);
731
732 {call_args}
733 }}
734 }}
735 "
736 ));
737 }
738 } else {
739 flow.push_str(&format!(
740 "
741 {available}
742 {doc_alias}
743 #[inline]
744 {pre} fn {impl_fn_name}{gen}{args}{impl_ret_full} {{
745 extern \"C\" {{
746 #[link_name = \"objc_msgSend${sel}\"]
747 fn msg_send();
748 }}
749
750 unsafe {{
751 let fn_ptr = msg_send as *const std::ffi::c_void;
752 let sig: extern \"C\" fn{fn_args} {impl_ret} = std::mem::transmute(fn_ptr);
753
754 {call_args}
755 }}
756 }}
757 "
758 ));
759 if versions.any() {
760 let unsafe_str = if unsafe_already { "" } else { "unsafe" };
761 let optional = if optional_already {
762 String::new()
763 } else {
764 format!(
765 "
766 /// `@selector({sel})` but dynamic
767 /// use this function to check if object responds to selector
768 #[inline]
769 pub fn sel_{fn_name}() -> &'static objc::Sel {{
770 unsafe {{ objc::sel_reg_name(c\"{sel}\".as_ptr()) }}
771 }}
772 "
773 )
774 };
775
776 flow.push_str(&format!(
777 "
778 {optional}
779
780 {unavailable}
781 {doc_alias}
782 #[inline]
783 {pre} {unsafe_str} fn {impl_fn_name}{gen}{args}{impl_ret_full} {{
784 extern \"C\" {{
785 #[link_name = \"objc_msgSend${sel}\"]
786 fn msg_send();
787 }}
788
789 let fn_ptr = msg_send as *const std::ffi::c_void;
790 let sig: extern \"C\" fn{fn_args} {impl_ret} = std::mem::transmute(fn_ptr);
791
792 {call_args}
793 }}
794 "
795 ));
796 }
797 };
798
799 if gen_rar_version {
800 if debug {
801 println!("get rar version");
802 }
803 if option {
804 flow.push_str(&format!(
805 "
806
807 {available}
808 {doc_alias}
809 #[inline]
810 {pre} fn {fn_name}{gen}{args}{ret_full} {{
811 arc::rar_retain_option({self_}{fn_name}_ar({vars}) )
812 }}
813 "
814 ));
815 if versions.any() {
816 let unsafe_str = if unsafe_already { "" } else { "unsafe" };
817 flow.push_str(&format!(
818 "
819
820 {unavailable}
821 {doc_alias}
822 #[inline]
823 /// Check availability with selector1 `Self::sel_{fn_name}()`
824 {pre} {unsafe_str} fn {fn_name}{gen}{args}{ret_full} {{
825 arc::rar_retain_option({self_}{fn_name}_ar({vars}) )
826 }}
827 ",
828 ));
829 }
830 } else {
831 flow.push_str(&format!(
833 "
834
835 {available}
836 {doc_alias}
837 #[inline]
838 {pre} fn {fn_name}{gen}{args}{ret_full} {{
839 arc::rar_retain({self_}{fn_name}_ar({vars}))
840 }}
841 ",
842 ));
843 if versions.any() {
844 let unsafe_str = if unsafe_already { "" } else { "unsafe" };
845 flow.push_str(&format!(
846 "
847
848 {unavailable}
849 {doc_alias}
850 /// Check availability with selector `Self::sel_{fn_name}()`
851 #[inline]
852 {pre} {unsafe_str} fn {fn_name}{gen}{args}{ret_full} {{
853 arc::rar_retain({self_}{fn_name}_ar({vars}))
854 }}
855 "
856 ));
857 }
858 }
859 }
860 if debug {
861 println!("{flow}");
862 }
863
864 flow.parse().unwrap()
865}
866
867fn fn_args_from_stream(stream: TokenStream) -> (bool, Vec<String>) {
868 if stream.is_empty() {
869 return (true, Vec::new());
870 }
871 let mut res = Vec::new();
872 let mut pos = 0;
873 let mut self_arg = false;
874 let mut skip_ident = false;
875 let mut lifetime = false;
876 let mut nesting = 0;
877 for s in stream.into_iter() {
878 match s {
879 TokenTree::Group(_) => {}
880 TokenTree::Ident(ref i) => {
881 if lifetime {
882 lifetime = false;
883 continue;
884 }
885 if !skip_ident {
886 let str = i.to_string();
887 if str == "mut" {
888 continue;
889 }
890 if pos == 0 && str == "self" {
891 self_arg = true;
892 continue;
893 }
894 res.push(str);
895 skip_ident = true;
896 }
897 }
898 TokenTree::Punct(p) => match p.as_char() {
901 '<' => nesting += 1,
902 '>' => nesting -= 1,
903 '\'' => lifetime = true,
904 ',' if nesting == 0 => {
906 pos += 1;
907 skip_ident = false;
908 }
909 ':' => skip_ident = true,
910 _ => {}
911 },
912 TokenTree::Literal(ref _l) => {}
913 }
914 }
915 (!self_arg, res)
916}
917
918#[proc_macro_attribute]
919pub fn api_weak(_ts: TokenStream, body: TokenStream) -> TokenStream {
920 let mut original_body = body.clone();
921 let mut iter = body.into_iter();
922 let mut versions = None;
923 let mut tokens: Vec<TokenTree> = Vec::new();
924 let mut vars: Vec<(Versions, String, String)> = Vec::new(); while let Some(t) = iter.next() {
926 match t {
927 TokenTree::Group(ref p) if p.delimiter() == Delimiter::Brace => {
929 let mut group = p.stream().into_iter();
930 while let Some(t) = group.next() {
931 match t {
932 TokenTree::Punct(ref p) if p.as_char() == ':' => {
933 if let Some(version) = versions.take() {
934 let var_name = tokens.last().unwrap().to_string();
935 let _t = group.next().unwrap(); let _t = group.next().unwrap(); if let TokenTree::Ident(ident) = group.next().unwrap() {
938 assert_eq!(ident.to_string(), "static");
939 let mut ty = Vec::new();
940 while let Some(t) = group.next() {
941 match t {
942 TokenTree::Punct(ref p) if p.as_char() == ';' => break,
943 t => ty.push(t),
944 }
945 }
946 let ty = if ty.len() == 1 {
947 ty[0].to_string()
948 } else {
949 TokenStream::from_iter(ty).to_string()
950 };
951 vars.push((version, var_name, ty));
952 }
953 }
954 }
955 TokenTree::Punct(ref p) if p.as_char() == ';' => {
956 tokens.clear();
957 versions = None;
958 }
959 TokenTree::Group(ref p) if p.delimiter() == Delimiter::Parenthesis => {
960 if let Some(version) = versions.take() {
961 let mut ty: Vec<TokenTree> =
962 TokenStream::from_str("extern \"C\" fn ")
963 .unwrap()
964 .into_iter()
965 .collect();
966 let name = tokens.pop().unwrap();
967 let var_name = name.to_string();
968 ty.push(t.clone());
970 while let Some(t) = group.next() {
971 match t {
972 TokenTree::Punct(ref p) if p.as_char() == ';' => break,
973 t => ty.push(t),
974 }
975 }
976 let ty = if ty.len() == 1 {
977 ty[0].to_string()
978 } else {
979 TokenStream::from_iter(ty).to_string()
980 };
981
982 vars.push((version, var_name, ty));
985 }
986 }
987 TokenTree::Group(ref p) if p.delimiter() == Delimiter::Bracket => {
988 let mut attr = p.stream().into_iter();
989 while let Some(ref ident) = attr.next() {
990 match ident {
991 TokenTree::Ident(i) => {
993 let st = i.to_string();
994 match st.as_str() {
995 "api" => {
997 attr.next();
998 attr.next();
999 continue;
1000 }
1001 "objc" => {
1003 attr.next();
1004 attr.next();
1005 continue;
1006 }
1007 "available" => {
1009 if let Some(TokenTree::Group(g)) = attr.next() {
1010 versions =
1011 Some(Versions::from_stream(g.stream()));
1012 } else {
1014 break;
1015 }
1016 break;
1017 }
1018 _ => break,
1019 }
1020 }
1021 _ => break, }
1024 }
1025
1026 }
1028 _ => {}
1029 }
1030 tokens.push(t);
1031 }
1033 }
1034 _x => {
1035 }
1037 }
1038 }
1039 let vars = vars
1041 .iter()
1042 .map(|(version, name, ty)| {
1043 let upper_name = upper_case(name);
1044 let availability = version.unavailable_cfg();
1045 format!(
1046 "{availability}\nstatic {upper_name}: api::DlSym<{ty}> = api::DlSym::new(c\"{name}\");"
1047 )
1048 })
1049 .collect::<Vec<_>>()
1050 .join("\n");
1051 let stream = TokenStream::from_str(&vars).unwrap();
1052 original_body.extend(stream);
1053 original_body
1054}
1055
1056#[derive(Default, Debug, Copy, Clone)]
1057struct Version(u32, u32);
1058
1059impl Version {
1060 fn from_str(str: &str) -> Option<Self> {
1061 if let Some((major, minor)) = str.split_once('.') {
1062 Some(Self(str::parse(major).unwrap(), str::parse(minor).unwrap()))
1063 } else if let Some((major, minor)) = str.split_once('_') {
1064 Some(Self(str::parse(major).unwrap(), str::parse(minor).unwrap()))
1065 } else {
1066 None
1067 }
1068 }
1069}
1070
1071#[derive(Default, Debug)]
1072struct Versions {
1073 macos: Option<Version>,
1074 ios: Option<Version>,
1075 tvos: Option<Version>,
1076 watchos: Option<Version>,
1077 visionos: Option<Version>,
1078 maccatalyst: Option<Version>,
1079}
1080
1081impl Versions {
1082 fn any(&self) -> bool {
1083 self.macos.is_some()
1084 || self.ios.is_some()
1085 || self.tvos.is_some()
1086 || self.watchos.is_some()
1087 || self.visionos.is_some()
1088 || self.maccatalyst.is_some()
1089 }
1090
1091 fn available_cfg_ts(&self) -> TokenStream {
1092 TokenStream::from_str(&self.available_cfg()).unwrap()
1093 }
1094
1095 fn available_cfg(&self) -> String {
1096 let mut vec = Vec::with_capacity(6);
1097 if let Some(v) = self.macos {
1098 vec.push(format!(
1099 "all(target_os=\"macos\", feature=\"macos_{}_{}\")",
1100 v.0, v.1
1101 ));
1102 }
1103 if let Some(v) = self.ios {
1104 vec.push(format!(
1105 "all(target_os=\"ios\", feature=\"ios_{}_{}\")",
1106 v.0, v.1
1107 ));
1108 }
1109 if let Some(v) = self.tvos {
1110 vec.push(format!(
1111 "all(target_os=\"tvos\", feature=\"tvos_{}_{}\")",
1112 v.0, v.1,
1113 ));
1114 }
1115 if let Some(v) = self.watchos {
1116 vec.push(format!(
1117 "all(target_os=\"watchos\", feature=\"watchos_{}_{}\")",
1118 v.0, v.1,
1119 ));
1120 }
1121 if let Some(v) = self.visionos {
1122 vec.push(format!(
1123 "all(target_os=\"visionos\", feature=\"visionos_{}_{}\")",
1124 v.0, v.1
1125 ));
1126 }
1127 if let Some(v) = self.maccatalyst {
1128 vec.push(format!(
1129 "all(target_os=\"ios\", target_abi=\"macabi\", feature=\"maccatalyst_{}_{}\")",
1130 v.0, v.1
1131 ));
1132 }
1133
1134 match vec.len() {
1135 0 => String::new(),
1136 1 => format!("#[cfg({})]\n", vec[0]),
1137 _ => format!("#[cfg(any({}))]\n", vec.join(", ")),
1138 }
1139 }
1140 fn unavailable_cfg_ts(&self) -> TokenStream {
1141 TokenStream::from_str(&self.unavailable_cfg()).unwrap()
1142 }
1143
1144 fn unavailable_cfg(&self) -> String {
1145 let mut vec = Vec::with_capacity(6);
1146 if let Some(v) = self.macos {
1147 vec.push(format!(
1148 "all(target_os=\"macos\", not(feature=\"macos_{}_{}\"))",
1149 v.0, v.1
1150 ));
1151 }
1152 if let Some(v) = self.ios {
1153 vec.push(format!(
1154 "all(target_os=\"ios\", not(feature=\"ios_{}_{}\"))",
1155 v.0, v.1
1156 ));
1157 }
1158 if let Some(v) = self.tvos {
1159 vec.push(format!(
1160 "all(target_os=\"tvos\", not(feature=\"tvos_{}_{}\"))",
1161 v.0, v.1
1162 ));
1163 }
1164 if let Some(v) = self.watchos {
1165 vec.push(format!(
1166 "all(target_os=\"watchos\", not(feature=\"watchos_{}_{}\"))",
1167 v.0, v.1
1168 ));
1169 }
1170 if let Some(v) = self.visionos {
1171 vec.push(format!(
1172 "all(target_os=\"visionos\", not(feature=\"visionos_{}_{}\"))",
1173 v.0, v.1
1174 ));
1175 }
1176 if let Some(v) = self.maccatalyst {
1177 vec.push(format!(
1178 "all(target_os=\"ios\", target_abi=\"macabi\", not(feature=\"maccatalyst_{}_{}\"))",
1179 v.0, v.1
1180 ));
1181 }
1182
1183 match vec.len() {
1184 0 => String::new(),
1185 1 => format!("#[cfg({})]\n", vec[0]),
1186 _ => format!("#[cfg(any({}))]\n", vec.join(", ")),
1187 }
1188 }
1189 fn available_doc_ts(&self) -> TokenStream {
1190 TokenStream::from_str(&self.available_doc()).unwrap()
1191 }
1192
1193 fn available_doc(&self) -> String {
1194 let mut vec = Vec::with_capacity(6);
1195 if let Some(v) = self.macos {
1196 vec.push(format!("macos_{}_{}", v.0, v.1));
1197 }
1198 if let Some(v) = self.ios {
1199 vec.push(format!("ios_{}_{}", v.0, v.1));
1200 }
1201 if let Some(v) = self.tvos {
1202 vec.push(format!("tvos_{}_{}", v.0, v.1,));
1203 }
1204 if let Some(v) = self.watchos {
1205 vec.push(format!("watchos_{}_{}", v.0, v.1));
1206 }
1207 if let Some(v) = self.visionos {
1208 vec.push(format!("visionos_{}_{}", v.0, v.1));
1209 }
1210 if let Some(v) = self.maccatalyst {
1211 vec.push(format!("maccatalyst_{}_{}", v.0, v.1));
1212 }
1213
1214 match vec.len() {
1215 0 => String::new(),
1216 1 => format!("/// # Availability\n/// {0}", vec[0]),
1217 _ => format!("/// # Availability\n/// {0}", vec.join(", ")),
1218 }
1219 }
1220 fn unavailable_doc_ts(&self) -> TokenStream {
1221 TokenStream::from_str(&self.unavailable_doc()).unwrap()
1222 }
1223
1224 fn unavailable_doc(&self) -> String {
1225 let mut vec = Vec::with_capacity(6);
1226 if let Some(v) = self.macos {
1227 vec.push(format!("macos_{}_{}", v.0, v.1));
1228 }
1229 if let Some(v) = self.ios {
1230 vec.push(format!("ios_{}_{}", v.0, v.1));
1231 }
1232 if let Some(v) = self.tvos {
1233 vec.push(format!("tvos_{}_{}", v.0, v.1,));
1234 }
1235 if let Some(v) = self.watchos {
1236 vec.push(format!("watchos_{}_{}", v.0, v.1));
1237 }
1238 if let Some(v) = self.visionos {
1239 vec.push(format!("visionos_{}_{}", v.0, v.1));
1240 }
1241 if let Some(v) = self.maccatalyst {
1242 vec.push(format!("maccatalyst_{}_{}", v.0, v.1));
1243 }
1244
1245 match vec.len() {
1246 0 => String::new(),
1247 1 => format!("/// # Availability\n/// Not {0}", vec[0]),
1248 _ => format!("/// # Availability\n/// Not {0}", vec.join(", ")),
1249 }
1250 }
1251
1252 fn from_stream(versions: TokenStream) -> Self {
1253 let mut iter = versions.into_iter();
1254 let mut versions = Self::default();
1255 while let Some(t) = iter.next() {
1256 let target_os = match t {
1257 TokenTree::Ident(ident) => ident.to_string(),
1258 _ => panic!("Unexpected token {t:?}"),
1259 };
1260 let Some(TokenTree::Punct(ident)) = iter.next() else {
1261 panic!("Expecting = ");
1262 };
1263
1264 assert_eq!(ident, '=', "expecting =");
1265
1266 let Some(TokenTree::Literal(val)) = iter.next() else {
1267 panic!("expecting version");
1268 };
1269
1270 let v = Version::from_str(&val.to_string());
1271 match target_os.as_str() {
1272 "macos" => versions.macos = v,
1273 "ios" => versions.ios = v,
1274 "tvos" => versions.tvos = v,
1275 "watchos" => versions.watchos = v,
1276 "visionos" => versions.visionos = v,
1277 "maccatalyst" => versions.maccatalyst = v,
1278 t => panic!("Unsupported platform. Platform should be macos, ios, watchos, visionos or maccatalyst. Found {t:?}"),
1279 };
1280
1281 if let Some(TokenTree::Punct(p)) = iter.next() {
1282 assert_eq!(p, ',', "expect ,");
1283 };
1284 }
1285
1286 versions
1287 }
1288
1289 fn from_doc_str(str: &str) -> Self {
1290 let mut res = Self::default();
1291 for str in str.split_whitespace() {
1292 for str in str.split_terminator(',') {
1293 if str.starts_with("macos_") {
1294 res.macos = Version::from_str(&str[6..]);
1295 } else if str.starts_with("ios_") {
1296 res.ios = Version::from_str(&str[4..]);
1297 } else if str.starts_with("tvos_") {
1298 res.tvos = Version::from_str(&str[5..]);
1299 } else if str.starts_with("watchos_") {
1300 res.watchos = Version::from_str(&str[8..]);
1301 } else if str.starts_with("visionos_") {
1302 res.visionos = Version::from_str(&str[8..]);
1303 } else if str.starts_with("maccatalyst_") {
1304 res.maccatalyst = Version::from_str(&str[12..]);
1305 }
1306 }
1307 }
1308 res
1309 }
1310}
1311
1312#[proc_macro_attribute]
1313pub fn api_available(versions: TokenStream, body: TokenStream) -> TokenStream {
1314 let versions = Versions::from_stream(versions);
1315 let available = versions.available_cfg_ts();
1316 let available_doc = versions.available_doc_ts();
1317 let unavailable = versions.unavailable_cfg_ts();
1318 let unavailable_doc = versions.unavailable_doc_ts();
1319 if available.is_empty() {
1320 return body;
1321 }
1322
1323 let mut no_args = false;
1324 let mut no_body = false;
1325
1326 let mut available = Some(available);
1327 let mut available_doc = Some(available_doc);
1328 let mut unavailable = Some(unavailable);
1329 let mut unavailable_doc = Some(unavailable_doc);
1330 let mut res = Vec::new();
1331 let mut maybe_res: Vec<TokenTree> = Vec::new();
1332 let mut fn_index = 0usize;
1333 let mut unsafe_already = false;
1334
1335 let mut body_iter = body.into_iter();
1336
1337 while let Some(t) = body_iter.next() {
1338 if available.is_some() {
1339 res.extend(available.take().unwrap());
1340 }
1341 if unavailable.is_some() {
1342 maybe_res.extend(unavailable.take().unwrap());
1343 }
1344 if available_doc.is_some() {
1345 match t {
1346 TokenTree::Ident(ref _i) => {
1347 let doc = available_doc.take().unwrap();
1348 res.extend(doc);
1349 }
1350 _ => {}
1351 }
1352 }
1353 if unavailable_doc.is_some() {
1354 match t {
1355 TokenTree::Ident(ref _i) => {
1356 let doc = unavailable_doc.take().unwrap();
1357 maybe_res.extend(doc);
1358 }
1359 _ => {}
1360 }
1361 }
1362 match t {
1363 TokenTree::Punct(ref p) if p.as_char() == ';' => {
1364 no_body = true;
1365 }
1366 TokenTree::Ident(ref ident) => match ident.to_string().as_str() {
1367 "fn" => {
1368 fn_index = maybe_res.len();
1369 }
1370 "unsafe" => {
1371 unsafe_already = true;
1372 }
1373 "define_cls" => {
1374 let token = TokenTree::Ident(Ident::new("define_weak_cls", t.span()));
1375 maybe_res.push(token);
1376 res.push(t.clone());
1377 while let Some(t) = body_iter.next() {
1378 maybe_res.push(t.clone());
1379 res.push(t);
1380 }
1381 break;
1382 }
1383 "define_cls_init" => {
1384 let token = TokenTree::Ident(Ident::new("define_weak_cls_init", t.span()));
1385 maybe_res.push(token);
1386 res.push(t.clone());
1387 while let Some(t) = body_iter.next() {
1388 maybe_res.push(t.clone());
1389 res.push(t);
1390 }
1391 break;
1392 }
1393 _ => {}
1394 },
1395 _ => {}
1396 }
1397
1398 maybe_res.push(t.clone());
1399
1400 if let TokenTree::Group(ref g) = t {
1401 if g.delimiter() == Delimiter::Parenthesis {
1403 no_args = g.stream().is_empty();
1404 }
1406 if g.delimiter() == Delimiter::Brace {
1408 if no_args && try_replace_return(&mut maybe_res) {}
1409 let mut make_result_optional = false;
1410 if try_replace_fn(&mut maybe_res, &mut make_result_optional) {
1411 if make_result_optional {
1412 let mut i = maybe_res.len() - 2;
1413
1414 while i > 0 {
1415 if let TokenTree::Punct(ref p) = maybe_res[i] {
1416 if p.as_char() == '-' && p.spacing() == Spacing::Joint {
1417 i += 2;
1418 break;
1419 }
1420 }
1421 i -= 1;
1422 }
1423
1424 maybe_res.insert(i, TokenTree::Punct(Punct::new('<', Spacing::Alone)));
1425 maybe_res
1426 .insert(i, TokenTree::Ident(Ident::new("Option", Span::call_site())));
1427 maybe_res.insert(
1428 maybe_res.len() - 2,
1429 TokenTree::Punct(Punct::new('>', Spacing::Alone)),
1430 );
1431 }
1432 } else {
1433 if !unsafe_already {
1434 maybe_res.insert(
1435 fn_index,
1436 TokenTree::Ident(Ident::new("unsafe", Span::call_site())),
1437 );
1438 }
1439 }
1440 }
1441 }
1442
1443 res.push(t);
1444 }
1445 if !no_body {
1446 res.extend(maybe_res);
1447 }
1448
1449 TokenStream::from_iter(res)
1450}
1451
1452fn try_replace_fn(tokens: &mut Vec<TokenTree>, make_result_optional: &mut bool) -> bool {
1453 let Some(TokenTree::Group(ref g)) = tokens.last() else {
1454 return false;
1455 };
1456 if g.delimiter() != Delimiter::Brace {
1457 return false;
1458 }
1459 let mut body_stream = g.stream().into_iter();
1461
1462 match body_stream.next().unwrap() {
1463 TokenTree::Group(scope) => {
1464 if scope.delimiter() == Delimiter::Brace {
1465 *make_result_optional = true;
1466 return true;
1467 }
1468 }
1469 TokenTree::Ident(ident) => match ident.to_string().as_str() {
1470 "unsafe" => {
1471 let Some(TokenTree::Group(g)) = body_stream.next() else {
1472 return false;
1473 };
1474 if g.delimiter() != Delimiter::Brace {
1475 return false;
1476 }
1477 let mut block = g.stream().into_iter();
1478 let Some(TokenTree::Ident(ident)) = block.next() else {
1479 return false;
1480 };
1481 let Some(TokenTree::Group(args)) = block.next() else {
1482 return false;
1483 };
1484 if args.delimiter() != Delimiter::Parenthesis {
1485 return false;
1486 }
1487 if block.next().is_none() {
1488 tokens.pop();
1489 let var = upper_case(&ident.to_string());
1490 let stream = TokenStream::from_str(&format!(
1491 "{{ unsafe {{ {var}.get_fn().unwrap(){} }} }}",
1492 args.to_string()
1493 ))
1494 .unwrap();
1495 tokens.extend(stream);
1496
1497 return true;
1498 }
1499 }
1500 "Self" => {
1501 let mut new_body = Vec::new();
1504 new_body.push(TokenTree::Ident(ident));
1505 let Some(TokenTree::Punct(p)) = body_stream.next() else {
1506 return false;
1507 };
1508
1509 if p.as_char() != ':' {
1510 return false;
1511 }
1512 new_body.push(TokenTree::Punct(p));
1513 let Some(TokenTree::Punct(p)) = body_stream.next() else {
1514 return false;
1515 };
1516
1517 if p.as_char() != ':' {
1518 return false;
1519 }
1520 new_body.push(TokenTree::Punct(p));
1521 let Some(TokenTree::Ident(ident)) = body_stream.next() else {
1522 return false;
1523 };
1524
1525 if ident.to_string() != "alloc" {
1526 return false;
1527 };
1528 new_body.push(TokenTree::Ident(ident));
1529
1530 let Some(TokenTree::Group(g)) = body_stream.next() else {
1531 return false;
1532 };
1533
1534 if g.delimiter() != Delimiter::Parenthesis {
1535 return false;
1536 }
1537 new_body.push(TokenTree::Group(g));
1538
1539 new_body.push(TokenTree::Punct(Punct::new('?', Spacing::Alone)));
1541 while let Some(t) = body_stream.next() {
1542 new_body.push(t)
1543 }
1544 let s = TokenStream::from_iter(new_body.drain(..));
1545 new_body.push(TokenTree::Ident(Ident::new("Some", Span::call_site())));
1546 let g = TokenTree::Group(Group::new(Delimiter::Parenthesis, s));
1547 new_body.push(g);
1548 let s = TokenStream::from_iter(new_body.drain(..));
1549 let g = TokenTree::Group(Group::new(Delimiter::Brace, s));
1550
1551 tokens.pop();
1552 tokens.push(g);
1553
1554 *make_result_optional = true;
1555 return true;
1556 }
1558 _ => return false,
1559 },
1560
1561 _ => return false,
1562 };
1563 false
1564}
1565
1566fn try_replace_return(tokens: &mut Vec<TokenTree>) -> bool {
1567 let mut idx = tokens.len() - 1;
1568 let mut has_static = false;
1569 while idx > 0 {
1571 match &tokens[idx] {
1572 TokenTree::Group(g) if g.delimiter() == Delimiter::Parenthesis => return false,
1573 TokenTree::Ident(i) if i.to_string() == "static" => has_static = true,
1574 TokenTree::Punct(p) if p.as_char() == '\'' => break,
1575 _ => {}
1577 }
1578 idx -= 1;
1579 }
1580
1581 if !has_static {
1582 return false;
1583 }
1584
1585 let mut body_stream = {
1586 let Some(TokenTree::Group(ref g)) = tokens.last() else {
1587 return false;
1588 };
1589
1590 g.stream().into_iter()
1592 };
1593
1594 let var = match body_stream.next().unwrap() {
1600 TokenTree::Ident(ident) => match ident.to_string().as_str() {
1601 "unsafe" => {
1602 let TokenTree::Group(g) = body_stream.next().unwrap() else {
1603 return false;
1604 };
1605 if g.delimiter() != Delimiter::Brace {
1606 return false;
1607 }
1608 let mut block = g.stream().into_iter();
1609 let TokenTree::Ident(ident) = block.next().unwrap() else {
1610 return false;
1611 };
1612 if let Some(TokenTree::Group(_g)) = block.next() {
1613 return false;
1614 };
1615 ident.to_string()
1616 }
1617 x => x.to_string(),
1618 },
1619
1620 _ => return false,
1621 };
1622
1623 idx -= 1; tokens.pop(); let stream = TokenStream::from_iter(tokens.drain(idx..));
1626 let ty = stream.to_string();
1627 let stream = TokenStream::from_str(&format!("Option<{ty}>")).unwrap();
1628 tokens.extend(stream);
1629 let var = upper_case(&var.to_string());
1630 let stream = TokenStream::from_str(&format!("{{ unsafe {{ {var}.get_var() }} }}")).unwrap();
1631 tokens.extend(stream);
1632 true
1633}
1634
1635fn upper_case(str: &str) -> String {
1637 let len = str.len();
1638 let mut res = Vec::<u8>::with_capacity(len + 10);
1639 let bytes = str.as_bytes();
1640 let mut was_lowercase = false;
1641 for ch in bytes {
1642 let is_upper = ch.is_ascii_uppercase();
1643 if was_lowercase && is_upper {
1644 res.push(b'_');
1645 }
1646 res.push(ch.to_ascii_uppercase());
1647 was_lowercase = !is_upper;
1648 }
1649
1650 String::from_utf8(res).unwrap()
1651}
1652
1653fn in_method_family(sel: &str, family: &str) -> bool {
1660 match sel.trim_start_matches('_').strip_prefix(family) {
1661 Some(rest) => !rest.starts_with(|c: char| c.is_ascii_lowercase()),
1662 None => false,
1663 }
1664}
1665
1666fn returns_retained(sel: &str) -> bool {
1679 const RETAINING_FAMILIES: [&str; 4] = ["init", "new", "mutableCopy", "copy"];
1680 RETAINING_FAMILIES
1682 .iter()
1683 .any(|family| in_method_family(sel, family))
1684}
1685
1686fn choose_msg_send_fn(ret: &str) -> &'static str {
1687 const MSG_SEND: &'static str = "objc_msgSend";
1688 const MSG_SEND_FPRET: &'static str = "objc_msgSend_fpret";
1689 const MSG_SEND_STRET: &'static str = "objc_msgSend_stret";
1690
1691 if ret.is_empty()
1692 || ret.starts_with("-> arc :: ")
1693 || ret.starts_with("-> Option <")
1694 || ret.starts_with("-> * const ")
1695 || ret.starts_with("-> * mut ")
1696 || ret.starts_with("-> & ")
1697 {
1698 return MSG_SEND;
1699 }
1700
1701 match ret {
1702 "-> bool"
1703 | "-> i8"
1704 | "-> u8"
1705 | "-> i16"
1706 | "-> u16"
1707 | "-> f32"
1708 | "-> u32"
1709 | "-> i32"
1710 | "-> u64"
1711 | "-> i64"
1712 | "-> isize"
1713 | "-> usize"
1714 | "-> ns :: Integer"
1715 | "-> ns :: UInteger"
1716 | "-> std :: ffi :: c_int"
1717 | "-> mps :: DType"
1718 | "-> crate :: mtl :: ResId"
1719 | "-> mach :: Port"
1720 | "-> os :: Type"
1721 | "-> vn :: Confidence"
1722 | "-> vn :: AspectRatio"
1723 | "-> vn :: Degrees"
1724 | "-> sys :: Pid" => MSG_SEND,
1725
1726 "-> ns :: Rect"
1728 | "-> cg :: Rect"
1729 | "-> cm :: Time"
1730 | "-> cm :: TimeRange"
1731 | "-> cg :: AffineTransform"
1732 | "-> cg :: Size"
1733 | "-> cg :: Point"
1734 | "-> ns :: Size"
1735 | "-> ns :: Point"
1736 | "-> cm :: RotationRate"
1737 | "-> cm :: Acceleration"
1738 | "-> ca :: Transform3d"
1739 | "-> mtl :: Size" => MSG_SEND_STRET,
1740
1741 "-> f64"
1743 | "-> cg :: Float"
1744 | "-> ns :: Float"
1745 | "-> ns :: Range"
1746 | "-> cf :: TimeInterval"
1747 | "-> ns :: TimeInterval" => MSG_SEND_FPRET,
1748 _x => {
1749 MSG_SEND
1752 }
1753 }
1754}
1755
1756