use base ("Understand::IReport"); use Understand; use Understand::VariableTracker; use strict; # The name of the report for the menu system sub name { return "Variable Tracker"; } # The desciption of the report sub description { return "Understand the impact of a variable."; } # Indicate this report is only available for objects # A return value of -1 means the report is never available for entities sub test_entity { my $ent = shift; if($ent->kind()->check("Object,Parameter,Enumerator")) { return 1; } return 0; } # Indicate this report is never available as a global report. sub test_global { my $db = shift; return -1; } # Indicate this report supports the abort method. # return 1 if it is supported # return 0 if it is not supported sub support_abort { return 0; } sub abort { if(support_abort() == 1) { Understand::VariableTracker::abort(); } } # Indicate this report supports the progress bar. # return 1 if it is supported # return 0 if it is not supported sub support_progress { return 0; } # Optional; indicate if the options window should be displayed # initially, before a report is generated. # Return -1 to indicate this should be user controlled by a checkbox option. # Return 0 to indicate it should not be displayed. # Return 1 to indicate it should be displayed. sub show_initial_options { return 1; } # Optional; called once at report creation. sub init { my $report = shift; # define options dialog, with initial default values $report->option->integer ("MaxLevels", # option name "Levels", # display text 3); # initial default value } sub getGuiPosition { if(!Understand::Gui::active()) { return (undef, 0, 0); } my ($file, $line, $column) = (Understand::Gui::file(), Understand::Gui::line(), Understand::Gui::column()); # ::file() method doesn't always work. if(!$file) { my $db = Understand::Gui::db(); my @ents = $db->lookup(Understand::Gui::filename(), "File", 1); if(@ents == 1) { $file = $ents[0]; } } return ($file, $line, $column); } # global variables my $g_report = undef; sub progress { # print STDERR "PROGRESS @_\n"; if(support_progress() == 1) { my ($percent) = @_; if($g_report) { $g_report->progress($percent); } } } # Required; generate report. sub generate { if( !Understand::Gui::active() ) { return; } my $report = shift; my $entity = shift; my ($g_file, $g_line, $g_column) = getGuiPosition(); my $max_depth = $report->option->lookup("MaxLevels"); if($max_depth < 1) { $max_depth = 3; } my $progress_function_ref = undef; if(support_progress() == 1) { $progress_function_ref = \&progress; } Understand::VariableTracker::init_all(); $g_report = $report; my $root = Understand::VariableTracker::build_tree($entity, $g_file, $g_line, $g_column, $max_depth, $progress_function_ref); draw_tree($report, $root); return; } sub draw_tree { my ($report, # the report we are building the impact in $root, # the entity associated with the "current node" ) = @_; my $level = 1; $report->tree($level,1); $report->syncfile($root->{'file'}, $root->{'line'}, $root->{'column'}); $report->bold(); $report->print($root->{'text'}); $report->nobold(); $report->syncfile(); draw_tree_aux($report, $level + 1, $root); $report->print("\n"); } sub draw_tree_aux { my ($report, $level, $node) = @_; foreach my $edge (@{$node->{'right_edges'}}) { my $right_node = $edge->{'right'}; $report->tree($level,0); my $ref = $edge->{'reference'}; if($ref->kindname() =~ /^set$/i) { # "var1" is set by "var2" at "file:line:column" # "var1" $report->syncfile($edge->{'file'}, $edge->{'line'}, $edge->{'column'}); $report->fontcolor("#0000FF"); $report->print($node->{'text'}); $report->fontcolor(); $report->syncfile(); # is set by $report->print(" is "); $report->print(lc($ref->kindname())); $report->print(" by "); # "var2" $report->syncfile($right_node->{'file'}, $right_node->{'line'}, $right_node->{'column'}); $report->fontcolor("#0000FF"); $report->print($right_node->{'text'}); $report->fontcolor(); $report->syncfile(); # at $report->print(" at "); # "file:line:column" $report->syncfile($edge->{'file'}, $edge->{'line'}, $edge->{'column'}); $report->fontcolor("#0000FF"); $report->print($edge->{'file'}->name() . ":" . $edge->{'line'} . ":" . $edge->{'column'}); $report->fontcolor(); $report->syncfile(); } elsif($ref->kindname() =~ /^define$/i) { # "var1" is initialized and defined by "var2" at "file:line:column" # "var1" $report->syncfile($edge->{'file'}, $edge->{'line'}, $edge->{'column'}); $report->fontcolor("#0000FF"); $report->print($node->{'text'}); $report->fontcolor(); $report->syncfile(); # is initialized and defined by $report->print(" is initialized and defined by "); # "var2" $report->syncfile($right_node->{'file'}, $right_node->{'line'}, $right_node->{'column'}); $report->fontcolor("#0000FF"); $report->print($right_node->{'text'}); $report->fontcolor(); $report->syncfile(); # at $report->print(" at "); # "file:line:column" $report->syncfile($edge->{'file'}, $edge->{'line'}, $edge->{'column'}); $report->fontcolor("#0000FF"); $report->print($edge->{'file'}->name() . ":" . $edge->{'line'} . ":" . $edge->{'column'}); $report->fontcolor(); $report->syncfile(); } else { $report->syncfile($right_node->{'file'}, $right_node->{'line'}, $right_node->{'column'}); $report->bold(); $report->print($right_node->{'text'}); $report->nobold(); $report->syncfile(); $report->print(" "); my $ref = $edge->{'reference'}; $report->syncfile($edge->{'file'}, $edge->{'line'}, $edge->{'column'}); $report->fontcolor("#0000FF"); $report->print($ref->kindname() . " " . $node->{'text'}); $report->fontcolor(); $report->syncfile(); } draw_tree_aux($report, $level + 1, $right_node); } }