@@ -7,6 +7,7 @@ use goblin::mach::{Mach, MachO, SingleArch};
77use goblin:: pe:: PE ;
88use goblin:: Object ;
99use serde:: Deserialize ;
10+ use std:: cmp:: Ordering ;
1011use std:: collections:: HashMap ;
1112use std:: fs;
1213use std:: path:: Path ;
@@ -21,19 +22,23 @@ pub fn introspect_cdylib(library_path: impl AsRef<Path>, main_module_name: &str)
2122
2223/// Parses the introspection chunks found in the binary
2324fn parse_chunks ( chunks : & [ Chunk ] , main_module_name : & str ) -> Result < Module > {
24- let chunks_by_id = chunks
25- . iter ( )
26- . map ( |c| {
27- (
28- match c {
29- Chunk :: Module { id, .. } => id,
30- Chunk :: Class { id, .. } => id,
31- Chunk :: Function { id, .. } => id,
32- } ,
33- c,
34- )
35- } )
36- . collect :: < HashMap < _ , _ > > ( ) ;
25+ let mut chunks_by_id = HashMap :: < & str , & Chunk > :: new ( ) ;
26+ let mut chunks_by_parent = HashMap :: < & str , Vec < & Chunk > > :: new ( ) ;
27+ for chunk in chunks {
28+ if let Some ( id) = match chunk {
29+ Chunk :: Module { id, .. } => Some ( id) ,
30+ Chunk :: Class { id, .. } => Some ( id) ,
31+ Chunk :: Function { id, .. } => id. as_ref ( ) ,
32+ } {
33+ chunks_by_id. insert ( id, chunk) ;
34+ }
35+ if let Some ( parent) = match chunk {
36+ Chunk :: Module { .. } | Chunk :: Class { .. } => None ,
37+ Chunk :: Function { parent, .. } => parent. as_ref ( ) ,
38+ } {
39+ chunks_by_parent. entry ( parent) . or_default ( ) . push ( chunk) ;
40+ }
41+ }
3742 // We look for the root chunk
3843 for chunk in chunks {
3944 if let Chunk :: Module {
@@ -43,7 +48,7 @@ fn parse_chunks(chunks: &[Chunk], main_module_name: &str) -> Result<Module> {
4348 } = chunk
4449 {
4550 if name == main_module_name {
46- return convert_module ( name, members, & chunks_by_id) ;
51+ return convert_module ( name, members, & chunks_by_id, & chunks_by_parent ) ;
4752 }
4853 }
4954 }
@@ -53,59 +58,111 @@ fn parse_chunks(chunks: &[Chunk], main_module_name: &str) -> Result<Module> {
5358fn convert_module (
5459 name : & str ,
5560 members : & [ String ] ,
56- chunks_by_id : & HashMap < & String , & Chunk > ,
61+ chunks_by_id : & HashMap < & str , & Chunk > ,
62+ chunks_by_parent : & HashMap < & str , Vec < & Chunk > > ,
5763) -> Result < Module > {
64+ let ( modules, classes, functions) = convert_members (
65+ & members
66+ . iter ( )
67+ . filter_map ( |id| chunks_by_id. get ( id. as_str ( ) ) . copied ( ) )
68+ . collect :: < Vec < _ > > ( ) ,
69+ chunks_by_id,
70+ chunks_by_parent,
71+ ) ?;
72+ Ok ( Module {
73+ name : name. into ( ) ,
74+ modules,
75+ classes,
76+ functions,
77+ } )
78+ }
79+
80+ /// Convert a list of members of a module or a class
81+ fn convert_members (
82+ chunks : & [ & Chunk ] ,
83+ chunks_by_id : & HashMap < & str , & Chunk > ,
84+ chunks_by_parent : & HashMap < & str , Vec < & Chunk > > ,
85+ ) -> Result < ( Vec < Module > , Vec < Class > , Vec < Function > ) > {
5886 let mut modules = Vec :: new ( ) ;
5987 let mut classes = Vec :: new ( ) ;
6088 let mut functions = Vec :: new ( ) ;
61- for member in members {
62- if let Some ( chunk) = chunks_by_id. get ( member) {
63- match chunk {
64- Chunk :: Module {
89+ for chunk in chunks {
90+ match chunk {
91+ Chunk :: Module {
92+ name,
93+ members,
94+ id : _,
95+ } => {
96+ modules. push ( convert_module (
6597 name,
6698 members,
67- id : _,
68- } => {
69- modules. push ( convert_module ( name, members, chunks_by_id) ?) ;
70- }
71- Chunk :: Class { name, id : _ } => classes. push ( Class { name : name. into ( ) } ) ,
72- Chunk :: Function {
73- name,
74- id : _,
75- arguments,
76- } => functions. push ( Function {
99+ chunks_by_id,
100+ chunks_by_parent,
101+ ) ?) ;
102+ }
103+ Chunk :: Class { name, id } => {
104+ let ( _, _, mut methods) = convert_members (
105+ chunks_by_parent
106+ . get ( & id. as_str ( ) )
107+ . map ( Vec :: as_slice)
108+ . unwrap_or_default ( ) ,
109+ chunks_by_id,
110+ chunks_by_parent,
111+ ) ?;
112+ // We sort methods to get a stable output
113+ methods. sort_by ( |l, r| match l. name . cmp ( & r. name ) {
114+ Ordering :: Equal => {
115+ // We put the getter before the setter
116+ if l. decorators . iter ( ) . any ( |d| d == "property" ) {
117+ Ordering :: Less
118+ } else if r. decorators . iter ( ) . any ( |d| d == "property" ) {
119+ Ordering :: Greater
120+ } else {
121+ // We pick an ordering based on decorators
122+ l. decorators . cmp ( & r. decorators )
123+ }
124+ }
125+ o => o,
126+ } ) ;
127+ classes. push ( Class {
77128 name : name. into ( ) ,
78- arguments : Arguments {
79- positional_only_arguments : arguments
80- . posonlyargs
81- . iter ( )
82- . map ( convert_argument)
83- . collect ( ) ,
84- arguments : arguments. args . iter ( ) . map ( convert_argument) . collect ( ) ,
85- vararg : arguments
86- . vararg
87- . as_ref ( )
88- . map ( convert_variable_length_argument) ,
89- keyword_only_arguments : arguments
90- . kwonlyargs
91- . iter ( )
92- . map ( convert_argument)
93- . collect ( ) ,
94- kwarg : arguments
95- . kwarg
96- . as_ref ( )
97- . map ( convert_variable_length_argument) ,
98- } ,
99- } ) ,
129+ methods,
130+ } )
100131 }
132+ Chunk :: Function {
133+ name,
134+ id : _,
135+ arguments,
136+ parent : _,
137+ decorators,
138+ } => functions. push ( Function {
139+ name : name. into ( ) ,
140+ decorators : decorators. clone ( ) ,
141+ arguments : Arguments {
142+ positional_only_arguments : arguments
143+ . posonlyargs
144+ . iter ( )
145+ . map ( convert_argument)
146+ . collect ( ) ,
147+ arguments : arguments. args . iter ( ) . map ( convert_argument) . collect ( ) ,
148+ vararg : arguments
149+ . vararg
150+ . as_ref ( )
151+ . map ( convert_variable_length_argument) ,
152+ keyword_only_arguments : arguments
153+ . kwonlyargs
154+ . iter ( )
155+ . map ( convert_argument)
156+ . collect ( ) ,
157+ kwarg : arguments
158+ . kwarg
159+ . as_ref ( )
160+ . map ( convert_variable_length_argument) ,
161+ } ,
162+ } ) ,
101163 }
102164 }
103- Ok ( Module {
104- name : name. into ( ) ,
105- modules,
106- classes,
107- functions,
108- } )
165+ Ok ( ( modules, classes, functions) )
109166}
110167
111168fn convert_argument ( arg : & ChunkArgument ) -> Argument {
@@ -290,9 +347,14 @@ enum Chunk {
290347 name : String ,
291348 } ,
292349 Function {
293- id : String ,
350+ #[ serde( default ) ]
351+ id : Option < String > ,
294352 name : String ,
295353 arguments : ChunkArguments ,
354+ #[ serde( default ) ]
355+ parent : Option < String > ,
356+ #[ serde( default ) ]
357+ decorators : Vec < String > ,
296358 } ,
297359}
298360
0 commit comments