1#![doc = include_str!("../readme.md")]
2
3mod cli;
4mod config;
5mod derive;
6mod derive_writer;
7mod filter;
8mod format;
9mod guid;
10mod implements;
11mod io;
12mod package_writer;
13mod param;
14mod paths;
15mod references;
16mod signature;
17mod tables;
18mod tokens;
19mod type_map;
20mod type_name;
21mod type_tree;
22mod types;
23mod value;
24mod winmd;
25
26pub use cli::bindgen;
27use config::*;
28use derive::*;
29use derive_writer::*;
30use filter::*;
31use guid::*;
32use implements::*;
33use io::*;
34use package_writer::*;
35use param::*;
36use references::*;
37use signature::*;
38use std::cmp::Ordering;
39use std::collections::*;
40use std::fmt::Write;
41use std::path::{Path, PathBuf};
42use tables::*;
43use tokens::*;
44use type_map::*;
45use type_name::*;
46use type_tree::*;
47use types::*;
48use value::*;
49use winmd::*;
50mod filter_parser;
51mod method_names;
52mod type_closure;
53use method_names::*;
54use type_closure::*;
55
56fn report_timing(output: &Path, phase: &str, elapsed: std::time::Duration) {
57 if std::env::var_os("WINDOWS_BINDGEN_TIMINGS").is_some() {
58 eprintln!(
59 "windows-bindgen timing `{}` {phase}: {:.3} ms",
60 output.display(),
61 elapsed.as_secs_f64() * 1_000.0
62 );
63 }
64}
65
66pub fn builder() -> Bindgen {
68 Bindgen::new()
69}
70
71#[derive(Default)]
84pub struct Bindgen {
85 input: Vec<Input>,
86 input_default: bool,
87 filter: Vec<String>,
88 output: PathBuf,
89 derive: Vec<String>,
90 implement: Option<Vec<String>>,
91 compose: Vec<String>,
92 rustfmt: Option<String>,
93 layout: Layout,
94 style: Style,
95 dead_code: bool,
96}
97
98enum Input {
99 Path(PathBuf),
100 Bytes(Vec<u8>),
101}
102
103#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
105enum Layout {
106 #[default]
108 Modules,
109 Flat,
111 Package,
113}
114
115impl Layout {
116 fn is_flat(self) -> bool {
117 matches!(self, Self::Flat)
118 }
119 fn is_package(self) -> bool {
120 matches!(self, Self::Package)
121 }
122}
123
124#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
126enum Style {
127 #[default]
129 Default,
130 Sys {
132 extern_fns: bool,
134 },
135 Minimal,
138}
139
140impl Style {
141 fn is_sys(self) -> bool {
142 matches!(self, Self::Sys { .. })
143 }
144 fn is_minimal(self) -> bool {
145 matches!(self, Self::Minimal)
146 }
147 fn sys_fn_extern(self) -> bool {
148 matches!(self, Self::Sys { extern_fns: true })
149 }
150
151 fn emit_class_methods(self) -> bool {
153 !self.is_minimal()
154 }
155
156 fn emit_inherited_forwarders(self) -> bool {
158 !self.is_minimal()
159 }
160
161 fn emit_iterable_into_iterator(self) -> bool {
163 !self.is_minimal()
164 }
165
166 fn minimal_string_input(self, param: &Param) -> bool {
168 self.is_minimal() && param.is_input_only() && matches!(param.ty, Type::String)
169 }
170
171 fn minimal_string_return(self, ty: &Type) -> bool {
173 self.is_minimal() && matches!(ty, Type::String)
174 }
175
176 fn derive_std_traits(self) -> bool {
178 !self.is_sys()
179 }
180
181 fn emit_core_traits(self) -> bool {
183 !self.is_sys()
184 }
185
186 fn emit_bare_typedef(self) -> bool {
188 self.is_sys() || self.is_minimal()
189 }
190}
191
192impl Bindgen {
193 pub fn new() -> Self {
195 Self::default()
196 }
197
198 pub fn input(&mut self, input: impl AsRef<Path>) -> &mut Self {
200 self.input.push(Input::Path(input.as_ref().to_path_buf()));
201 self
202 }
203
204 pub fn input_default(&mut self) -> &mut Self {
206 self.input_default = true;
207 self
208 }
209
210 pub fn input_bytes(&mut self, input: &[u8]) -> &mut Self {
212 self.input.push(Input::Bytes(input.to_vec()));
213 self
214 }
215
216 pub fn input_byte_sets<I, B>(&mut self, inputs: I) -> &mut Self
218 where
219 I: IntoIterator<Item = B>,
220 B: AsRef<[u8]>,
221 {
222 for input in inputs {
223 self.input_bytes(input.as_ref());
224 }
225 self
226 }
227
228 pub fn inputs<I, S>(&mut self, inputs: I) -> &mut Self
230 where
231 I: IntoIterator<Item = S>,
232 S: AsRef<Path>,
233 {
234 for input in inputs {
235 self.input(input);
236 }
237 self
238 }
239
240 pub fn output(&mut self, output: impl AsRef<Path>) -> &mut Self {
242 self.output = output.as_ref().to_path_buf();
243 self
244 }
245
246 pub fn filter(&mut self, filter: &str) -> &mut Self {
253 self.filters(std::iter::once(filter))
254 }
255
256 #[track_caller]
258 pub fn filter_file(&mut self, input: impl AsRef<Path>) -> &mut Self {
259 self.filters(cli::read_tokens(input))
260 }
261
262 #[track_caller]
264 pub fn filter_files<I, S>(&mut self, inputs: I) -> &mut Self
265 where
266 I: IntoIterator<Item = S>,
267 S: AsRef<Path>,
268 {
269 for input in inputs {
270 self.filter_file(input);
271 }
272 self
273 }
274
275 pub fn filters<I, S>(&mut self, filters: I) -> &mut Self
282 where
283 I: IntoIterator<Item = S>,
284 S: AsRef<str>,
285 {
286 for filter in filters {
287 self.filter.push(filter.as_ref().to_string());
288 }
289 self
290 }
291
292 pub fn derive(&mut self, derive: &str) -> &mut Self {
294 self.derives(std::iter::once(derive))
295 }
296
297 pub fn derives<I, S>(&mut self, derives: I) -> &mut Self
299 where
300 I: IntoIterator<Item = S>,
301 S: AsRef<str>,
302 {
303 for derive in derives {
304 self.derive.push(derive.as_ref().to_string());
305 }
306 self
307 }
308
309 pub fn rustfmt(&mut self, rustfmt: &str) -> &mut Self {
311 self.rustfmt = Some(rustfmt.to_string());
312 self
313 }
314
315 #[track_caller]
317 pub fn flat(&mut self) -> &mut Self {
318 if matches!(self.layout, Layout::Package) {
319 panic!("cannot combine `--package` and `--flat`");
320 }
321 self.layout = Layout::Flat;
322 self
323 }
324
325 fn uses_inline_core_types(&self) -> bool {
326 self.style.is_sys() && !self.layout.is_package()
327 }
328
329 #[track_caller]
331 pub fn package(&mut self) -> &mut Self {
332 if matches!(self.layout, Layout::Flat) {
333 panic!("cannot combine `--package` and `--flat`");
334 }
335 self.layout = Layout::Package;
336 self
337 }
338
339 #[track_caller]
341 pub fn implement_all(&mut self) -> &mut Self {
342 match &self.implement {
343 None => self.implement = Some(vec![]),
344 Some(names) if names.is_empty() => {}
345 Some(_) => panic!("cannot combine `implement_all` with selected implementations"),
346 }
347 self
348 }
349
350 #[track_caller]
355 pub fn implement(&mut self, name: &str) -> &mut Self {
356 assert!(
357 !self.implement.as_ref().is_some_and(Vec::is_empty),
358 "cannot combine selected implementations with `implement_all`"
359 );
360 self.implement
361 .get_or_insert_with(Vec::new)
362 .push(name.to_string());
363 self
364 }
365
366 pub fn implements<I, S>(&mut self, names: I) -> &mut Self
368 where
369 I: IntoIterator<Item = S>,
370 S: AsRef<str>,
371 {
372 for name in names {
373 self.implement(name.as_ref());
374 }
375 self
376 }
377
378 #[track_caller]
382 pub fn compose(&mut self, name: &str) -> &mut Self {
383 assert!(
384 name.rsplit_once('.')
385 .is_some_and(|(namespace, name)| !namespace.is_empty() && !name.is_empty()),
386 "`compose` requires a fully qualified class name"
387 );
388 self.compose.push(name.to_string());
389 self
390 }
391
392 pub fn composes<I, S>(&mut self, names: I) -> &mut Self
394 where
395 I: IntoIterator<Item = S>,
396 S: AsRef<str>,
397 {
398 for name in names {
399 self.compose(name.as_ref());
400 }
401 self
402 }
403
404 #[track_caller]
409 pub fn sys(&mut self) -> &mut Self {
410 let extern_fns = matches!(self.style, Style::Sys { extern_fns: true });
411 if matches!(self.style, Style::Minimal) {
412 panic!("cannot combine `--sys` and `--minimal`");
413 }
414 self.style = Style::Sys { extern_fns };
415 self
416 }
417
418 #[track_caller]
425 pub fn minimal(&mut self) -> &mut Self {
426 if matches!(self.style, Style::Sys { .. }) {
427 panic!("cannot combine `--sys` and `--minimal`");
428 }
429 self.style = Style::Minimal;
430 self
431 }
432
433 #[track_caller]
437 pub fn extern_fns(&mut self) -> &mut Self {
438 match &mut self.style {
439 Style::Sys { extern_fns } => *extern_fns = true,
440 _ => panic!("`--extern` requires `--sys`"),
441 }
442 self
443 }
444
445 pub fn dead_code(&mut self) -> &mut Self {
448 self.dead_code = true;
449 self
450 }
451
452 #[track_caller]
454 pub fn write(&self) {
455 let total = std::time::Instant::now();
456
457 assert!(
459 !self.output.as_os_str().is_empty(),
460 "output is required (call `.output()` or pass `--out`)"
461 );
462 assert!(
463 self.compose.is_empty() || self.style.is_minimal(),
464 "`compose` requires `minimal`"
465 );
466
467 let mut include: Vec<&str> = vec![];
468 let mut exclude: Vec<&str> = vec![];
469
470 for f in &self.filter {
471 if let Some(rest) = f.strip_prefix('!') {
472 exclude.push(rest);
473 } else {
474 include.push(f.as_str());
475 }
476 }
477
478 assert!(!include.is_empty(), "at least one `--filter` required");
479
480 let sys = self.style.is_sys();
481 let link = if sys { "windows_link" } else { "windows_core" };
482
483 let phase = std::time::Instant::now();
484 let reader_storage;
485 let reader = if self.input.is_empty() {
486 default_reader()
487 } else {
488 reader_storage = Reader::new(expand_input(&self.input, self.input_default));
489 &reader_storage
490 };
491 report_timing(&self.output, "metadata", phase.elapsed());
492
493 let phase = std::time::Instant::now();
494 let mut references: Vec<ReferenceStage> = Vec::new();
495
496 if !sys {
497 for (probe_namespace, crate_name, paths) in [
499 (
500 "Windows.Foundation",
501 "windows_future",
502 &[
503 "Windows.Foundation.AsyncActionCompletedHandler",
504 "Windows.Foundation.AsyncActionProgressHandler",
505 "Windows.Foundation.AsyncActionWithProgressCompletedHandler",
506 "Windows.Foundation.AsyncOperationCompletedHandler",
507 "Windows.Foundation.AsyncOperationProgressHandler",
508 "Windows.Foundation.AsyncOperationWithProgressCompletedHandler",
509 "Windows.Foundation.AsyncStatus",
510 "Windows.Foundation.IAsyncAction",
511 "Windows.Foundation.IAsyncActionWithProgress",
512 "Windows.Foundation.IAsyncInfo",
513 "Windows.Foundation.IAsyncOperation",
514 "Windows.Foundation.IAsyncOperationWithProgress",
515 ][..],
516 ),
517 (
518 "Windows.Foundation.Collections",
519 "windows_collections",
520 &[
521 "Windows.Foundation.Collections.CollectionChange",
522 "Windows.Foundation.Collections.IIterable",
523 "Windows.Foundation.Collections.IIterator",
524 "Windows.Foundation.Collections.IKeyValuePair",
525 "Windows.Foundation.Collections.IMap",
526 "Windows.Foundation.Collections.IMapChangedEventArgs",
527 "Windows.Foundation.Collections.IMapView",
528 "Windows.Foundation.Collections.IObservableMap",
529 "Windows.Foundation.Collections.IObservableVector",
530 "Windows.Foundation.Collections.IVector",
531 "Windows.Foundation.Collections.IVectorChangedEventArgs",
532 "Windows.Foundation.Collections.IVectorView",
533 "Windows.Foundation.Collections.MapChangedEventHandler",
534 "Windows.Foundation.Collections.VectorChangedEventHandler",
535 ][..],
536 ),
537 (
538 "Windows.Foundation",
539 "windows_reference",
540 &["Windows.Foundation.IReference"][..],
541 ),
542 (
543 "Windows.Foundation",
544 "windows_time",
545 &["Windows.Foundation.DateTime", "Windows.Foundation.TimeSpan"][..],
546 ),
547 (
548 "Windows.Foundation.Numerics",
549 "windows_numerics",
550 &[
551 "Windows.Foundation.Numerics.Matrix3x2",
552 "Windows.Foundation.Numerics.Matrix4x4",
553 "Windows.Foundation.Numerics.Vector2",
554 "Windows.Foundation.Numerics.Vector3",
555 "Windows.Foundation.Numerics.Vector4",
556 ][..],
557 ),
558 ] {
559 if reader.contains_key(probe_namespace) {
560 let filtered: Vec<&str> = paths
561 .iter()
562 .copied()
563 .filter(|path| {
564 if let Some((namespace, name)) = path.rsplit_once('.')
565 && let Some(ns_map) = reader.get(namespace)
566 {
567 return ns_map.contains_key(name);
568 }
569 false
570 })
571 .collect();
572 if !filtered.is_empty() {
573 prepend_default_refs(&mut references, crate_name, &filtered);
574 }
575 }
576 }
577 }
578
579 let derive_str: Vec<&str> = self.derive.iter().map(|s| s.as_str()).collect();
580 let implements = self.implement.as_ref().map(|names| {
581 let names_str: Vec<&str> = names.iter().map(|s| s.as_str()).collect();
582 Implements::new(&names_str)
583 });
584
585 let references = References::new(reader, references);
586 report_timing(&self.output, "references", phase.elapsed());
587
588 let phase = std::time::Instant::now();
589 let (filter, types) = {
590 let mut all_parsed = Vec::new();
591 for entry in &include {
592 all_parsed.extend(filter_parser::parse_filter_entry(entry));
593 }
594 for entry in &exclude {
595 let mut entries = filter_parser::parse_filter_entry(entry);
596 for e in &mut entries {
597 e.exclude = true;
598 }
599 all_parsed.extend(entries);
600 }
601 let resolved = filter_parser::resolve_entries(reader, &all_parsed);
602
603 let mut filter = Filter::from_resolved(reader, &resolved);
604
605 let types = if !filter.has_broad_filter && !self.layout.is_package() {
607 filter.uses_closure = true;
608 TypeClosure::build(reader, &mut filter, &references, implements.as_ref())
609 } else {
610 TypeMap::filter(reader, &filter, &references, self.style.is_sys())
611 };
612
613 (filter, types)
614 };
615 report_timing(&self.output, "selection", phase.elapsed());
616
617 let phase = std::time::Instant::now();
618 let derive = Derive::new(reader, &types, &derive_str);
619 if let Some(implements) = &implements {
620 filter.validate_implements(implements);
621 }
622
623 let event_only_delegates = compute_event_only_delegates(&types, reader);
624
625 let config = Config {
626 bindgen: self,
627 reader,
628 types: &types,
629 references: &references,
630 filter: &filter,
631 derive: &derive,
632 implement: implements.as_ref(),
633 link,
634 namespace: "",
635 event_only_delegates: &event_only_delegates,
636 self_ty: None,
637 self_generics: Vec::new(),
638 prunable: std::sync::Arc::new(BTreeSet::new()),
639 };
640 let filter_config = Config {
641 implement: None,
642 ..config.clone()
643 };
644
645 for target in &self.compose {
646 let class = composition_target(reader, target);
647 assert!(
648 types.contains_key(&class.type_name()),
649 "composition target `{target}` is not selected by the filter"
650 );
651 assert!(
652 class
653 .required_interfaces(reader)
654 .iter()
655 .filter(|interface| interface.kind == InterfaceKind::Composable)
656 .filter(|interface| types.contains_key(&interface.def.type_name()))
657 .any(|interface| interface
658 .get_methods(&filter_config)
659 .iter()
660 .any(|method| matches!(method, MethodOrName::Method(_)))),
661 "composition target `{target}` has no composable factory method selected by the \
662 filter"
663 );
664 }
665
666 let tree = TypeTree::new(&types);
667 report_timing(&self.output, "planning", phase.elapsed());
668 config.write(tree);
669 report_timing(&self.output, "total", total.elapsed());
670 }
671}
672
673#[track_caller]
674fn composition_target(reader: &Reader, target: &str) -> Class {
675 let (namespace, name) = target.rsplit_once('.').unwrap();
676 let class = reader
677 .with_full_name(namespace, name)
678 .find_map(|ty| match ty {
679 Type::Class(class) => Some(class),
680 _ => None,
681 })
682 .unwrap_or_else(|| panic!("composition target `{target}` is not a WinRT class"));
683 assert!(
684 class
685 .required_interfaces(reader)
686 .iter()
687 .any(|interface| interface.kind == InterfaceKind::Composable),
688 "composition target `{target}` is not composable"
689 );
690 class
691}
692
693fn default_reader() -> &'static Reader {
694 static READER: std::sync::OnceLock<Reader> = std::sync::OnceLock::new();
695 READER.get_or_init(|| Reader::new(default_input()))
696}
697
698#[track_caller]
699fn default_input() -> Vec<File> {
700 [windows_default::WINRT, windows_default::WIN32]
701 .into_iter()
702 .map(|bytes| File::new(bytes.to_vec()).unwrap())
703 .collect()
704}
705
706fn expand_input(input: &[Input], input_default: bool) -> Vec<File> {
707 #[track_caller]
708 fn expand_path(result: &mut Vec<File>, path: &Path) {
709 if path.is_dir() {
710 let mut paths = vec![];
711
712 for path in path
713 .read_dir()
714 .unwrap_or_else(|_| panic!("failed to read directory `{}`", path.display()))
715 .flatten()
716 .map(|entry| entry.path())
717 {
718 if path.is_file()
719 && path
720 .extension()
721 .is_some_and(|extension| extension.eq_ignore_ascii_case("winmd"))
722 {
723 paths.push(path);
724 }
725 }
726
727 assert!(
728 !paths.is_empty(),
729 "failed to find .winmd files in directory `{}`",
730 path.display()
731 );
732
733 for path in paths {
734 let bytes = std::fs::read(&path)
735 .unwrap_or_else(|_| panic!("failed to read binary file `{}`", path.display()));
736 let file = File::new(bytes)
737 .unwrap_or_else(|| panic!("failed to read .winmd format `{}`", path.display()));
738 result.push(file);
739 }
740 } else {
741 let Ok(bytes) = std::fs::read(path) else {
742 panic!("failed to read binary file `{}`", path.display());
743 };
744 let Some(file) = File::new(bytes) else {
745 panic!("failed to read .winmd format `{}`", path.display());
746 };
747 result.push(file);
748 }
749 }
750
751 let mut result = if input_default {
752 default_input()
753 } else {
754 vec![]
755 };
756
757 for input in input {
758 match input {
759 Input::Path(path) => expand_path(&mut result, path),
760 Input::Bytes(bytes) => result.push(
761 File::new(bytes.clone())
762 .unwrap_or_else(|| panic!("failed to read .winmd format from memory")),
763 ),
764 }
765 }
766
767 result
768}
769
770fn compute_event_only_delegates(types: &TypeMap, reader: &Reader) -> HashSet<TypeName> {
772 let mut event_delegates: HashSet<TypeName> = HashSet::new();
773 let mut non_event_delegates: HashSet<TypeName> = HashSet::new();
774
775 for type_set in types.values() {
776 for ty in type_set {
777 let (methods, generics): (Box<dyn Iterator<Item = MethodDef>>, &[Type]) = match ty {
778 Type::Interface(i) => (Box::new(i.def.methods()), &i.generics),
779 _ => continue,
780 };
781
782 for method in methods {
783 let is_event_add = method.flags().contains(MethodAttributes::SpecialName)
784 && method.name().starts_with("add_");
785
786 let sig = method.method_signature(generics, reader);
787 for param in &sig.params {
788 if let Type::Delegate(d) = ¶m.ty {
789 if is_event_add {
790 event_delegates.insert(d.type_name());
791 } else {
792 non_event_delegates.insert(d.type_name());
793 }
794 }
795 }
796 }
797 }
798 }
799
800 event_delegates
801 .difference(&non_event_delegates)
802 .copied()
803 .collect()
804}
805
806fn namespace_starts_with(namespace: &str, starts_with: &str) -> bool {
807 namespace.starts_with(starts_with)
808 && (namespace.len() == starts_with.len()
809 || namespace.as_bytes().get(starts_with.len()) == Some(&b'.'))
810}
811
812fn flat_module_namespace(namespace: &str) -> &str {
814 const UMBRELLA: &str = "Windows.Win32";
815 if namespace.len() > UMBRELLA.len()
816 && namespace.starts_with(UMBRELLA)
817 && namespace.as_bytes()[UMBRELLA.len()] == b'.'
818 {
819 return UMBRELLA;
820 }
821 namespace
822}
823
824fn namespace_feature(namespace: &str) -> String {
826 if let Some(stem) = namespace.strip_prefix("Windows.Win32.") {
827 stem.replace('.', "_")
828 } else if let Some((_, rest)) = namespace.split_once('.') {
829 rest.replace('.', "_")
830 } else {
831 namespace.to_string()
832 }
833}
834
835fn prepend_default_refs(refs: &mut Vec<ReferenceStage>, crate_name: &str, paths: &[&str]) {
837 refs.splice(
838 0..0,
839 paths
840 .iter()
841 .rev()
842 .map(|path| ReferenceStage::new(crate_name, path)),
843 );
844}
845
846#[cfg(test)]
847mod tests {
848 use super::*;
849
850 #[test]
851 fn test_starts_with() {
852 assert!(namespace_starts_with(
853 "Windows.Win32.Graphics.Direct3D11on12",
854 "Windows.Win32.Graphics.Direct3D11on12"
855 ));
856 assert!(namespace_starts_with(
857 "Windows.Win32.Graphics.Direct3D11on12",
858 "Windows.Win32.Graphics"
859 ));
860 assert!(!namespace_starts_with(
861 "Windows.Win32.Graphics.Direct3D11on12",
862 "Windows.Win32.Graphics.Direct3D11"
863 ));
864 assert!(!namespace_starts_with(
865 "Windows.Win32.Graphics.Direct3D",
866 "Windows.Win32.Graphics.Direct3D11"
867 ));
868 }
869
870 #[test]
871 fn default_metadata_reader_is_reused() {
872 assert!(std::ptr::eq(default_reader(), default_reader()));
873 }
874
875 #[test]
876 fn implementation_selection() {
877 let mut builder = Bindgen::new();
878 builder
879 .implement("Test.IFirst")
880 .implements(["Test.ISecond", "Other"]);
881 assert_eq!(
882 builder.implement,
883 Some(vec![
884 "Test.IFirst".to_string(),
885 "Test.ISecond".to_string(),
886 "Other".to_string()
887 ])
888 );
889
890 let mut builder = Bindgen::new();
891 builder.implement_all().implement_all();
892 assert_eq!(builder.implement, Some(vec![]));
893
894 let mut builder = Bindgen::new();
895 builder.implements(std::iter::empty::<&str>());
896 assert_eq!(builder.implement, None);
897 }
898
899 #[test]
900 fn composition_selection() {
901 let mut builder = Bindgen::new();
902 builder
903 .compose("Test.First")
904 .composes(["Test.Second", "Other.Third"]);
905 assert_eq!(
906 builder.compose,
907 vec![
908 "Test.First".to_string(),
909 "Test.Second".to_string(),
910 "Other.Third".to_string()
911 ]
912 );
913 }
914
915 #[test]
916 #[should_panic(expected = "cannot combine selected implementations with `implement_all`")]
917 fn implementation_after_all_panics() {
918 Bindgen::new().implement_all().implement("Test.IFirst");
919 }
920
921 #[test]
922 #[should_panic(expected = "cannot combine `implement_all` with selected implementations")]
923 fn implementation_all_after_selection_panics() {
924 Bindgen::new().implement("Test.IFirst").implement_all();
925 }
926}