#!/usr/bin/perl use strict; use warnings; sub classes { my $file = shift; my @classes; open (my $jar, "jar tf $file |") or die $!; while (<$jar>) { chomp; /\$/ and next; s/\.class$// or next; s/\//./g; push @classes, $_; } \@classes; } sub reqfound { my $reqs = shift; no warnings qw/uninitialized/; #push @$reqs, map { "$_ |$&" } map { push @$reqs, map { "$_" } map { s/"\[L([^;]+);"/$1/g; s/\$.*//; s/[\[\]]//g; s/\//./g; $_; } split /,\s*|;[BICJZ\[]*L/, join (",", @_); use warnings; } sub reqline { $_ = shift; my $reqs = shift; /\/\/Method ([^\.]+)\.[^:]+:\((L([^\)]+);)?\)/ and reqfound ($reqs, $1, $3); /\/\/class ([^\s"]+)$/ and reqfound ($reqs, $1); /\/\/Field [^:]+:L([^;]+);$/ and reqfound ($reqs, $1); /INSTANCE:L([^\s,;]+)/ and reqfound ($reqs, $1); s/\/\/.*//; /^(public|protected|private).* ([^\si]+) [^\s\(]+\(([^\)]*)\)/ and reqfound ($reqs, $2, $3); / extends ([^\s{]+)/ and reqfound ($reqs, $1); / implements ([^\s{]+)/ and reqfound ($reqs, $1); } sub requires { my $file = shift; my $classes = shift; my @requires; open (my $dump, "javap -classpath $file -private -c ". join (' ', @$classes).' |') or die $!; while (<$dump>) { chomp; reqline ($_, \@requires); } # Uniq [ sort keys %{{ map { $_ => undef } @requires }} ]; } sub deps { my %files; my %provides; my %deps; # Suck in the requires and provider foreach my $filename (@ARGV) { my $file = $filename; $file =~ s/.*\///; $files{$file} = { provides => classes ($filename) }; $files{$file}->{requires} = requires ($filename, $files{$file}->{provides}); }; # Index provides foreach my $file (keys %files) { foreach my $provide (@{$files{$file}->{provides}}) { $provides{$provide} ||= []; push @{$provides{$provide}}, $file; } } # Resolve requires foreach my $file (keys %files) { # Empty ones $deps{$file} = []; foreach my $require (@{$files{$file}->{requires}}) { my $providers = $provides{$require}; push @{$deps{$file}}, @$providers if $providers; } # Skip redundant and selves $deps{$file} = [ grep { $_ ne $file } sort keys %{{ map { $_ => undef } @{$deps{$file}} }} ]; } return \%deps; } sub graphviz { my $deps = shift; print "digraph g {\n"; print "\tsize=\"50\";\n"; foreach my $from (keys %$deps) { print "\t\"$from\";\n" unless @{$deps->{$from}}; foreach my $to (@{$deps->{$from}}) { print "\t\"$from\" -> \"$to\";\n"; } } print "}\n"; } my $deps = deps; graphviz ($deps);