1+ """ A very basic interface for generating a Java callgraph using
2+ a web UI instead of CLI
3+ """
4+
5+ from dash import html , dcc , Input , Output , State
6+ from dash .development .base_component import Component
7+ from dash .exceptions import PreventUpdate
8+ from graph_import import Args , GEN_CALLGRAPH_JAR
9+ from pathlib import Path
10+ from .app import app , field
11+
12+ graph_create_status = html .Div (children = [
13+ dcc .Loading (id = "creating" , type = "default" , children = [
14+ html .H3 (id = "graph_state" )
15+ ])
16+ ])
17+
18+ graph_create = html .Div (children = [
19+ html .H2 (children = "Callgraph generation options" ),
20+
21+ # html.Label(children="Graph directory where the call graph will be created and imported from", htmlFor="out_dir"),
22+ # dcc.Input(id="out_dir", type="text", placeholder="Folder that will contain graph data",
23+ # value="graphImport-jadx"),
24+ field ("Output folder for generated callgraph (where it can be imported from)" ,
25+ dcc .Input (id = "out_dir" , type = "text" , placeholder = "Folder that will contain graph data" ,
26+ value = "graphImport-jadx" )),
27+
28+ field ("Folder that contains .jar files for generating a call graph" ,
29+ dcc .Input (id = "jar_dir" , type = "text" , placeholder = "Folder containing .jar files" ,
30+ value = "C:\\ dev\\ genCallgraph\\ toAnalyze" )),
31+
32+ field ("Identifier of main class (that contains the 'main' function)" ,
33+ dcc .Input (id = "main_identifier" , type = "text" , placeholder = "Type a fully qualified class name, e.g, com.foo.MainClass" ,
34+ value = "jadx.gui.JadxGUI" )),
35+
36+ field ("Words that should appear in a .jar file. A file without any of these words will be skipped" ,
37+ dcc .Dropdown (id = "jar_filter" , multi = True , placeholder = "Type any word, e.g 'jadx' " ,
38+ value = ["jadx" ])),
39+
40+
41+ field ("Words that should appear in an edge's source and target identifiers. An edge without any of these words will be skipped" ,
42+ dcc .Dropdown (id = "edge_filter" , multi = True , placeholder = "Type any word, e.g 'jadx' " ,
43+ value = ["jadx" ])),
44+
45+ html .Button ("Create callgraph" , id = "create_cg" ),
46+ graph_create_status ,
47+ ])
48+
49+
50+ # a hacky way of using a dropdown input
51+ # for arbitrary input
52+ def multi_input (search_value , value ):
53+ if not search_value :
54+ raise PreventUpdate
55+ existing = {v : v for v in value } if value else {}
56+ return {** existing , search_value : search_value }
57+
58+ jar_filter = app .callback (
59+ Output ("jar_filter" , "options" ),
60+ Input ("jar_filter" , "search_value" ),
61+ State ("jar_filter" , "value" )
62+ )(multi_input )
63+
64+ edge_filter = app .callback (
65+ Output ("edge_filter" , "options" ),
66+ Input ("edge_filter" , "search_value" ),
67+ State ("edge_filter" , "value" )
68+ )(multi_input )
69+
70+ @app .callback (
71+ dict (graph_state = Output ("graph_state" , "children" ),
72+ import_dir = Output ("import_dir" , "value" )
73+ ),
74+ Input ("create_cg" , "n_clicks" ),
75+ State ("out_dir" , "value" ),
76+ State ("jar_dir" , "value" ),
77+ State ("edge_filter" , "value" ),
78+ State ("jar_filter" , "value" ),
79+ State ("main_identifier" , "value" ),
80+ State ("graph_state" , "children" ),
81+ State ("import_dir" , "value" )
82+ )
83+ def create_callgraph (n_clicks , out_dir , jar_dir , edge_filter , jar_filter , main_identifier , graph_state , import_dir ):
84+ if n_clicks :
85+ print ("Trying to fetch callgraph" )
86+ args = Args (
87+ input_jar_folder = Path (jar_dir ).resolve (),
88+ edge_filter = list (edge_filter ) if edge_filter else [],
89+ jar_filter = list (jar_filter ) if jar_filter else [],
90+ main_class_identifier = main_identifier ,
91+ graph_output_folder = Path (out_dir ).resolve (),
92+ )
93+ cg = args .run_callgraph (GEN_CALLGRAPH_JAR )
94+ return dict (graph_state = f"Created a graph with { len (cg .node_props )} nodes and { len (cg .edges )} edges " ,
95+ import_dir = out_dir )
96+ return dict (graph_state = graph_state , import_dir = import_dir )
0 commit comments