#!/usr/bin/perl
my
%java_class_info
;
my
$type_caster
= Java::Javap::TypeCast->new();
GetOptions(
'javapopts=s'
=> \(
my
$opt_javapopts
=
''
),
'genwith=s'
=> \(
my
$opt_genwith
=
'Perl6'
),
'genopts=s'
=> \
my
$opt_genopts
,
'outdir=s'
=> \(
my
$opt_outdir
=
'.'
),
'nest!'
=> \(
my
$opt_nest
= 1),
'recurse!'
=> \(
my
$opt_recurse
= 1),
'compile!'
=> \(
my
$opt_compile
= 1),
'set_types=s'
=>
sub
{
$type_caster
->set_type_casts({});
$type_caster
->add_type_casts_from_file(
$_
[1] );
},
'add_types=s'
=>
sub
{
$type_caster
->add_type_casts_from_file(
$_
[1] );
},
'trace=i'
=> \(
my
$opt_trace
= 1),
'help!'
=> \
&help
,
) or
exit
1;
my
@classes
=
@ARGV
or
die
"usage: $0 [options] class [ class ... ]\nUse $0 --help for help\n"
;
if
(
$opt_compile
) {
my
$perl6_ver
=
qx{perl6 --version 2>&1}
;
if
(
$perl6_ver
) {
$perl6_ver
= $1
if
$perl6_ver
=~ m/(Rakudo Perl.*)/;
print
"perl6: $perl6_ver\n"
;
}
else
{
warn
"perl6 not available so --compile disabled\n"
;
$opt_compile
= 0;
}
}
$| = 1
if
$opt_trace
;
$::RD_WARN = 1;
$::RD_TRACE = 1
if
$opt_trace
>= 10;
my
$parser
= Java::Javap::Grammar->new();
my
$jenny
= Java::Javap::Generator->get_generator(
$opt_genwith
,
trace_level
=>
$opt_trace
,
split
(/\s+/,
$opt_genopts
||
''
),
);
my
%check_status
;
foreach
my
$class
(
@classes
) {
load_java_class_info(
$class
,
$opt_recurse
);
}
for
my
$class
(
sort
keys
%java_class_info
) {
my
$info
=
$java_class_info
{
$class
};
next
unless
$info
->{tree};
my
@epilogue
= (
"=begin pod\n"
);
if
(
my
$refs
=
$info
->{referred_to_by_classes}) {
push
@epilogue
,
"=head1 Referenced By\n"
;
push
@epilogue
,
" $_"
for
sort
@$refs
;
push
@epilogue
,
"\n(Among the set of classes processed at the same time.)\n"
;
}
if
(
my
@decomp_norm
=
split
/\n/,
$info
->{decomp_norm}) {
push
@epilogue
,
"=head1 Java\n"
;
push
@epilogue
,
" $_"
for
@decomp_norm
;
push
@epilogue
,
"\n"
;
}
push
@epilogue
,
"=end pod\n"
;
my
$file_name
= generate_output_file( {
class_file
=>
$class
,
ast
=>
$info
->{tree},
javap_command
=>
$info
->{javap_command},
type_caster
=>
$type_caster
,
epilogue
=>
join
(
"\n"
,
''
,
@epilogue
),
});
$info
->{output_filename} =
$file_name
;
warn
"wrote $file_name - $info->{kind} $class\n"
if
$opt_trace
&&
$file_name
;
if
(
$opt_trace
>= 2) {
warn
"\t uses @{ $info->{refers_to_classes} }\n"
if
@{
$info
->{refers_to_classes} };
warn
"\t used by @{ $info->{referred_to_by_classes} }\n"
if
@{
$info
->{referred_to_by_classes} || [] };
}
}
for
my
$info
(
sort
{ (
$b
->{depth}||0) <=> (
$a
->{depth}||0) }
values
%java_class_info
) {
next
unless
$opt_compile
;
my
$class
=
$info
->{java_class_name};
my
$file_name
=
$info
->{output_filename}
or
next
;
warn
"compiling $file_name - $info->{kind} $class\n"
if
$opt_trace
;
(
my
$pirfile
=
$file_name
) =~ s/\.pm6$/.pir/;
my
@perl6cmd
= (
"perl6"
,
"--target=PIR"
,
"--output=$pirfile"
,
$file_name
);
local
$ENV
{PERL6LIB} =
join
(
":"
,
grep
{
$_
}
$opt_outdir
,
$ENV
{PERL6LIB});
warn
"\t @perl6cmd\n"
if
$opt_trace
>= 2;
$check_status
{
$file_name
} = (
system
(
@perl6cmd
) == 0);
}
if
(
%check_status
) {
my
$passed
= 0;
for
my
$file_name
(
sort
keys
%check_status
) {
my
$ok
=
$check_status
{
$file_name
};
++
$passed
if
$ok
;
printf
"%7s: %s\n"
, (
$ok
?
"ok"
:
"FAILED"
),
$file_name
if
!
$ok
or
$opt_trace
>= 2;
}
printf
"%d ok, %d failed.\n"
,
$passed
,
keys
(
%check_status
)-
$passed
if
keys
%check_status
> 1;
}
exit
0;
sub
load_java_class_info {
my
(
$class
,
$recurse
) =
@_
;
return
undef
if
$java_class_info
{
$class
}->{decomp_norm};
my
@javapopts
=
split
/ /,
$opt_javapopts
;
my
$javap_command
=
"javap @javapopts $class"
;
warn
sprintf
"loading %s%s%s\n"
,
$recurse
? (
". "
x (
$recurse
-1)) :
""
,
$class
,
" @javapopts"
if
$opt_trace
;
my
$decomp_norm
= Java::Javap->javap(
$class
, \
@javapopts
);
warn
$decomp_norm
if
$opt_trace
>= 3;
my
$decomp_verb
= Java::Javap->javap(
$class
, [
@javapopts
,
'-verbose'
]);
warn
$decomp_verb
if
$opt_trace
>= 9;
my
$tree
=
$parser
->comp_unit(
$decomp_verb
)
or
die
"Error parsing output of '$javap_command'\n"
;
my
$referenced_classes
= Java::Javap->get_included_types(
$tree
,
$type_caster
);
$java_class_info
{
$class
} = {
%{
$java_class_info
{
$class
} || {} },
java_class_name
=>
$class
,
javap_command
=>
$javap_command
,
decomp_norm
=>
$decomp_norm
,
decomp_verb
=>
$decomp_verb
,
tree
=>
$tree
,
refers_to_classes
=>
$referenced_classes
,
kind
=>
$tree
->{class_or_interface},
depth
=>
$recurse
,
};
warn
"$class: "
.Dumper(
$java_class_info
{
$class
})
if
$opt_trace
>= 9;
push
@{
$java_class_info
{
$_
}->{referred_to_by_classes}},
$class
for
@$referenced_classes
;
if
(
$recurse
) {
for
my
$ref_class
(
sort
@$referenced_classes
) {
if
(
$ref_class
=~ m/\$/) {
warn
"$ref_class: skipped private class\n"
if
$opt_trace
>= 2;
next
;
}
load_java_class_info(
$ref_class
,
$recurse
+1 );
}
}
return
$referenced_classes
;
}
sub
generate_output_file {
my
$params
=
shift
;
my
$epilogue
=
delete
$params
->{epilogue};
my
$output
=
$jenny
->generate(
$params
);
$output
.=
$epilogue
if
$epilogue
;
my
$file_name
;
if
(
$opt_outdir
eq
'STDOUT'
) {
print
$output
;
}
else
{
my
$class
=
$params
->{class_file};
$file_name
= output_filename_for_class(
$class
, 1);
open
my
$API_MODULE
,
'>'
,
$file_name
or
die
"Couldn't write to $file_name: $!\n"
;
print
$API_MODULE
$output
;
close
$API_MODULE
or
die
"Error writing $file_name: $!\n"
;
}
return
$file_name
;
}
sub
output_filename_for_class {
my
(
$class
,
$mkdir
) =
@_
;
my
@subdirs
=
split
/\./,
$class
;
my
$class_name
=
pop
@subdirs
;
my
$module_dir
=
$opt_outdir
;
mkdir
$module_dir
or -d
$module_dir
or
die
"Can't mkdir $module_dir: $!\n"
;
if
(
$opt_nest
) {
foreach
my
$subdir
(
@subdirs
) {
$module_dir
= File::Spec->catdir(
$module_dir
,
$subdir
);
mkdir
$module_dir
or -d
$module_dir
or
die
"Can't mkdir $module_dir: $!\n"
;
}
}
return
File::Spec->catfile(
$module_dir
,
"$class_name.pm6"
);
}
sub
help {
print
<<'EO_Help';
java2perl6api [options] class_file [class_file...]
where options are:
--help this message
--outdir=D root directory for output files (default '.')
--nonest don't place output files in nested directories
--nocompile don't compile the generated modules into .pir files
--norecurse don't recurse into types used by this class
--set_types=S replace typemapings with those in file S
--add_types=S add/override typemapings with those in file S
--javapopts=S a string of command line flags for javap, example:
-j '-classpath /some/path'
--genwith=S the name of a Java::Javap::Generator:: module
which will make the output, defaults to Perl6
--genopts=S strings to pass to your --genwith constructor
--trace=N defines the trace level (integer value), where:
0 means silence, no trace,
1 is the default minimum trace messages,
>= 9 for full trace
EO_Help
exit
;
}