@@ -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,61 +58,126 @@ 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 {
77- 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- } ) ,
99+ chunks_by_id,
100+ chunks_by_parent,
101+ ) ?) ;
100102 }
103+ Chunk :: Class { name, id } => {
104+ classes. push ( convert_class ( id, name, chunks_by_id, chunks_by_parent) ?)
105+ }
106+ Chunk :: Function {
107+ name,
108+ id : _,
109+ arguments,
110+ parent : _,
111+ decorators,
112+ } => functions. push ( convert_function ( name, arguments, decorators) ) ,
101113 }
102114 }
103- Ok ( Module {
115+ Ok ( ( modules, classes, functions) )
116+ }
117+
118+ fn convert_class (
119+ id : & str ,
120+ name : & str ,
121+ chunks_by_id : & HashMap < & str , & Chunk > ,
122+ chunks_by_parent : & HashMap < & str , Vec < & Chunk > > ,
123+ ) -> Result < Class > {
124+ let ( nested_modules, nested_classes, mut methods) = convert_members (
125+ chunks_by_parent
126+ . get ( & id)
127+ . map ( Vec :: as_slice)
128+ . unwrap_or_default ( ) ,
129+ chunks_by_id,
130+ chunks_by_parent,
131+ ) ?;
132+ ensure ! (
133+ nested_modules. is_empty( ) ,
134+ "Classes cannot contain nested modules"
135+ ) ;
136+ ensure ! (
137+ nested_classes. is_empty( ) ,
138+ "Nested classes are not supported yet"
139+ ) ;
140+ // We sort methods to get a stable output
141+ methods. sort_by ( |l, r| match l. name . cmp ( & r. name ) {
142+ Ordering :: Equal => {
143+ // We put the getter before the setter
144+ if l. decorators . iter ( ) . any ( |d| d == "property" ) {
145+ Ordering :: Less
146+ } else if r. decorators . iter ( ) . any ( |d| d == "property" ) {
147+ Ordering :: Greater
148+ } else {
149+ // We pick an ordering based on decorators
150+ l. decorators . cmp ( & r. decorators )
151+ }
152+ }
153+ o => o,
154+ } ) ;
155+ Ok ( Class {
104156 name : name. into ( ) ,
105- modules,
106- classes,
107- functions,
157+ methods,
108158 } )
109159}
110160
161+ fn convert_function ( name : & str , arguments : & ChunkArguments , decorators : & [ String ] ) -> Function {
162+ Function {
163+ name : name. into ( ) ,
164+ decorators : decorators. to_vec ( ) ,
165+ arguments : Arguments {
166+ positional_only_arguments : arguments. posonlyargs . iter ( ) . map ( convert_argument) . collect ( ) ,
167+ arguments : arguments. args . iter ( ) . map ( convert_argument) . collect ( ) ,
168+ vararg : arguments
169+ . vararg
170+ . as_ref ( )
171+ . map ( convert_variable_length_argument) ,
172+ keyword_only_arguments : arguments. kwonlyargs . iter ( ) . map ( convert_argument) . collect ( ) ,
173+ kwarg : arguments
174+ . kwarg
175+ . as_ref ( )
176+ . map ( convert_variable_length_argument) ,
177+ } ,
178+ }
179+ }
180+
111181fn convert_argument ( arg : & ChunkArgument ) -> Argument {
112182 Argument {
113183 name : arg. name . clone ( ) ,
@@ -290,9 +360,14 @@ enum Chunk {
290360 name : String ,
291361 } ,
292362 Function {
293- id : String ,
363+ #[ serde( default ) ]
364+ id : Option < String > ,
294365 name : String ,
295366 arguments : ChunkArguments ,
367+ #[ serde( default ) ]
368+ parent : Option < String > ,
369+ #[ serde( default ) ]
370+ decorators : Vec < String > ,
296371 } ,
297372}
298373
0 commit comments